From 6d69566e1a784f4f14703cd752aff5abdd1b4a38 Mon Sep 17 00:00:00 2001 From: Chris Kruining Date: Tue, 7 Jan 2025 13:14:35 +0100 Subject: [PATCH] refactor dropdown to select --- src/components/select/index.module.css | 95 ++++++++++++++++++++++++++ src/components/select/index.tsx | 81 ++++++++++++++++++++++ src/features/i18n/picker.tsx | 6 +- 3 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 src/components/select/index.module.css create mode 100644 src/components/select/index.tsx diff --git a/src/components/select/index.module.css b/src/components/select/index.module.css new file mode 100644 index 0000000..3ffce53 --- /dev/null +++ b/src/components/select/index.module.css @@ -0,0 +1,95 @@ +.box { + display: contents; + + &:has(> :popover-open) > .button { + background-color: var(--surface-500); + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } +} + +.button { + position: relative; + display: grid; + grid-template-columns: inherit; + place-items: center start; + + /* Make sure the height of the button does not collapse when it is empty */ + block-size: 1em; + box-sizing: content-box; + + padding: var(--padding-m); + background-color: transparent; + border: none; + border-radius: var(--radii-m); + font-size: 1rem; + + cursor: pointer; + + &:hover { + background-color: var(--surface-700); + } + + &:has(> .caret) { + padding-inline-end: calc(1em + (2 * var(--padding-m))); + } + + & > .caret { + position: absolute; + inset-inline-end: var(--padding-m); + inset-block-start: 50%; + translate: 0 -50%; + inline-size: 1em; + } +} + +.dialog { + display: none; + position: relative; + grid-template-columns: inherit; + + inset-inline-start: anchor(start); + inset-block-start: anchor(end); + position-try-fallbacks: flip-block, flip-inline; + + /* inline-size: anchor-size(self-inline); */ + background-color: var(--surface-500); + padding: var(--padding-m); + border: none; + box-shadow: var(--shadow-2); + + &:popover-open { + display: grid; + } + + & > header { + display: grid; + grid-column: 1 / -1; + + gap: var(--padding-s); + } + + & > main { + display: grid; + grid-template-columns: subgrid; + grid-column: 1 / -1; + row-gap: var(--padding-s); + } +} + +.option { + display: grid; + grid-template-columns: subgrid; + grid-column: 1 / -1; + place-items: center start; + + border-radius: var(--radii-m); + padding: var(--padding-s); + margin-inline: calc(-1 * var(--padding-s)); + + cursor: pointer; + + &.selected { + background-color: oklch(from var(--info) l c h / .1); + } +} \ No newline at end of file diff --git a/src/components/select/index.tsx b/src/components/select/index.tsx new file mode 100644 index 0000000..4bca60c --- /dev/null +++ b/src/components/select/index.tsx @@ -0,0 +1,81 @@ +import { createMemo, createSignal, For, JSX, Setter, createEffect, Show } from "solid-js"; +import { FaSolidAngleDown } from "solid-icons/fa"; +import css from './index.module.css'; + +interface SelectProps { + id: string; + class?: string; + value: K; + setValue?: Setter; + values: Record; + open?: boolean; + showCaret?: boolean; + children: (key: K, value: T) => JSX.Element; + filter?: (query: string, key: K, value: T) => boolean; +} + +export function Select(props: SelectProps) { + const [dialog, setDialog] = createSignal(); + const [key, setKey] = createSignal(props.value); + const [open, setOpen] = createSignal(props.open ?? false); + const [query, setQuery] = createSignal(''); + + const values = createMemo(() => { + let entries = Object.entries(props.values) as [K, T][]; + const filter = props.filter; + const q = query(); + + if (filter) { + entries = entries.filter(([k, v]) => filter(q, k, v)); + } + + return entries; + }); + + const showCaret = createMemo(() => props.showCaret ?? true); + + createEffect(() => { + props.setValue?.(() => key()); + }); + + createEffect(() => { + dialog()?.[open() ? 'showPopover' : 'hidePopover'](); + }); + + return
+ + + setOpen(e.newState === 'open')}> + +
+ setQuery(e.target.value)} /> +
+
+ +
+ { + ([k, v]) => { + const selected = createMemo(() => key() === k); + + return { + setKey(() => k); + dialog()?.hidePopover(); + }}>{props.children(k, v)}; + } + } +
+
+
; +} \ No newline at end of file diff --git a/src/features/i18n/picker.tsx b/src/features/i18n/picker.tsx index dfd88a7..3e431fd 100644 --- a/src/features/i18n/picker.tsx +++ b/src/features/i18n/picker.tsx @@ -1,7 +1,7 @@ import { Component } from "solid-js"; import { internal_useI18n } from "./context"; import { locales } from "./constants"; -import { Dropdown } from "~/components/dropdown"; +import { Select } from "~/components/select"; import { Dynamic } from "solid-js/web"; import css from './picker.module.css'; @@ -10,7 +10,7 @@ interface LocalePickerProps { } export const LocalePicker: Component = (props) => { const { locale, setLocale } = internal_useI18n(); - return = (props) => { showCaret={false} > {(locale, { flag, label }) => } - + }; \ No newline at end of file