@syware/jlib 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +116 -2
- package/dist/all.d.ts +1 -0
- package/dist/all.js +75 -0
- package/dist/atoms/jl-avatar.css_raw.js +3 -0
- package/dist/atoms/jl-avatar.d.ts +52 -0
- package/dist/atoms/jl-avatar.js +167 -0
- package/dist/atoms/jl-badge.css_raw.js +3 -0
- package/dist/atoms/jl-badge.d.ts +32 -0
- package/dist/atoms/jl-badge.js +60 -0
- package/dist/atoms/jl-button.css_raw.js +3 -0
- package/dist/atoms/jl-button.d.ts +70 -0
- package/dist/atoms/jl-button.js +146 -0
- package/dist/atoms/jl-checkbox.css_raw.js +3 -0
- package/dist/atoms/jl-checkbox.d.ts +61 -0
- package/dist/atoms/jl-checkbox.js +149 -0
- package/dist/atoms/jl-chip.css_raw.js +3 -0
- package/dist/atoms/jl-chip.d.ts +59 -0
- package/dist/atoms/jl-chip.js +164 -0
- package/dist/atoms/jl-divider.css_raw.js +3 -0
- package/dist/atoms/jl-divider.d.ts +26 -0
- package/dist/atoms/jl-divider.js +59 -0
- package/dist/atoms/jl-icon.css_raw.js +3 -0
- package/dist/atoms/jl-icon.d.ts +25 -0
- package/dist/atoms/jl-icon.js +41 -0
- package/dist/atoms/jl-input.css_raw.js +3 -0
- package/dist/atoms/jl-input.d.ts +112 -0
- package/dist/atoms/jl-input.js +202 -0
- package/dist/atoms/jl-kbd.css_raw.js +3 -0
- package/dist/atoms/jl-kbd.d.ts +17 -0
- package/dist/atoms/jl-kbd.js +62 -0
- package/dist/atoms/jl-link.css_raw.js +3 -0
- package/dist/atoms/jl-link.d.ts +37 -0
- package/dist/atoms/jl-link.js +86 -0
- package/dist/atoms/jl-progress-ring.css_raw.js +3 -0
- package/dist/atoms/jl-progress-ring.d.ts +49 -0
- package/dist/atoms/jl-progress-ring.js +132 -0
- package/dist/atoms/jl-progress.css_raw.js +3 -0
- package/dist/atoms/jl-progress.d.ts +43 -0
- package/dist/atoms/jl-progress.js +109 -0
- package/dist/atoms/jl-rating.css_raw.js +3 -0
- package/dist/atoms/jl-rating.d.ts +73 -0
- package/dist/atoms/jl-rating.js +184 -0
- package/dist/atoms/jl-select.css_raw.js +3 -0
- package/dist/atoms/jl-select.d.ts +70 -0
- package/dist/atoms/jl-select.js +146 -0
- package/dist/atoms/jl-skeleton.css_raw.js +3 -0
- package/dist/atoms/jl-skeleton.d.ts +34 -0
- package/dist/atoms/jl-skeleton.js +63 -0
- package/dist/atoms/jl-slider.css_raw.js +3 -0
- package/dist/atoms/jl-slider.d.ts +65 -0
- package/dist/atoms/jl-slider.js +143 -0
- package/dist/atoms/jl-spinner.css_raw.js +3 -0
- package/dist/atoms/jl-spinner.d.ts +16 -0
- package/dist/atoms/jl-spinner.js +26 -0
- package/dist/atoms/jl-switch.css_raw.js +3 -0
- package/dist/atoms/jl-switch.d.ts +57 -0
- package/dist/atoms/jl-switch.js +125 -0
- package/dist/atoms/jl-textarea.css_raw.js +3 -0
- package/dist/atoms/jl-textarea.d.ts +81 -0
- package/dist/atoms/jl-textarea.js +148 -0
- package/dist/base/jl-element.d.ts +16 -0
- package/dist/base/jl-element.js +35 -0
- package/dist/base/jl-form-control.d.ts +29 -0
- package/dist/base/jl-form-control.js +44 -0
- package/dist/index.d.ts +119 -0
- package/dist/index.js +72 -0
- package/dist/lib/arrow.d.ts +6 -0
- package/dist/lib/arrow.js +15 -0
- package/dist/lib/attributes.d.ts +9 -0
- package/dist/lib/attributes.js +22 -0
- package/dist/lib/avatar.d.ts +11 -0
- package/dist/lib/avatar.js +37 -0
- package/dist/lib/calendar.d.ts +48 -0
- package/dist/lib/calendar.js +166 -0
- package/dist/lib/css-length.d.ts +2 -0
- package/dist/lib/css-length.js +8 -0
- package/dist/lib/define.d.ts +9 -0
- package/dist/lib/define.js +9 -0
- package/dist/lib/dom.d.ts +4 -0
- package/dist/lib/dom.js +16 -0
- package/dist/lib/files.d.ts +18 -0
- package/dist/lib/files.js +89 -0
- package/dist/lib/floating.d.ts +13 -0
- package/dist/lib/floating.js +127 -0
- package/dist/lib/fuzzy.d.ts +26 -0
- package/dist/lib/fuzzy.js +149 -0
- package/dist/lib/highlight.d.ts +5 -0
- package/dist/lib/highlight.js +267 -0
- package/dist/lib/hotkey.d.ts +9 -0
- package/dist/lib/hotkey.js +101 -0
- package/dist/lib/icon-set.d.ts +95 -0
- package/dist/lib/icon-set.js +97 -0
- package/dist/lib/icons.d.ts +12 -0
- package/dist/lib/icons.js +39 -0
- package/dist/lib/indicator.d.ts +6 -0
- package/dist/lib/indicator.js +18 -0
- package/dist/lib/keep-in-view.d.ts +5 -0
- package/dist/lib/keep-in-view.js +15 -0
- package/dist/lib/key-shortcut.d.ts +20 -0
- package/dist/lib/key-shortcut.js +100 -0
- package/dist/lib/keycaps.d.ts +8 -0
- package/dist/lib/keycaps.js +162 -0
- package/dist/lib/locale.d.ts +4 -0
- package/dist/lib/locale.js +13 -0
- package/dist/lib/nav-tree.d.ts +9 -0
- package/dist/lib/nav-tree.js +30 -0
- package/dist/lib/numeric.d.ts +20 -0
- package/dist/lib/numeric.js +50 -0
- package/dist/lib/option-filter.d.ts +29 -0
- package/dist/lib/option-filter.js +108 -0
- package/dist/lib/option-groups.d.ts +6 -0
- package/dist/lib/option-groups.js +26 -0
- package/dist/lib/options.d.ts +5 -0
- package/dist/lib/options.js +24 -0
- package/dist/lib/pagination.d.ts +9 -0
- package/dist/lib/pagination.js +52 -0
- package/dist/lib/palette.d.ts +8 -0
- package/dist/lib/palette.js +46 -0
- package/dist/lib/progress.d.ts +20 -0
- package/dist/lib/progress.js +33 -0
- package/dist/lib/rating.d.ts +7 -0
- package/dist/lib/rating.js +26 -0
- package/dist/lib/roving-index.d.ts +10 -0
- package/dist/lib/roving-index.js +32 -0
- package/dist/lib/roving.d.ts +11 -0
- package/dist/lib/roving.js +21 -0
- package/dist/lib/slots.d.ts +2 -0
- package/dist/lib/slots.js +6 -0
- package/dist/lib/sparkline.d.ts +11 -0
- package/dist/lib/sparkline.js +42 -0
- package/dist/lib/split.d.ts +12 -0
- package/dist/lib/split.js +37 -0
- package/dist/lib/surface.d.ts +7 -0
- package/dist/lib/surface.js +28 -0
- package/dist/lib/table.d.ts +30 -0
- package/dist/lib/table.js +110 -0
- package/dist/lib/tags.d.ts +14 -0
- package/dist/lib/tags.js +29 -0
- package/dist/lib/text.d.ts +5 -0
- package/dist/lib/text.js +13 -0
- package/dist/lib/tokens.d.ts +5 -0
- package/dist/lib/tokens.js +15 -0
- package/dist/lib/tree.d.ts +16 -0
- package/dist/lib/tree.js +89 -0
- package/dist/lib/trend.d.ts +3 -0
- package/dist/lib/trend.js +15 -0
- package/dist/lib/trigger.d.ts +11 -0
- package/dist/lib/trigger.js +34 -0
- package/dist/molecules/jl-accordion-item.css_raw.js +3 -0
- package/dist/molecules/jl-accordion-item.d.ts +34 -0
- package/dist/molecules/jl-accordion-item.js +82 -0
- package/dist/molecules/jl-accordion.css_raw.js +3 -0
- package/dist/molecules/jl-accordion.d.ts +27 -0
- package/dist/molecules/jl-accordion.js +61 -0
- package/dist/molecules/jl-alert.css_raw.js +3 -0
- package/dist/molecules/jl-alert.d.ts +38 -0
- package/dist/molecules/jl-alert.js +131 -0
- package/dist/molecules/jl-avatar-group.css_raw.js +3 -0
- package/dist/molecules/jl-avatar-group.d.ts +28 -0
- package/dist/molecules/jl-avatar-group.js +69 -0
- package/dist/molecules/jl-breadcrumb.css_raw.js +3 -0
- package/dist/molecules/jl-breadcrumb.d.ts +35 -0
- package/dist/molecules/jl-breadcrumb.js +126 -0
- package/dist/molecules/jl-calendar.css_raw.js +3 -0
- package/dist/molecules/jl-calendar.d.ts +101 -0
- package/dist/molecules/jl-calendar.js +314 -0
- package/dist/molecules/jl-card.css_raw.js +3 -0
- package/dist/molecules/jl-card.d.ts +46 -0
- package/dist/molecules/jl-card.js +172 -0
- package/dist/molecules/jl-code-block.css_raw.js +3 -0
- package/dist/molecules/jl-code-block.d.ts +44 -0
- package/dist/molecules/jl-code-block.js +106 -0
- package/dist/molecules/jl-combobox.css_raw.js +3 -0
- package/dist/molecules/jl-combobox.d.ts +120 -0
- package/dist/molecules/jl-combobox.js +450 -0
- package/dist/molecules/jl-copy-button.css_raw.js +3 -0
- package/dist/molecules/jl-copy-button.d.ts +31 -0
- package/dist/molecules/jl-copy-button.js +69 -0
- package/dist/molecules/jl-date-picker.css_raw.js +3 -0
- package/dist/molecules/jl-date-picker.d.ts +109 -0
- package/dist/molecules/jl-date-picker.js +322 -0
- package/dist/molecules/jl-empty.css_raw.js +3 -0
- package/dist/molecules/jl-empty.d.ts +32 -0
- package/dist/molecules/jl-empty.js +94 -0
- package/dist/molecules/jl-field.css_raw.js +3 -0
- package/dist/molecules/jl-field.d.ts +49 -0
- package/dist/molecules/jl-field.js +147 -0
- package/dist/molecules/jl-file-drop.css_raw.js +3 -0
- package/dist/molecules/jl-file-drop.d.ts +93 -0
- package/dist/molecules/jl-file-drop.js +366 -0
- package/dist/molecules/jl-list-item.css_raw.js +3 -0
- package/dist/molecules/jl-list-item.d.ts +56 -0
- package/dist/molecules/jl-list-item.js +176 -0
- package/dist/molecules/jl-menu.css_raw.js +3 -0
- package/dist/molecules/jl-menu.d.ts +62 -0
- package/dist/molecules/jl-menu.js +288 -0
- package/dist/molecules/jl-number-input.css_raw.js +3 -0
- package/dist/molecules/jl-number-input.d.ts +81 -0
- package/dist/molecules/jl-number-input.js +220 -0
- package/dist/molecules/jl-pagination.css_raw.js +3 -0
- package/dist/molecules/jl-pagination.d.ts +59 -0
- package/dist/molecules/jl-pagination.js +172 -0
- package/dist/molecules/jl-popover.css_raw.js +3 -0
- package/dist/molecules/jl-popover.d.ts +49 -0
- package/dist/molecules/jl-popover.js +152 -0
- package/dist/molecules/jl-radio-group.css_raw.js +3 -0
- package/dist/molecules/jl-radio-group.d.ts +71 -0
- package/dist/molecules/jl-radio-group.js +202 -0
- package/dist/molecules/jl-search.css_raw.js +3 -0
- package/dist/molecules/jl-search.d.ts +71 -0
- package/dist/molecules/jl-search.js +210 -0
- package/dist/molecules/jl-segmented.css_raw.js +3 -0
- package/dist/molecules/jl-segmented.d.ts +65 -0
- package/dist/molecules/jl-segmented.js +187 -0
- package/dist/molecules/jl-stat.css_raw.js +3 -0
- package/dist/molecules/jl-stat.d.ts +55 -0
- package/dist/molecules/jl-stat.js +169 -0
- package/dist/molecules/jl-steps.css_raw.js +3 -0
- package/dist/molecules/jl-steps.d.ts +44 -0
- package/dist/molecules/jl-steps.js +118 -0
- package/dist/molecules/jl-tab-panel.css_raw.js +3 -0
- package/dist/molecules/jl-tab-panel.d.ts +35 -0
- package/dist/molecules/jl-tab-panel.js +35 -0
- package/dist/molecules/jl-tabs.css_raw.js +3 -0
- package/dist/molecules/jl-tabs.d.ts +51 -0
- package/dist/molecules/jl-tabs.js +198 -0
- package/dist/molecules/jl-tag-input.css_raw.js +3 -0
- package/dist/molecules/jl-tag-input.d.ts +103 -0
- package/dist/molecules/jl-tag-input.js +429 -0
- package/dist/molecules/jl-timeline.css_raw.js +3 -0
- package/dist/molecules/jl-timeline.d.ts +28 -0
- package/dist/molecules/jl-timeline.js +81 -0
- package/dist/molecules/jl-toast.css_raw.js +3 -0
- package/dist/molecules/jl-toast.d.ts +75 -0
- package/dist/molecules/jl-toast.js +274 -0
- package/dist/molecules/jl-tooltip.css_raw.js +3 -0
- package/dist/molecules/jl-tooltip.d.ts +44 -0
- package/dist/molecules/jl-tooltip.js +160 -0
- package/dist/organisms/jl-command-palette.css_raw.js +3 -0
- package/dist/organisms/jl-command-palette.d.ts +60 -0
- package/dist/organisms/jl-command-palette.js +285 -0
- package/dist/organisms/jl-confirm.css_raw.js +3 -0
- package/dist/organisms/jl-confirm.d.ts +56 -0
- package/dist/organisms/jl-confirm.js +132 -0
- package/dist/organisms/jl-dialog.css_raw.js +3 -0
- package/dist/organisms/jl-dialog.d.ts +43 -0
- package/dist/organisms/jl-dialog.js +120 -0
- package/dist/organisms/jl-drawer.css_raw.js +3 -0
- package/dist/organisms/jl-drawer.d.ts +50 -0
- package/dist/organisms/jl-drawer.js +141 -0
- package/dist/organisms/jl-navbar.css_raw.js +3 -0
- package/dist/organisms/jl-navbar.d.ts +32 -0
- package/dist/organisms/jl-navbar.js +121 -0
- package/dist/organisms/jl-page-header.css_raw.js +3 -0
- package/dist/organisms/jl-page-header.d.ts +26 -0
- package/dist/organisms/jl-page-header.js +85 -0
- package/dist/organisms/jl-side-nav.css_raw.js +3 -0
- package/dist/organisms/jl-side-nav.d.ts +60 -0
- package/dist/organisms/jl-side-nav.js +269 -0
- package/dist/organisms/jl-table.css_raw.js +3 -0
- package/dist/organisms/jl-table.d.ts +109 -0
- package/dist/organisms/jl-table.js +318 -0
- package/dist/organisms/jl-toaster.css_raw.js +3 -0
- package/dist/organisms/jl-toaster.d.ts +48 -0
- package/dist/organisms/jl-toaster.js +142 -0
- package/dist/organisms/jl-tree.css_raw.js +3 -0
- package/dist/organisms/jl-tree.d.ts +59 -0
- package/dist/organisms/jl-tree.js +198 -0
- package/dist/styles/base.css_raw.js +3 -0
- package/dist/styles/control.css_raw.js +3 -0
- package/dist/styles/floating.css_raw.js +3 -0
- package/dist/styles/shared.d.ts +8 -0
- package/dist/styles/shared.js +14 -0
- package/dist/styles/tokens.css_raw.js +3 -0
- package/dist/templates/jl-app-shell.css_raw.js +3 -0
- package/dist/templates/jl-app-shell.d.ts +37 -0
- package/dist/templates/jl-app-shell.js +150 -0
- package/dist/templates/jl-split-view.css_raw.js +3 -0
- package/dist/templates/jl-split-view.d.ts +49 -0
- package/dist/templates/jl-split-view.js +149 -0
- package/dist/tokens.css +188 -0
- package/dist/types/calendar.types.d.ts +11 -0
- package/dist/types/files.types.d.ts +23 -0
- package/dist/types/floating.types.d.ts +13 -0
- package/dist/types/fuzzy.types.d.ts +10 -0
- package/dist/types/highlight.types.d.ts +6 -0
- package/dist/types/hotkey.types.d.ts +19 -0
- package/dist/types/jl-accordion.types.d.ts +2 -0
- package/dist/types/jl-avatar.types.d.ts +10 -0
- package/dist/types/jl-breadcrumb.types.d.ts +6 -0
- package/dist/types/jl-button.types.d.ts +3 -0
- package/dist/types/jl-card.types.d.ts +2 -0
- package/dist/types/jl-chip.types.d.ts +1 -0
- package/dist/types/jl-combobox.types.d.ts +8 -0
- package/dist/types/jl-command-palette.types.d.ts +22 -0
- package/dist/types/jl-confirm.types.d.ts +10 -0
- package/dist/types/jl-dialog.types.d.ts +1 -0
- package/dist/types/jl-divider.types.d.ts +2 -0
- package/dist/types/jl-drawer.types.d.ts +4 -0
- package/dist/types/jl-empty.types.d.ts +2 -0
- package/dist/types/jl-field.types.d.ts +2 -0
- package/dist/types/jl-input.types.d.ts +1 -0
- package/dist/types/jl-link.types.d.ts +2 -0
- package/dist/types/jl-menu.types.d.ts +22 -0
- package/dist/types/jl-navbar.types.d.ts +6 -0
- package/dist/types/jl-progress.types.d.ts +3 -0
- package/dist/types/jl-radio-group.types.d.ts +10 -0
- package/dist/types/jl-segmented.types.d.ts +7 -0
- package/dist/types/jl-select.types.d.ts +12 -0
- package/dist/types/jl-side-nav.types.d.ts +17 -0
- package/dist/types/jl-skeleton.types.d.ts +2 -0
- package/dist/types/jl-slider.types.d.ts +5 -0
- package/dist/types/jl-split-view.types.d.ts +2 -0
- package/dist/types/jl-stat.types.d.ts +2 -0
- package/dist/types/jl-steps.types.d.ts +8 -0
- package/dist/types/jl-switch.types.d.ts +1 -0
- package/dist/types/jl-table.types.d.ts +30 -0
- package/dist/types/jl-tabs.types.d.ts +2 -0
- package/dist/types/jl-timeline.types.d.ts +10 -0
- package/dist/types/jl-toast.types.d.ts +2 -0
- package/dist/types/jl-toaster.types.d.ts +23 -0
- package/dist/types/jl-tree.types.d.ts +26 -0
- package/dist/types/key-shortcut.types.d.ts +9 -0
- package/dist/types/keycaps.types.d.ts +7 -0
- package/dist/types/option-filter.types.d.ts +10 -0
- package/dist/types/options.types.d.ts +4 -0
- package/dist/types/pagination.types.d.ts +8 -0
- package/dist/types/roving-index.types.d.ts +2 -0
- package/dist/types/sparkline.types.d.ts +11 -0
- package/package.json +96 -4
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { JlElement } from '../base/jl-element';
|
|
2
|
+
/**
|
|
3
|
+
* A row of a list: an icon or an avatar, a heading and a description, a short note at the end and trailing
|
|
4
|
+
* controls. With `href` the row is a link; with `interactive` it fires `press`. Rows next to each other are
|
|
5
|
+
* separated by a line.
|
|
6
|
+
*/
|
|
7
|
+
export declare class JlListItem extends JlElement {
|
|
8
|
+
static readonly selector = "jl-list-item";
|
|
9
|
+
readonly events: Readonly<import("@jadis/core").UseEventsHandler<{
|
|
10
|
+
press: undefined;
|
|
11
|
+
}>>;
|
|
12
|
+
readonly heading: Readonly<Readonly<{
|
|
13
|
+
get(): string;
|
|
14
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
15
|
+
}>>;
|
|
16
|
+
readonly description: Readonly<Readonly<{
|
|
17
|
+
get(): string;
|
|
18
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
19
|
+
}>>;
|
|
20
|
+
/** An icon at the start; `slot="start"` takes an avatar instead. */
|
|
21
|
+
readonly icon: Readonly<Readonly<{
|
|
22
|
+
get(): string;
|
|
23
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
24
|
+
}>>;
|
|
25
|
+
/** A short note at the end: a time, a count, a size. */
|
|
26
|
+
readonly meta: Readonly<Readonly<{
|
|
27
|
+
get(): string;
|
|
28
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
29
|
+
}>>;
|
|
30
|
+
/** Makes the row a link, named by its heading. The trailing controls stay pressable on their own. */
|
|
31
|
+
readonly href: Readonly<Readonly<{
|
|
32
|
+
get(): string;
|
|
33
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
34
|
+
}>>;
|
|
35
|
+
/** Can be focused and pressed as a whole, and fires `press`. */
|
|
36
|
+
readonly interactive: Readonly<Readonly<{
|
|
37
|
+
get(): boolean;
|
|
38
|
+
set(setter: boolean | ((prevState: boolean) => boolean)): void;
|
|
39
|
+
}>>;
|
|
40
|
+
/** The current row: the open page of a menu, the shown item of a master list. */
|
|
41
|
+
readonly selected: Readonly<Readonly<{
|
|
42
|
+
get(): boolean;
|
|
43
|
+
set(setter: boolean | ((prevState: boolean) => boolean)): void;
|
|
44
|
+
}>>;
|
|
45
|
+
private readonly _refs;
|
|
46
|
+
focus(options?: FocusOptions): void;
|
|
47
|
+
templateHtml(): Node;
|
|
48
|
+
templateCss(): string;
|
|
49
|
+
onConnect(): void;
|
|
50
|
+
/** An interactive row without a link fires `press`; a link row leaves the press to the link. */
|
|
51
|
+
private pressable;
|
|
52
|
+
private renderInteraction;
|
|
53
|
+
/** The start holds an avatar when one is slotted, otherwise the icon, otherwise nothing. */
|
|
54
|
+
private renderStart;
|
|
55
|
+
private renderEnd;
|
|
56
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { BASE_STYLES } from "../styles/shared.js";
|
|
2
|
+
import { JlElement } from "../base/jl-element.js";
|
|
3
|
+
import { registerIcons } from "../lib/icons.js";
|
|
4
|
+
import { setOrRemove } from "../lib/dom.js";
|
|
5
|
+
import { hasSlotted } from "../lib/slots.js";
|
|
6
|
+
import { JlIcon } from "../atoms/jl-icon.js";
|
|
7
|
+
import { isActivationKey, startedOnControl } from "../lib/surface.js";
|
|
8
|
+
import jl_list_item_default from "./jl-list-item.css_raw.js";
|
|
9
|
+
import { css } from "@jadis/core";
|
|
10
|
+
import { ChevronRight } from "lucide";
|
|
11
|
+
import { jsx, jsxs } from "@jadis/core/jsx-runtime";
|
|
12
|
+
// src/molecules/jl-list-item.tsx
|
|
13
|
+
registerIcons({ "chevron-right": ChevronRight });
|
|
14
|
+
var ICON_SIZE = 16;
|
|
15
|
+
var CHEVRON_SIZE = 16;
|
|
16
|
+
var DEFAULT_LINK_NAME = "Open";
|
|
17
|
+
/**
|
|
18
|
+
* A row of a list: an icon or an avatar, a heading and a description, a short note at the end and trailing
|
|
19
|
+
* controls. With `href` the row is a link; with `interactive` it fires `press`. Rows next to each other are
|
|
20
|
+
* separated by a line.
|
|
21
|
+
*/
|
|
22
|
+
var JlListItem = class extends JlElement {
|
|
23
|
+
static selector = "jl-list-item";
|
|
24
|
+
events = this.useEvents();
|
|
25
|
+
heading = this.useChange("", (value) => {
|
|
26
|
+
this._refs.heading.textContent = value;
|
|
27
|
+
this._refs.linkName.textContent = value || DEFAULT_LINK_NAME;
|
|
28
|
+
}, { attribute: "heading" });
|
|
29
|
+
description = this.useChange("", (value) => {
|
|
30
|
+
this._refs.description.textContent = value;
|
|
31
|
+
this._refs.description.hidden = value === "";
|
|
32
|
+
}, { attribute: "description" });
|
|
33
|
+
/** An icon at the start; `slot="start"` takes an avatar instead. */
|
|
34
|
+
icon = this.useChange("", () => this.renderStart(), { attribute: "icon" });
|
|
35
|
+
/** A short note at the end: a time, a count, a size. */
|
|
36
|
+
meta = this.useChange("", (value) => {
|
|
37
|
+
this._refs.meta.textContent = value;
|
|
38
|
+
this._refs.meta.hidden = value === "";
|
|
39
|
+
}, { attribute: "meta" });
|
|
40
|
+
/** Makes the row a link, named by its heading. The trailing controls stay pressable on their own. */
|
|
41
|
+
href = this.useChange("", () => this.renderInteraction(), { attribute: "href" });
|
|
42
|
+
/** Can be focused and pressed as a whole, and fires `press`. */
|
|
43
|
+
interactive = this.useChange(false, () => this.renderInteraction(), { attribute: "interactive" });
|
|
44
|
+
/** The current row: the open page of a menu, the shown item of a master list. */
|
|
45
|
+
selected = this.useChange(false, (value) => {
|
|
46
|
+
this.reflect("selected", value);
|
|
47
|
+
this.renderInteraction();
|
|
48
|
+
}, { attribute: "selected" });
|
|
49
|
+
_refs = this.useRefs((ref) => ({
|
|
50
|
+
chevron: ref(".chevron"),
|
|
51
|
+
description: ref(".description"),
|
|
52
|
+
end: ref(".end"),
|
|
53
|
+
endSlot: ref("slot[name=\"end\"]"),
|
|
54
|
+
heading: ref(".heading"),
|
|
55
|
+
icon: ref(".icon"),
|
|
56
|
+
iconBox: ref(".icon-box"),
|
|
57
|
+
item: ref(".item"),
|
|
58
|
+
link: ref(".link"),
|
|
59
|
+
linkName: ref(".link-name"),
|
|
60
|
+
meta: ref(".meta"),
|
|
61
|
+
start: ref(".start"),
|
|
62
|
+
startSlot: ref("slot[name=\"start\"]")
|
|
63
|
+
}));
|
|
64
|
+
focus(options) {
|
|
65
|
+
(this.href.get() === "" ? this._refs.item : this._refs.link).focus(options);
|
|
66
|
+
}
|
|
67
|
+
templateHtml() {
|
|
68
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
69
|
+
class: "item",
|
|
70
|
+
attrs: { part: "base" },
|
|
71
|
+
children: [
|
|
72
|
+
/* @__PURE__ */ jsx("a", {
|
|
73
|
+
class: "link",
|
|
74
|
+
"aria-describedby": "description",
|
|
75
|
+
hidden: true,
|
|
76
|
+
children: /* @__PURE__ */ jsx("span", { class: "link-name sr-only" })
|
|
77
|
+
}),
|
|
78
|
+
/* @__PURE__ */ jsxs("span", {
|
|
79
|
+
class: "start",
|
|
80
|
+
hidden: true,
|
|
81
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
82
|
+
class: "icon-box",
|
|
83
|
+
children: /* @__PURE__ */ jsx(JlIcon, {
|
|
84
|
+
class: "icon",
|
|
85
|
+
size: ICON_SIZE
|
|
86
|
+
})
|
|
87
|
+
}), /* @__PURE__ */ jsx("slot", { name: "start" })]
|
|
88
|
+
}),
|
|
89
|
+
/* @__PURE__ */ jsxs("span", {
|
|
90
|
+
class: "text",
|
|
91
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
92
|
+
class: "heading",
|
|
93
|
+
id: "heading"
|
|
94
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
95
|
+
class: "description",
|
|
96
|
+
id: "description",
|
|
97
|
+
hidden: true
|
|
98
|
+
})]
|
|
99
|
+
}),
|
|
100
|
+
/* @__PURE__ */ jsx("span", {
|
|
101
|
+
class: "meta",
|
|
102
|
+
hidden: true
|
|
103
|
+
}),
|
|
104
|
+
/* @__PURE__ */ jsx("span", {
|
|
105
|
+
class: "end",
|
|
106
|
+
hidden: true,
|
|
107
|
+
children: /* @__PURE__ */ jsx("slot", { name: "end" })
|
|
108
|
+
}),
|
|
109
|
+
/* @__PURE__ */ jsx(JlIcon, {
|
|
110
|
+
class: "chevron",
|
|
111
|
+
name: "chevron-right",
|
|
112
|
+
size: CHEVRON_SIZE,
|
|
113
|
+
hidden: true
|
|
114
|
+
})
|
|
115
|
+
]
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
templateCss() {
|
|
119
|
+
return css`${BASE_STYLES}${jl_list_item_default}`;
|
|
120
|
+
}
|
|
121
|
+
onConnect() {
|
|
122
|
+
const item = this._refs.item;
|
|
123
|
+
this.renderStart();
|
|
124
|
+
this.renderEnd();
|
|
125
|
+
this.on(this._refs.startSlot, "slotchange", () => this.renderStart());
|
|
126
|
+
this.on(this._refs.endSlot, "slotchange", () => this.renderEnd());
|
|
127
|
+
this.on(item, "click", (event) => {
|
|
128
|
+
if (this.pressable() && !startedOnControl(event, item)) this.events.emit("press");
|
|
129
|
+
});
|
|
130
|
+
this.on(item, "keydown", (event) => {
|
|
131
|
+
if (this.pressable() && event.composedPath()[0] === item && isActivationKey(event)) {
|
|
132
|
+
event.preventDefault();
|
|
133
|
+
this.events.emit("press");
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/** An interactive row without a link fires `press`; a link row leaves the press to the link. */
|
|
138
|
+
pressable() {
|
|
139
|
+
return this.interactive.get() && this.href.get() === "";
|
|
140
|
+
}
|
|
141
|
+
renderInteraction() {
|
|
142
|
+
const href = this.href.get();
|
|
143
|
+
const item = this._refs.item;
|
|
144
|
+
const link = this._refs.link;
|
|
145
|
+
const selected = this.selected.get();
|
|
146
|
+
setOrRemove(link, "href", href);
|
|
147
|
+
link.hidden = href === "";
|
|
148
|
+
this._refs.chevron.hidden = href === "";
|
|
149
|
+
this.reflect("clickable", this.interactive.get() || href !== "");
|
|
150
|
+
setOrRemove(link, "aria-current", selected ? "page" : "");
|
|
151
|
+
if (!this.pressable()) {
|
|
152
|
+
item.removeAttribute("tabindex");
|
|
153
|
+
item.removeAttribute("role");
|
|
154
|
+
item.removeAttribute("aria-labelledby");
|
|
155
|
+
item.removeAttribute("aria-current");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
item.tabIndex = 0;
|
|
159
|
+
item.setAttribute("role", "button");
|
|
160
|
+
item.setAttribute("aria-labelledby", "heading");
|
|
161
|
+
setOrRemove(item, "aria-current", selected ? "true" : "");
|
|
162
|
+
}
|
|
163
|
+
/** The start holds an avatar when one is slotted, otherwise the icon, otherwise nothing. */
|
|
164
|
+
renderStart() {
|
|
165
|
+
const icon = this.icon.get();
|
|
166
|
+
const slotted = hasSlotted(this._refs.startSlot);
|
|
167
|
+
this._refs.icon.name.set(icon);
|
|
168
|
+
this._refs.iconBox.hidden = slotted || icon === "";
|
|
169
|
+
this._refs.start.hidden = !slotted && icon === "";
|
|
170
|
+
}
|
|
171
|
+
renderEnd() {
|
|
172
|
+
this._refs.end.hidden = !hasSlotted(this._refs.endSlot);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
JlListItem.register();
|
|
176
|
+
export { JlListItem };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// src/molecules/jl-menu.css?raw
|
|
2
|
+
var jl_menu_default = ":host {\n display: inline-flex;\n vertical-align: middle;\n}\n\n.menu {\n min-width: 200px;\n max-width: min(320px, calc(100vw - 16px));\n max-height: min(420px, calc(100dvh - 16px));\n padding: 4px;\n overflow-y: auto;\n}\n\n.menu:focus-visible {\n box-shadow: var(--jl-shadow-lg), var(--jl-focus-ring);\n}\n\n.item {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n min-height: 32px;\n padding: 6px 10px;\n border-radius: var(--jl-radius-xs);\n color: var(--jl-text);\n font-size: var(--jl-font-md);\n line-height: 20px;\n cursor: pointer;\n user-select: none;\n}\n\n/* The focused item is the highlighted one, for the pointer as for the keyboard. */\n.item:focus,\n.item:focus-visible {\n outline: none;\n box-shadow: none;\n background: var(--jl-surface-3);\n}\n\n.icon {\n flex: none;\n margin-top: 2px;\n color: var(--jl-muted);\n}\n\n.text {\n display: grid;\n flex: 1;\n min-width: 0;\n}\n\n.label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.description {\n color: var(--jl-muted);\n font-size: var(--jl-font-xs);\n line-height: 1.4;\n}\n\n.shortcut {\n flex: none;\n margin-left: 12px;\n color: var(--jl-muted);\n font-size: var(--jl-font-xs);\n letter-spacing: 0.02em;\n}\n\n.item[data-danger] {\n color: var(--jl-danger);\n}\n\n.item[data-danger] .icon {\n color: inherit;\n}\n\n.item[data-danger]:focus {\n background: var(--jl-danger-soft);\n}\n\n.item[aria-disabled] {\n color: var(--jl-faint);\n cursor: not-allowed;\n}\n\n.item[aria-disabled] .icon,\n.item[aria-disabled] .description {\n color: inherit;\n}\n\n.heading {\n padding: 8px 10px 4px;\n color: var(--jl-muted);\n font-size: 11px;\n font-weight: 650;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n}\n\n.divider {\n height: 1px;\n margin: 4px -4px;\n border: none;\n background: var(--jl-line);\n}\n";
|
|
3
|
+
export { jl_menu_default as default };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { JlElement } from '../base/jl-element';
|
|
2
|
+
import type { Placement } from '../types/floating.types';
|
|
3
|
+
import type { MenuItem } from '../types/jl-menu.types';
|
|
4
|
+
/**
|
|
5
|
+
* A list of actions that opens from its trigger (`slot="trigger"`, any button): on a press or with ArrowDown. Arrows,
|
|
6
|
+
* Home, End and the first letter move through it; Enter chooses, Escape closes. `select` fires with the value.
|
|
7
|
+
*/
|
|
8
|
+
export declare class JlMenu extends JlElement {
|
|
9
|
+
static readonly selector = "jl-menu";
|
|
10
|
+
readonly events: Readonly<import("@jadis/core").UseEventsHandler<{
|
|
11
|
+
select: string;
|
|
12
|
+
open: undefined;
|
|
13
|
+
close: undefined;
|
|
14
|
+
}>>;
|
|
15
|
+
readonly items: Readonly<Readonly<{
|
|
16
|
+
get(): MenuItem[];
|
|
17
|
+
set(setter: MenuItem[] | ((prevState: MenuItem[]) => MenuItem[])): void;
|
|
18
|
+
}>>;
|
|
19
|
+
/** Where the menu goes; it flips to the other side when there is no room. */
|
|
20
|
+
readonly placement: Readonly<Readonly<{
|
|
21
|
+
get(): Placement;
|
|
22
|
+
set(setter: ((prevState: Placement) => Placement) | Placement): void;
|
|
23
|
+
}>>;
|
|
24
|
+
/** The menu's name for screen readers; the trigger names it otherwise. */
|
|
25
|
+
readonly label: Readonly<Readonly<{
|
|
26
|
+
get(): string;
|
|
27
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
28
|
+
}>>;
|
|
29
|
+
private readonly _state;
|
|
30
|
+
private readonly _refs;
|
|
31
|
+
/** Opens the menu and focuses its first item. */
|
|
32
|
+
show(): void;
|
|
33
|
+
hide(): void;
|
|
34
|
+
templateHtml(): Node;
|
|
35
|
+
templateCss(): string;
|
|
36
|
+
onConnect(): void;
|
|
37
|
+
onDisconnect(): void;
|
|
38
|
+
private triggerElement;
|
|
39
|
+
private isOpen;
|
|
40
|
+
/** Every item, the disabled ones included, in order. */
|
|
41
|
+
private allItems;
|
|
42
|
+
/** The items people can choose. */
|
|
43
|
+
private menuItems;
|
|
44
|
+
private enabledFlags;
|
|
45
|
+
private onTriggerClick;
|
|
46
|
+
private onTriggerKeydown;
|
|
47
|
+
private onMenuKeydown;
|
|
48
|
+
private open;
|
|
49
|
+
private close;
|
|
50
|
+
private focusItem;
|
|
51
|
+
/** Chooses an item people picked: the menu closes, the focus goes back to the trigger, and `select` fires. */
|
|
52
|
+
private chooseItem;
|
|
53
|
+
private onToggle;
|
|
54
|
+
private focusIsInMenu;
|
|
55
|
+
/** Keeps the menu by the trigger while it is open. */
|
|
56
|
+
private follow;
|
|
57
|
+
private renderTrigger;
|
|
58
|
+
private renderName;
|
|
59
|
+
private renderItems;
|
|
60
|
+
private section;
|
|
61
|
+
private entry;
|
|
62
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { BASE_STYLES, FLOATING_STYLES } from "../styles/shared.js";
|
|
2
|
+
import { JlElement } from "../base/jl-element.js";
|
|
3
|
+
import { jsonOf, oneOf } from "../lib/attributes.js";
|
|
4
|
+
import { PLACEMENTS, followAnchor } from "../lib/floating.js";
|
|
5
|
+
import { uniqueId } from "../lib/dom.js";
|
|
6
|
+
import { JlIcon } from "../atoms/jl-icon.js";
|
|
7
|
+
import { firstIndex, lastIndex, letterIndex, stepIndex } from "../lib/roving.js";
|
|
8
|
+
import { setTriggerAria } from "../lib/trigger.js";
|
|
9
|
+
import jl_menu_default from "./jl-menu.css_raw.js";
|
|
10
|
+
import { css } from "@jadis/core";
|
|
11
|
+
import { Fragment, jsx, jsxs } from "@jadis/core/jsx-runtime";
|
|
12
|
+
// src/molecules/jl-menu.tsx
|
|
13
|
+
var ICON_SIZE = 16;
|
|
14
|
+
var CLOSED = "closed";
|
|
15
|
+
var ITEM_SELECTOR = "[role=\"menuitem\"]";
|
|
16
|
+
var KEY_MOVES = /* @__PURE__ */ new Map([
|
|
17
|
+
["ArrowDown", (enabled, from) => stepIndex(enabled, from, 1)],
|
|
18
|
+
["ArrowUp", (enabled, from) => stepIndex(enabled, from, -1)],
|
|
19
|
+
["End", lastIndex],
|
|
20
|
+
["Home", firstIndex]
|
|
21
|
+
]);
|
|
22
|
+
var OPENING_KEYS = /* @__PURE__ */ new Map([["ArrowDown", firstIndex], ["ArrowUp", lastIndex]]);
|
|
23
|
+
var CHOOSING_KEYS = /* @__PURE__ */ new Set(["Enter", " "]);
|
|
24
|
+
/** The items split under their headings; what comes before the first heading has none. */
|
|
25
|
+
function sectionsOf(items) {
|
|
26
|
+
const sections = [{
|
|
27
|
+
entries: [],
|
|
28
|
+
heading: ""
|
|
29
|
+
}];
|
|
30
|
+
for (const item of items) {
|
|
31
|
+
if ("heading" in item) {
|
|
32
|
+
sections.push({
|
|
33
|
+
entries: [],
|
|
34
|
+
heading: item.heading
|
|
35
|
+
});
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
sections.at(-1)?.entries.push(item);
|
|
39
|
+
}
|
|
40
|
+
return sections;
|
|
41
|
+
}
|
|
42
|
+
/** A single printable character typed without a modifier: a letter for typeahead. */
|
|
43
|
+
var isTypeahead = (event) => event.key.length === 1 && event.key !== " " && !event.ctrlKey && !event.metaKey && !event.altKey;
|
|
44
|
+
/**
|
|
45
|
+
* A list of actions that opens from its trigger (`slot="trigger"`, any button): on a press or with ArrowDown. Arrows,
|
|
46
|
+
* Home, End and the first letter move through it; Enter chooses, Escape closes. `select` fires with the value.
|
|
47
|
+
*/
|
|
48
|
+
var JlMenu = class extends JlElement {
|
|
49
|
+
static selector = "jl-menu";
|
|
50
|
+
events = this.useEvents();
|
|
51
|
+
items = this.useChange([], () => this.renderItems(), {
|
|
52
|
+
attribute: "items",
|
|
53
|
+
parse: jsonOf([])
|
|
54
|
+
});
|
|
55
|
+
/** Where the menu goes; it flips to the other side when there is no room. */
|
|
56
|
+
placement = this.useChange("bottom-start", () => this.follow(), {
|
|
57
|
+
attribute: "placement",
|
|
58
|
+
parse: oneOf(PLACEMENTS, "bottom-start")
|
|
59
|
+
});
|
|
60
|
+
/** The menu's name for screen readers; the trigger names it otherwise. */
|
|
61
|
+
label = this.useChange("", () => this.renderName(), { attribute: "label" });
|
|
62
|
+
_state = {
|
|
63
|
+
following: new AbortController(),
|
|
64
|
+
openAtPointerDown: false
|
|
65
|
+
};
|
|
66
|
+
_refs = this.useRefs((ref) => ({
|
|
67
|
+
menu: ref(".menu"),
|
|
68
|
+
trigger: ref("slot[name=\"trigger\"]")
|
|
69
|
+
}));
|
|
70
|
+
/** Opens the menu and focuses its first item. */
|
|
71
|
+
show() {
|
|
72
|
+
this.whenRendered(() => this.open(firstIndex));
|
|
73
|
+
}
|
|
74
|
+
hide() {
|
|
75
|
+
this.whenRendered(() => this.close());
|
|
76
|
+
}
|
|
77
|
+
templateHtml() {
|
|
78
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("slot", { name: "trigger" }), /* @__PURE__ */ jsx("div", {
|
|
79
|
+
class: "floating panel menu",
|
|
80
|
+
popover: "auto",
|
|
81
|
+
role: "menu",
|
|
82
|
+
tabIndex: -1,
|
|
83
|
+
attrs: { part: "base" }
|
|
84
|
+
})] });
|
|
85
|
+
}
|
|
86
|
+
templateCss() {
|
|
87
|
+
return css`${BASE_STYLES}${FLOATING_STYLES}${jl_menu_default}`;
|
|
88
|
+
}
|
|
89
|
+
onConnect() {
|
|
90
|
+
const menu = this._refs.menu;
|
|
91
|
+
const trigger = this._refs.trigger;
|
|
92
|
+
this.renderTrigger();
|
|
93
|
+
this.on(trigger, "slotchange", () => this.renderTrigger());
|
|
94
|
+
this.on(trigger, "pointerdown", () => {
|
|
95
|
+
this._state.openAtPointerDown = this.isOpen();
|
|
96
|
+
});
|
|
97
|
+
this.on(trigger, "click", () => this.onTriggerClick());
|
|
98
|
+
this.on(trigger, "keydown", (event) => this.onTriggerKeydown(event));
|
|
99
|
+
this.on(menu, "keydown", (event) => this.onMenuKeydown(event));
|
|
100
|
+
this.on(menu, "click", (event) => this.chooseItem(event.target.closest(ITEM_SELECTOR)));
|
|
101
|
+
this.on(menu, "pointermove", (event) => {
|
|
102
|
+
const item = event.target.closest(ITEM_SELECTOR);
|
|
103
|
+
if (item !== null && this.menuItems().includes(item) && item !== this.shadowRoot?.activeElement) item.focus();
|
|
104
|
+
});
|
|
105
|
+
this.on(menu, "focusout", () => {
|
|
106
|
+
window.setTimeout(() => {
|
|
107
|
+
if (!this.focusIsInMenu()) this.close();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
this.on(menu, "toggle", (event) => this.onToggle(event.newState !== CLOSED));
|
|
111
|
+
}
|
|
112
|
+
onDisconnect() {
|
|
113
|
+
this._state.following.abort();
|
|
114
|
+
}
|
|
115
|
+
triggerElement() {
|
|
116
|
+
const [element] = this._refs.trigger.assignedElements({ flatten: true });
|
|
117
|
+
return element instanceof HTMLElement ? element : null;
|
|
118
|
+
}
|
|
119
|
+
isOpen() {
|
|
120
|
+
return this._refs.menu.matches(":popover-open");
|
|
121
|
+
}
|
|
122
|
+
/** Every item, the disabled ones included, in order. */
|
|
123
|
+
allItems() {
|
|
124
|
+
return [...this._refs.menu.querySelectorAll(ITEM_SELECTOR)];
|
|
125
|
+
}
|
|
126
|
+
/** The items people can choose. */
|
|
127
|
+
menuItems() {
|
|
128
|
+
return this.allItems().filter((item) => !item.hasAttribute("aria-disabled"));
|
|
129
|
+
}
|
|
130
|
+
enabledFlags() {
|
|
131
|
+
return this.allItems().map((item) => !item.hasAttribute("aria-disabled"));
|
|
132
|
+
}
|
|
133
|
+
onTriggerClick() {
|
|
134
|
+
const closedByPointer = this._state.openAtPointerDown;
|
|
135
|
+
this._state.openAtPointerDown = false;
|
|
136
|
+
if (closedByPointer || this.isOpen()) {
|
|
137
|
+
this.close();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
this.open(firstIndex);
|
|
141
|
+
}
|
|
142
|
+
onTriggerKeydown(event) {
|
|
143
|
+
const focusFirst = OPENING_KEYS.get(event.key);
|
|
144
|
+
if (focusFirst === void 0) return;
|
|
145
|
+
event.preventDefault();
|
|
146
|
+
this.open(focusFirst);
|
|
147
|
+
}
|
|
148
|
+
onMenuKeydown(event) {
|
|
149
|
+
const items = this.allItems();
|
|
150
|
+
const from = items.indexOf(event.target);
|
|
151
|
+
const move = KEY_MOVES.get(event.key);
|
|
152
|
+
if (move !== void 0) {
|
|
153
|
+
event.preventDefault();
|
|
154
|
+
this.focusItem(move(this.enabledFlags(), from));
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (CHOOSING_KEYS.has(event.key)) {
|
|
158
|
+
event.preventDefault();
|
|
159
|
+
this.chooseItem(items[from] ?? null);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (event.key === "Escape") {
|
|
163
|
+
event.preventDefault();
|
|
164
|
+
this.close();
|
|
165
|
+
this.triggerElement()?.focus();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (isTypeahead(event)) {
|
|
169
|
+
const labels = items.map((item) => item.querySelector(".label")?.textContent ?? "");
|
|
170
|
+
this.focusItem(letterIndex(labels, this.enabledFlags(), from, event.key));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
open(focusFirst) {
|
|
174
|
+
const menu = this._refs.menu;
|
|
175
|
+
if (!this.isOpen()) {
|
|
176
|
+
menu.showPopover();
|
|
177
|
+
this.follow();
|
|
178
|
+
}
|
|
179
|
+
const index = focusFirst(this.enabledFlags());
|
|
180
|
+
if (index === -1) {
|
|
181
|
+
menu.focus();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
this.focusItem(index);
|
|
185
|
+
}
|
|
186
|
+
close() {
|
|
187
|
+
if (this.isOpen()) this._refs.menu.hidePopover();
|
|
188
|
+
}
|
|
189
|
+
focusItem(index) {
|
|
190
|
+
this.allItems()[index]?.focus();
|
|
191
|
+
}
|
|
192
|
+
/** Chooses an item people picked: the menu closes, the focus goes back to the trigger, and `select` fires. */
|
|
193
|
+
chooseItem(item) {
|
|
194
|
+
if (!(item instanceof HTMLElement) || item.hasAttribute("aria-disabled")) return;
|
|
195
|
+
this.close();
|
|
196
|
+
this.triggerElement()?.focus();
|
|
197
|
+
this.events.emit("select", item.dataset.value ?? "");
|
|
198
|
+
}
|
|
199
|
+
onToggle(open) {
|
|
200
|
+
this.renderTrigger();
|
|
201
|
+
if (!open) this._state.following.abort();
|
|
202
|
+
this.events.emit(open ? "open" : "close");
|
|
203
|
+
}
|
|
204
|
+
focusIsInMenu() {
|
|
205
|
+
const active = this.shadowRoot?.activeElement ?? null;
|
|
206
|
+
return active !== null && this._refs.menu.contains(active);
|
|
207
|
+
}
|
|
208
|
+
/** Keeps the menu by the trigger while it is open. */
|
|
209
|
+
follow() {
|
|
210
|
+
this._state.following.abort();
|
|
211
|
+
const trigger = this.triggerElement();
|
|
212
|
+
if (!this.isOpen() || trigger === null) return;
|
|
213
|
+
this._state.following = new AbortController();
|
|
214
|
+
followAnchor(trigger, this._refs.menu, this.placement.get(), this._state.following.signal);
|
|
215
|
+
}
|
|
216
|
+
renderTrigger() {
|
|
217
|
+
const trigger = this.triggerElement();
|
|
218
|
+
if (trigger === null) return;
|
|
219
|
+
setTriggerAria(trigger, "aria-haspopup", "menu");
|
|
220
|
+
setTriggerAria(trigger, "aria-expanded", String(this.isOpen()));
|
|
221
|
+
this.renderName();
|
|
222
|
+
}
|
|
223
|
+
renderName() {
|
|
224
|
+
const menu = this._refs.menu;
|
|
225
|
+
const label = this.label.get();
|
|
226
|
+
if (label !== "") {
|
|
227
|
+
menu.setAttribute("aria-label", label);
|
|
228
|
+
menu.ariaLabelledByElements = null;
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
menu.removeAttribute("aria-label");
|
|
232
|
+
const trigger = this.triggerElement();
|
|
233
|
+
menu.ariaLabelledByElements = trigger === null ? null : [trigger];
|
|
234
|
+
}
|
|
235
|
+
renderItems() {
|
|
236
|
+
this._refs.menu.replaceChildren(...sectionsOf(this.items.get()).map((section) => this.section(section)));
|
|
237
|
+
}
|
|
238
|
+
section(section) {
|
|
239
|
+
const entries = section.entries.map((entry) => this.entry(entry));
|
|
240
|
+
if (section.heading === "") return /* @__PURE__ */ jsx(Fragment, { children: entries });
|
|
241
|
+
const id = uniqueId("jl-menu-heading");
|
|
242
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
243
|
+
class: "group",
|
|
244
|
+
role: "group",
|
|
245
|
+
"aria-labelledby": id,
|
|
246
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
247
|
+
class: "heading",
|
|
248
|
+
id,
|
|
249
|
+
role: "presentation",
|
|
250
|
+
children: section.heading
|
|
251
|
+
}), entries]
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
entry(entry) {
|
|
255
|
+
if ("divider" in entry) return /* @__PURE__ */ jsx("hr", { class: "divider" });
|
|
256
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
257
|
+
class: "item",
|
|
258
|
+
role: "menuitem",
|
|
259
|
+
tabIndex: -1,
|
|
260
|
+
"data-value": entry.value,
|
|
261
|
+
"data-danger": entry.danger === true ? "" : void 0,
|
|
262
|
+
"aria-disabled": entry.disabled === true ? "true" : void 0,
|
|
263
|
+
children: [
|
|
264
|
+
entry.icon === void 0 ? "" : /* @__PURE__ */ jsx(JlIcon, {
|
|
265
|
+
class: "icon",
|
|
266
|
+
name: entry.icon,
|
|
267
|
+
size: ICON_SIZE
|
|
268
|
+
}),
|
|
269
|
+
/* @__PURE__ */ jsxs("span", {
|
|
270
|
+
class: "text",
|
|
271
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
272
|
+
class: "label",
|
|
273
|
+
children: entry.label
|
|
274
|
+
}), entry.description === void 0 ? "" : /* @__PURE__ */ jsx("span", {
|
|
275
|
+
class: "description",
|
|
276
|
+
children: entry.description
|
|
277
|
+
})]
|
|
278
|
+
}),
|
|
279
|
+
entry.shortcut === void 0 ? "" : /* @__PURE__ */ jsx("kbd", {
|
|
280
|
+
class: "shortcut",
|
|
281
|
+
children: entry.shortcut
|
|
282
|
+
})
|
|
283
|
+
]
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
JlMenu.register();
|
|
288
|
+
export { JlMenu };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// src/molecules/jl-number-input.css?raw
|
|
2
|
+
var jl_number_input_default = ":host {\n display: block;\n}\n\n.control {\n padding: 0 8px;\n}\n\n.native {\n text-align: center;\n font-variant-numeric: tabular-nums;\n}\n\n.action {\n margin-right: 0;\n}\n\n.action:disabled {\n background: none;\n color: var(--jl-faint);\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n:host([size=\"sm\"]) .action {\n width: 22px;\n height: 22px;\n}\n\n:host([size=\"lg\"]) .action {\n width: 30px;\n height: 30px;\n}\n";
|
|
3
|
+
export { jl_number_input_default as default };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { JlFormControl } from '../base/jl-form-control';
|
|
2
|
+
import type { ControlSize } from '../types/options.types';
|
|
3
|
+
/**
|
|
4
|
+
* A number field with − and + buttons. The arrow keys step it (Page Up/Down by ten steps, Home and End to the
|
|
5
|
+
* bounds); what is typed is read and kept between `min` and `max` when the field is left. Empty is `null`.
|
|
6
|
+
*/
|
|
7
|
+
export declare class JlNumberInput extends JlFormControl {
|
|
8
|
+
static readonly selector = "jl-number-input";
|
|
9
|
+
readonly events: Readonly<import("@jadis/core").UseEventsHandler<{
|
|
10
|
+
change: number | null;
|
|
11
|
+
}>>;
|
|
12
|
+
readonly min: Readonly<Readonly<{
|
|
13
|
+
get(): number | null;
|
|
14
|
+
set(setter: number | ((prevState: number | null) => number | null) | null): void;
|
|
15
|
+
}>>;
|
|
16
|
+
readonly max: Readonly<Readonly<{
|
|
17
|
+
get(): number | null;
|
|
18
|
+
set(setter: number | ((prevState: number | null) => number | null) | null): void;
|
|
19
|
+
}>>;
|
|
20
|
+
readonly step: Readonly<Readonly<{
|
|
21
|
+
get(): number;
|
|
22
|
+
set(setter: number | ((prevState: number) => number)): void;
|
|
23
|
+
}>>;
|
|
24
|
+
/** The number, or `null` while the field is empty. */
|
|
25
|
+
readonly value: Readonly<Readonly<{
|
|
26
|
+
get(): number | null;
|
|
27
|
+
set(setter: number | ((prevState: number | null) => number | null) | null): void;
|
|
28
|
+
}>>;
|
|
29
|
+
readonly placeholder: Readonly<Readonly<{
|
|
30
|
+
get(): string;
|
|
31
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
32
|
+
}>>;
|
|
33
|
+
/** The field's name for screen readers, when no visible label names it. */
|
|
34
|
+
readonly label: Readonly<Readonly<{
|
|
35
|
+
get(): string;
|
|
36
|
+
set(setter: string | ((prevState: string) => string)): void;
|
|
37
|
+
}>>;
|
|
38
|
+
readonly size: Readonly<Readonly<{
|
|
39
|
+
get(): ControlSize;
|
|
40
|
+
set(setter: ((prevState: ControlSize) => ControlSize) | ControlSize): void;
|
|
41
|
+
}>>;
|
|
42
|
+
readonly required: Readonly<Readonly<{
|
|
43
|
+
get(): boolean;
|
|
44
|
+
set(setter: boolean | ((prevState: boolean) => boolean)): void;
|
|
45
|
+
}>>;
|
|
46
|
+
readonly disabled: Readonly<Readonly<{
|
|
47
|
+
get(): boolean;
|
|
48
|
+
set(setter: boolean | ((prevState: boolean) => boolean)): void;
|
|
49
|
+
}>>;
|
|
50
|
+
/** Draws the field in the danger colour, for an error the page found itself. */
|
|
51
|
+
readonly invalid: Readonly<Readonly<{
|
|
52
|
+
get(): boolean;
|
|
53
|
+
set(setter: boolean | ((prevState: boolean) => boolean)): void;
|
|
54
|
+
}>>;
|
|
55
|
+
private readonly _state;
|
|
56
|
+
private readonly _refs;
|
|
57
|
+
focus(options?: FocusOptions): void;
|
|
58
|
+
/** Adds one step (or `count` steps, negative to go down), within the bounds, and fires `change`. */
|
|
59
|
+
stepBy(count: number): void;
|
|
60
|
+
templateHtml(): Node;
|
|
61
|
+
templateCss(): string;
|
|
62
|
+
onConnect(): void;
|
|
63
|
+
protected resetValue(): void;
|
|
64
|
+
/** The browser calls it for a disabled fieldset and for the element's own disabled attribute, even before render. */
|
|
65
|
+
protected setDisabledByForm(disabled: boolean): void;
|
|
66
|
+
private onKey;
|
|
67
|
+
/** Home and End go to a bound when there is one, and otherwise move the caret as in any field. */
|
|
68
|
+
private goToBound;
|
|
69
|
+
/** The number typed so far, or the value when the text cannot be read. */
|
|
70
|
+
private typedNumber;
|
|
71
|
+
/** Reads what was typed: empty gives null, unreadable text goes back to the value, a number is kept in bounds. */
|
|
72
|
+
private commitText;
|
|
73
|
+
private commitNumber;
|
|
74
|
+
private commit;
|
|
75
|
+
private renderValue;
|
|
76
|
+
private renderBounds;
|
|
77
|
+
private renderDisabled;
|
|
78
|
+
/** A button that cannot move the value further is disabled. */
|
|
79
|
+
private renderButtons;
|
|
80
|
+
private renderValidity;
|
|
81
|
+
}
|