mirror of
https://github.com/lifegpc/eh-downloader.git
synced 2026-06-06 05:38:44 +08:00
Use custom datalist implement
This commit is contained in:
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user