Use custom datalist implement

This commit is contained in:
2023-06-30 15:57:34 +08:00
parent 42cb28e38e
commit 5aa432fc10
4 changed files with 106 additions and 51 deletions

View File

@@ -1,7 +1,8 @@
import { Component, ComponentChildren, ContextType } from "preact";
import TextField from "preact-material-components/TextField";
import { Ref, useRef } from "preact/hooks";
import { Ref, useRef, useState } from "preact/hooks";
import { BCtx } from "./BContext.tsx";
import List from "preact-material-components/List";
interface TextType {
text: string;
@@ -34,6 +35,7 @@ type Props<T extends keyof TextType> = {
outlined?: boolean;
id?: string;
list?: string;
datalist?: { value: TextType[T]; label?: string }[];
};
export default class BTextField<T extends keyof TextType>
@@ -98,6 +100,22 @@ export default class BTextField<T extends keyof TextType>
if (this.props.value !== undefined) this.update(this.props.value);
else if (this.clear_cache) this.clear();
}
get value(): TextType[T] | undefined {
const e = this.ref?.current;
if (e) {
const b = e.base;
if (b) {
const t = b as HTMLElement;
const d = t.querySelector("input");
if (d) {
const n = this.get_value(d);
if (typeof n === "number" && isNaN(n)) return undefined;
return n;
}
}
}
return undefined;
}
get_value(e: HTMLInputElement): TextType[T] {
const type = this.props.type;
// @ts-ignore Checked
@@ -107,10 +125,41 @@ export default class BTextField<T extends keyof TextType>
}
render() {
this.ref = useRef<TextField>();
let cn = "text";
let cn = "b-text-field text";
let datalist_div = null;
const [display_datalist, set_display_datalist] = useState(false);
if (this.props.helpertext) cn += " helper";
if (this.props.outlined) cn += " outlined";
if (this.props.label) cn += " label";
if (this.props.datalist && this.props.datalist.length) {
cn += " datalist";
let cn2 = "datalist";
if (display_datalist) cn2 += " open";
const v = this.value?.toString();
datalist_div = (
<List class={cn2}>
{this.props.datalist.map((d) => {
if (v !== undefined) {
if (!d.value.toString().startsWith(v)) return null;
}
let label_div = null;
if (d.label) {
label_div = <div class="label">{d.label}</div>;
}
return (
<List.Item
onMousedown={() => {
this.set_value(d.value);
}}
>
<div class="value">{d.value}</div>
{label_div}
</List.Item>
);
})}
</List>
);
}
let desc = null;
if (this.props.description) {
desc = <label>{this.props.description}</label>;
@@ -136,7 +185,10 @@ export default class BTextField<T extends keyof TextType>
max={this.props.max}
outlined={this.props.outlined}
list={this.props.list}
onFocus={() => set_display_datalist(true)}
onBlur={() => set_display_datalist(false)}
/>
{datalist_div}
{this.props.children}
</div>
);

View File

@@ -1,44 +0,0 @@
import { Component } from "preact";
import { GMeta } from "../db.ts";
import { useEffect } from "preact/hooks";
import { GalleryListResult } from "../server/gallery.ts";
type Props = {
/**@default {false}*/
jpn_title?: boolean;
id: string;
};
type State = {
data?: GMeta[];
};
export default class GidDataList extends Component<Props, State> {
render() {
const fetchData = async () => {
const re = await fetch(
"/api/gallery/list?all=1&fields=gid,title,title_jpn",
);
const d: GalleryListResult = await re.json();
if (d.ok) {
this.setState({ data: d.data });
}
};
useEffect(() => {
fetchData().catch((e) => console.error(e));
}, []);
return (
<datalist id={this.props.id}>
{this.state.data
? this.state.data.map((d) => {
let title = d.title;
if (this.props.jpn_title && d.title_jpn) {
title = d.title_jpn;
}
return <option value={d.gid}>{title}</option>;
})
: null}
</datalist>
);
}
}

View File

@@ -5,7 +5,7 @@ import Icon from "preact-material-components/Icon";
import { set_state } from "../server/state.ts";
import t from "../server/i18n.ts";
import BSelect from "./BSelect.tsx";
import { StateUpdater, useRef, useState } from "preact/hooks";
import { StateUpdater, useEffect, useRef, useState } from "preact/hooks";
import { TaskType } from "../task.ts";
import BTextField from "./BTextField.tsx";
import { parseUrl, UrlType } from "../url.ts";
@@ -22,13 +22,18 @@ import Snackbar from "preact-material-components/Snackbar";
import { GalleryResult } from "../server/gallery.ts";
import { tw } from "twind";
import { ExportZipConfig } from "../tasks/export_zip.ts";
import GidDataList from "./GidDataList.tsx";
import { GMeta } from "../db.ts";
import { GalleryListResult } from "../server/gallery.ts";
export type NewTaskProps = {
show: boolean;
};
export default class NewTask extends Component<NewTaskProps> {
type State = {
gids?: GMeta[];
};
export default class NewTask extends Component<NewTaskProps, State> {
static contextType = GlobalCtx;
declare context: ContextType<typeof GlobalCtx>;
render() {
@@ -54,6 +59,20 @@ export default class NewTask extends Component<NewTaskProps> {
const [abort, set_abort] = useState<AbortController>();
const [ezcfg, set_ezcfg1] = useState(generate_export_zip_cfg());
const [overwrite_ezcfg, set_overwrite_ezcfg] = useState(false);
const fetchGidsData = async () => {
const re = await fetch(
"/api/gallery/list?all=1&fields=gid,title,title_jpn",
);
const d: GalleryListResult = await re.json();
if (d.ok) {
this.setState({ ...this.state, gids: d.data });
}
};
useEffect(() => {
if (task_type === TaskType.ExportZip) {
fetchGidsData().catch((e) => console.error(e));
}
}, [task_type]);
if (task_type === TaskType.Download) {
const set_url: StateUpdater<string> = (u) => {
const n = typeof u === "string" ? u : u(url || "");
@@ -288,16 +307,22 @@ export default class NewTask extends Component<NewTaskProps> {
</div>
);
}
const datalist: { value: number; label?: string }[] = [];
if (this.state.gids) {
this.state.gids.forEach((g) => {
const t = jpn_title && g.title_jpn ? g.title_jpn : g.title;
datalist.push({ value: g.gid, label: t });
});
}
config_div = (
<div class="export_zip">
<GidDataList id="gid-list" jpn_title={jpn_title} />
<BTextField
value={ezgid}
description={t("task.gallery_id")}
type="number"
outlined={true}
set_value={set_ezgid}
list="gid-list"
datalist={datalist}
/>
{ginfo_div}
<BCheckbox

View File

@@ -35,6 +35,28 @@ body {
top: 64px;
}
.b-text-field .datalist {
width: 100%;
display: none;
}
.b-text-field .datalist.open {
display: grid;
}
.b-text-field .datalist .mdc-list-item {
display: grid;
}
.b-text-field .datalist .mdc-list-item .value {
width: 100%;
}
.b-text-field .datalist .mdc-list-item .label {
width: 100%;
font-size: smaller;
}
.settings {
margin: 0 18%;
padding-top: 40px;