@lilydesignsystem/angular-search-picker 0.1.0 → 0.2.1
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/README.md +6 -6
- package/fesm2022/lilydesignsystem-angular-search-picker.mjs +48 -8
- package/fesm2022/lilydesignsystem-angular-search-picker.mjs.map +1 -1
- package/package.json +6 -6
- package/types/lilydesignsystem-angular-search-picker.d.ts +32 -17
- package/types/lilydesignsystem-angular-search-picker.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -42,12 +42,12 @@ That is the whole wiring: a search for `foo` performs a GET to `/?foo`.
|
|
|
42
42
|
|
|
43
43
|
The destination is `searchHref(query, action)`:
|
|
44
44
|
|
|
45
|
-
| You type
|
|
46
|
-
|
|
|
47
|
-
| `foo`
|
|
48
|
-
| `
|
|
49
|
-
| `a&b`
|
|
50
|
-
| `foo`
|
|
45
|
+
| You type | `action` | Destination |
|
|
46
|
+
| ---------- | ----------- | ------------- |
|
|
47
|
+
| `foo` | `"/"` | `/?foo` |
|
|
48
|
+
| ` foo bar` | `"/"` | `/?foo%20bar` |
|
|
49
|
+
| `a&b` | `"/"` | `/?a%26b` |
|
|
50
|
+
| `foo` | `"/search"` | `/search?foo` |
|
|
51
51
|
|
|
52
52
|
The query is trimmed and URI-encoded, so spaces and `&` cannot split or
|
|
53
53
|
corrupt it. An empty query goes nowhere.
|
|
@@ -18,7 +18,7 @@ var SearchPickerIcon = class SearchPickerIcon {
|
|
|
18
18
|
static {
|
|
19
19
|
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
20
20
|
minVersion: "12.0.0",
|
|
21
|
-
version: "22.2.
|
|
21
|
+
version: "22.2.4",
|
|
22
22
|
ngImport: i0,
|
|
23
23
|
type: SearchPickerIcon,
|
|
24
24
|
deps: [],
|
|
@@ -28,7 +28,7 @@ var SearchPickerIcon = class SearchPickerIcon {
|
|
|
28
28
|
static {
|
|
29
29
|
this.ɵdir = i0.ɵɵngDeclareDirective({
|
|
30
30
|
minVersion: "14.0.0",
|
|
31
|
-
version: "22.2.
|
|
31
|
+
version: "22.2.4",
|
|
32
32
|
type: SearchPickerIcon,
|
|
33
33
|
isStandalone: true,
|
|
34
34
|
selector: "ng-template[lilySearchPickerIcon]",
|
|
@@ -38,7 +38,7 @@ var SearchPickerIcon = class SearchPickerIcon {
|
|
|
38
38
|
};
|
|
39
39
|
i0.ɵɵngDeclareClassMetadata({
|
|
40
40
|
minVersion: "12.0.0",
|
|
41
|
-
version: "22.2.
|
|
41
|
+
version: "22.2.4",
|
|
42
42
|
ngImport: i0,
|
|
43
43
|
type: SearchPickerIcon,
|
|
44
44
|
decorators: [{
|
|
@@ -68,7 +68,11 @@ var SearchPicker = class SearchPicker {
|
|
|
68
68
|
this.baseId = nextSearchPickerId();
|
|
69
69
|
this.panelId = `${this.baseId}-panel`;
|
|
70
70
|
this.returnSymbol = "⏎";
|
|
71
|
-
this.open =
|
|
71
|
+
this.open = model(false, ...ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []);
|
|
72
|
+
this.disabled = input(false, ...ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []);
|
|
73
|
+
this.closeWhenDisabled = effect(() => {
|
|
74
|
+
if (this.disabled() && this.open()) this.open.set(false);
|
|
75
|
+
}, ...ngDevMode ? [{ debugName: "closeWhenDisabled" }] : /* istanbul ignore next */ []);
|
|
72
76
|
this.tooltipId = `${this.baseId}-tooltip`;
|
|
73
77
|
this.hoverButton = signal(false, ...ngDevMode ? [{ debugName: "hoverButton" }] : /* istanbul ignore next */ []);
|
|
74
78
|
this.hoverTooltip = signal(false, ...ngDevMode ? [{ debugName: "hoverTooltip" }] : /* istanbul ignore next */ []);
|
|
@@ -159,7 +163,7 @@ var SearchPicker = class SearchPicker {
|
|
|
159
163
|
static {
|
|
160
164
|
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
161
165
|
minVersion: "12.0.0",
|
|
162
|
-
version: "22.2.
|
|
166
|
+
version: "22.2.4",
|
|
163
167
|
ngImport: i0,
|
|
164
168
|
type: SearchPicker,
|
|
165
169
|
deps: [],
|
|
@@ -169,7 +173,7 @@ var SearchPicker = class SearchPicker {
|
|
|
169
173
|
static {
|
|
170
174
|
this.ɵcmp = i0.ɵɵngDeclareComponent({
|
|
171
175
|
minVersion: "17.0.0",
|
|
172
|
-
version: "22.2.
|
|
176
|
+
version: "22.2.4",
|
|
173
177
|
type: SearchPicker,
|
|
174
178
|
isStandalone: true,
|
|
175
179
|
selector: "lily-search-picker",
|
|
@@ -229,11 +233,26 @@ var SearchPicker = class SearchPicker {
|
|
|
229
233
|
isSignal: true,
|
|
230
234
|
isRequired: false,
|
|
231
235
|
transformFunction: null
|
|
236
|
+
},
|
|
237
|
+
open: {
|
|
238
|
+
classPropertyName: "open",
|
|
239
|
+
publicName: "open",
|
|
240
|
+
isSignal: true,
|
|
241
|
+
isRequired: false,
|
|
242
|
+
transformFunction: null
|
|
243
|
+
},
|
|
244
|
+
disabled: {
|
|
245
|
+
classPropertyName: "disabled",
|
|
246
|
+
publicName: "disabled",
|
|
247
|
+
isSignal: true,
|
|
248
|
+
isRequired: false,
|
|
249
|
+
transformFunction: null
|
|
232
250
|
}
|
|
233
251
|
},
|
|
234
252
|
outputs: {
|
|
235
253
|
value: "valueChange",
|
|
236
|
-
searched: "searched"
|
|
254
|
+
searched: "searched",
|
|
255
|
+
open: "openChange"
|
|
237
256
|
},
|
|
238
257
|
host: { listeners: { "document:click": "onDocumentClick($event)" } },
|
|
239
258
|
queries: [{
|
|
@@ -281,6 +300,7 @@ var SearchPicker = class SearchPicker {
|
|
|
281
300
|
(focusout)="onRootFocusOut($event)"
|
|
282
301
|
>
|
|
283
302
|
<lily-icon-button
|
|
303
|
+
[disabled]="disabled()"
|
|
284
304
|
#buttonEl
|
|
285
305
|
[label]="label()"
|
|
286
306
|
baseClass="search-picker-button"
|
|
@@ -401,7 +421,7 @@ var SearchPicker = class SearchPicker {
|
|
|
401
421
|
};
|
|
402
422
|
i0.ɵɵngDeclareClassMetadata({
|
|
403
423
|
minVersion: "12.0.0",
|
|
404
|
-
version: "22.2.
|
|
424
|
+
version: "22.2.4",
|
|
405
425
|
ngImport: i0,
|
|
406
426
|
type: SearchPicker,
|
|
407
427
|
decorators: [{
|
|
@@ -419,6 +439,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
419
439
|
(focusout)="onRootFocusOut($event)"
|
|
420
440
|
>
|
|
421
441
|
<lily-icon-button
|
|
442
|
+
[disabled]="disabled()"
|
|
422
443
|
#buttonEl
|
|
423
444
|
[label]="label()"
|
|
424
445
|
baseClass="search-picker-button"
|
|
@@ -601,6 +622,25 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
601
622
|
inputRef: [{
|
|
602
623
|
type: i0.ViewChild,
|
|
603
624
|
args: ["inputEl", { isSignal: true }]
|
|
625
|
+
}],
|
|
626
|
+
open: [{
|
|
627
|
+
type: i0.Input,
|
|
628
|
+
args: [{
|
|
629
|
+
isSignal: true,
|
|
630
|
+
alias: "open",
|
|
631
|
+
required: false
|
|
632
|
+
}]
|
|
633
|
+
}, {
|
|
634
|
+
type: i0.Output,
|
|
635
|
+
args: ["openChange"]
|
|
636
|
+
}],
|
|
637
|
+
disabled: [{
|
|
638
|
+
type: i0.Input,
|
|
639
|
+
args: [{
|
|
640
|
+
isSignal: true,
|
|
641
|
+
alias: "disabled",
|
|
642
|
+
required: false
|
|
643
|
+
}]
|
|
604
644
|
}]
|
|
605
645
|
}
|
|
606
646
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lilydesignsystem-angular-search-picker.mjs","names":[],"sources":["../../search-picker.component.ts"],"sourcesContent":["import { NgTemplateOutlet } from \"@angular/common\";\nimport {\n ChangeDetectionStrategy,\n Component,\n Directive,\n ElementRef,\n TemplateRef,\n computed,\n contentChild,\n effect,\n input,\n model,\n output,\n signal,\n viewChild,\n} from \"@angular/core\";\n// Only the trigger button composes a headless primitive. The panel is a\n// real <form role=\"search\"> with a real search field and a real submit\n// button — a disclosure, not a listbox or a menu — so headless `Listbox`\n// is the wrong widget for it, not merely an unmigrated one.\nimport { IconButton } from \"@lilydesignsystem/angular-headless\";\n\n/**\n * The submit button's visible content: U+23CE RETURN SYMBOL, a bare\n * literal character (never an escape — see `bin/test`'s glyph check).\n * It is the button's visible label only; the accessible name comes from\n * the required `submitLabel` input, so assistive technology never has to\n * announce a symbol.\n */\nexport const RETURN_SYMBOL = \"⏎\";\n\n/** Context passed to a custom icon `<ng-template>` (the button icon). */\nexport type ChildArgs = {\n /** Is the search panel open? */\n open: boolean;\n /** The current text in the search field. */\n query: string;\n};\n\n/** Payload of the `searched` output: the trimmed query and its destination. */\nexport type SearchEvent = {\n /** The trimmed, non-empty query. */\n query: string;\n /** Where the search navigates: `searchHref(query, action)`. */\n href: string;\n};\n\n/**\n * The destination for a query: `action` + `?` + the URI-encoded, trimmed\n * query. `searchHref(\"foo\")` is `\"/?foo\"`; `searchHref(\"foo bar\")` is\n * `\"/?foo%20bar\"`.\n */\nexport function searchHref(query: string, action = \"/\"): string {\n return `${action}?${encodeURIComponent(query.trim())}`;\n}\n\nlet uid = 0;\n/** Stable per-instance id prefix; SSR-safe (no Math.random / Date.now). */\nexport function nextSearchPickerId(): string {\n uid += 1;\n return `search-picker-${uid}`;\n}\n\n/**\n * Optional marker for the projected icon template. Gives consumers typed\n * `let-` variables:\n *\n * ```html\n * <lily-search-picker label=\"Search\" inputLabel=\"Search terms\" submitLabel=\"Search\">\n * <ng-template lilySearchPickerIcon let-args>{{ args.open ? \"×\" : \"🔍\" }}</ng-template>\n * </lily-search-picker>\n * ```\n *\n * The component queries any projected `<ng-template>`, so the marker is\n * for type-checking and readability, not for matching.\n */\n@Directive({\n selector: \"ng-template[lilySearchPickerIcon]\",\n standalone: true,\n})\nexport class SearchPickerIcon {\n static ngTemplateContextGuard(\n _dir: SearchPickerIcon,\n _ctx: unknown,\n ): _ctx is ChildArgs & { $implicit: ChildArgs } {\n return true;\n }\n}\n\n/**\n * SearchPicker — a headless site-search control.\n *\n * A single-icon button (a bundled magnifying-glass SVG) that opens a\n * dropdown holding a search field and, at its right, a `⏎` submit button.\n * Return in the field, or the `⏎` button, navigates to `/?<query>` — a\n * search for `foo` goes to `/?foo`.\n *\n * Like `share-picker` this owns an *action*, not a preference: it applies\n * nothing to the document and persists nothing. See `spec/index.md` for\n * the full contract.\n */\n@Component({\n selector: \"lily-search-picker\",\n standalone: true,\n imports: [NgTemplateOutlet, IconButton],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n \"(document:click)\": \"onDocumentClick($event)\",\n },\n template: `\n <div\n #rootEl\n class=\"search-picker {{ className() }}\"\n (focusout)=\"onRootFocusOut($event)\"\n >\n <lily-icon-button\n #buttonEl\n [label]=\"label()\"\n baseClass=\"search-picker-button\"\n [ariaExpanded]=\"open()\"\n [ariaControls]=\"panelId\"\n (click)=\"onButtonClick(); hoverButton.set(false)\"\n (mouseenter)=\"onTooltipButtonEnter()\"\n (mouseleave)=\"hoverButton.set(false)\"\n (focusin)=\"onTooltipButtonFocusIn()\"\n (focusout)=\"onTooltipButtonFocusOut()\"\n (keydown)=\"onTooltipKeydown($event)\"\n >\n @if (iconTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"childContext()\"\n />\n } @else {\n <svg\n class=\"search-picker-icon\"\n viewBox=\"0 0 16 16\"\n width=\"1.05rem\"\n height=\"1.05rem\"\n aria-hidden=\"true\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.6\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <circle cx=\"7\" cy=\"7\" r=\"4.5\" />\n <path d=\"M10.5 10.5 14 14\" />\n </svg>\n }\n </lily-icon-button>\n\n <!-- Purely visual: the same text is already the button's aria-label,\n so it is not wired with aria-describedby (that would announce\n the name twice). -->\n <div\n class=\"search-picker-tooltip\"\n role=\"tooltip\"\n [id]=\"tooltipId\"\n [attr.hidden]=\"tooltipVisible() ? null : ''\"\n (mouseenter)=\"hoverTooltip.set(true)\"\n (mouseleave)=\"hoverTooltip.set(false)\"\n >{{ label() }}</div>\n\n <!-- The keydown handler only listens for Escape bubbling up from the\n field and the submit button inside; the panel itself takes no\n focus. -->\n <div\n #panelEl\n class=\"search-picker-panel\"\n [id]=\"panelId\"\n [attr.hidden]=\"open() ? null : ''\"\n (keydown)=\"onPanelKeydown($event)\"\n >\n <form\n class=\"search-picker-form\"\n role=\"search\"\n [attr.aria-label]=\"label()\"\n [attr.action]=\"action()\"\n method=\"get\"\n (submit)=\"onSubmit($event)\"\n >\n <input\n #inputEl\n class=\"search-picker-input\"\n type=\"search\"\n [attr.aria-label]=\"inputLabel()\"\n [attr.placeholder]=\"placeholder() ?? null\"\n enterkeyhint=\"search\"\n [value]=\"value()\"\n (input)=\"value.set($any($event.target).value)\"\n />\n <button\n type=\"submit\"\n class=\"search-picker-submit\"\n [attr.aria-label]=\"submitLabel()\"\n >\n <span class=\"search-picker-submit-symbol\" aria-hidden=\"true\">{{\n returnSymbol\n }}</span>\n </button>\n </form>\n </div>\n </div>\n `,\n})\nexport class SearchPicker {\n /** Accessible name for the icon button and the search landmark. */\n readonly label = input.required<string>();\n /** Accessible name for the search text field. */\n readonly inputLabel = input.required<string>();\n /** Accessible name for the ⏎ submit button. */\n readonly submitLabel = input.required<string>();\n /** Placeholder text for the search field. No default. */\n readonly placeholder = input<string | undefined>(undefined);\n /** The search text. Two-way bindable: `[(value)]`. */\n readonly value = model<string>(\"\");\n /**\n * Path the query is appended to. The search for `foo` navigates to\n * `${action}?foo`; the default `\"/\"` gives `/?foo`.\n */\n readonly action = input<string>(\"/\");\n /**\n * Performs the navigation. Defaults to `location.assign(href)` — a real\n * GET request. Pass a client-side router's navigate function (e.g.\n * `(href) => router.navigateByUrl(href)`) to keep the navigation in-app.\n */\n readonly navigate = input<((href: string) => void) | undefined>(undefined);\n /** Extra CSS class on the root div. */\n readonly className = input<string>(\"\");\n\n /**\n * Fires with the trimmed query and the destination, before navigating.\n * Named `searched`, not `search`: `<input type=\"search\">` fires a\n * native, bubbling `search` DOM event in Chromium and WebKit, and\n * Angular binds `(search)` on a component host to BOTH the output and\n * any same-named DOM event — the handler would receive a raw `Event`\n * on every Return.\n */\n readonly searched = output<SearchEvent>();\n\n /** Projected icon template; replaces the default icon when supplied. */\n protected readonly iconTemplate = contentChild(TemplateRef);\n\n private readonly rootRef =\n viewChild.required<ElementRef<HTMLDivElement>>(\"rootEl\");\n // A template-ref-variable on a component tag resolves to the component\n // INSTANCE — exactly what's needed to call IconButton's own `focus()`.\n private readonly buttonRef = viewChild.required<IconButton>(\"buttonEl\");\n private readonly panelRef =\n viewChild.required<ElementRef<HTMLDivElement>>(\"panelEl\");\n private readonly inputRef =\n viewChild.required<ElementRef<HTMLInputElement>>(\"inputEl\");\n\n private readonly baseId = nextSearchPickerId();\n protected readonly panelId = `${this.baseId}-panel`;\n protected readonly returnSymbol = RETURN_SYMBOL;\n\n protected readonly open = signal(false);\n\n // Tooltip: shown while the pointer is over the button or the tooltip\n // itself (hoverable, WCAG 1.4.13) or while the button has keyboard\n // focus; Escape dismisses it without moving focus; never shown while\n // the popup is open, since the popup then explains the control.\n protected readonly tooltipId = `${this.baseId}-tooltip`;\n protected readonly hoverButton = signal(false);\n protected readonly hoverTooltip = signal(false);\n protected readonly focusButton = signal(false);\n protected readonly dismissed = signal(false);\n protected readonly tooltipVisible = computed(\n () =>\n !this.open() &&\n !this.dismissed() &&\n (this.hoverButton() || this.hoverTooltip() || this.focusButton()),\n );\n\n protected onTooltipButtonEnter(): void {\n this.hoverButton.set(true);\n this.dismissed.set(false);\n }\n\n protected onTooltipButtonFocusIn(): void {\n // Keyboard focus only: a mouse click also focuses the button in\n // Chromium, and the tooltip should not stick after a click.\n try {\n this.focusButton.set(\n this.buttonRef().element?.matches(\":focus-visible\") ?? false,\n );\n } catch {\n this.focusButton.set(true); // engine without :focus-visible — err towards showing\n }\n }\n\n protected onTooltipButtonFocusOut(): void {\n this.focusButton.set(false);\n this.dismissed.set(false);\n }\n\n protected onTooltipKeydown(event: KeyboardEvent): void {\n if (event.key === \"Escape\" && this.tooltipVisible()) this.dismissed.set(true);\n }\n\n // WCAG 1.4.13 \"dismissable\": while the tooltip is visible, Escape works\n // wherever focus is (hover-only tooltips have focus elsewhere). The\n // listener exists only while visible; the effect cleanup removes it on\n // hide and on destroy. No preventDefault/stopPropagation, focus untouched.\n private readonly tooltipEscapeListener = effect((onCleanup) => {\n if (!this.tooltipVisible() || typeof document === \"undefined\") return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") this.dismissed.set(true);\n };\n document.addEventListener(\"keydown\", onKey);\n onCleanup(() => document.removeEventListener(\"keydown\", onKey));\n });\n\n protected readonly childContext = computed(() => {\n const args: ChildArgs = { open: this.open(), query: this.value() };\n return { $implicit: args, ...args };\n });\n\n // ---------------------------------------------------------------\n // Open / close\n // ---------------------------------------------------------------\n\n /** Open the panel and move focus into the search field. */\n openPanel(): void {\n this.open.set(true);\n // Unhide the panel now rather than waiting for change detection, so\n // the field is focusable in this same task — whether the host app is\n // zone-based or zoneless, the template binding settles on the same\n // value afterwards. preventScroll: the panel is positioned by\n // consumer CSS, and focusing a field rendered partly off-screen would\n // otherwise scroll the whole page.\n this.panelRef().nativeElement.removeAttribute(\"hidden\");\n this.inputRef().nativeElement.focus({ preventScroll: true });\n }\n\n /** Close the panel; `refocus` returns focus to the trigger. */\n closePanel(refocus = true): void {\n if (!this.open()) return;\n // Focus the trigger BEFORE hiding the panel, so focus never drops to\n // <body> in between.\n if (refocus) this.buttonRef().focus({ preventScroll: true });\n this.open.set(false);\n }\n\n protected onButtonClick(): void {\n if (this.open()) this.closePanel();\n else this.openPanel();\n }\n\n protected onPanelKeydown(event: KeyboardEvent): void {\n if (event.key === \"Escape\") {\n event.preventDefault();\n this.closePanel();\n }\n }\n\n protected onRootFocusOut(event: FocusEvent): void {\n // Close only when focus moves to a known element outside the picker.\n // A focusout with no relatedTarget is not \"focus left\": Safari does\n // not focus a <button> on click, so pressing ⏎ (or the icon button)\n // blurs the field with relatedTarget = null. Closing there hid the\n // panel before the click landed, so ⏎ never searched and the icon\n // button re-opened instead of closing. Clicks outside the picker are\n // handled by the document click listener below.\n const next = event.relatedTarget as Node | null;\n if (!next || this.rootRef().nativeElement.contains(next)) return;\n this.closePanel(false);\n }\n\n protected onDocumentClick(event: Event): void {\n if (!this.open()) return;\n const t = event.target as Node | null;\n if (t && !this.rootRef().nativeElement.contains(t)) this.closePanel(false);\n }\n\n // ---------------------------------------------------------------\n // Searching\n // ---------------------------------------------------------------\n\n protected onSubmit(event: Event): void {\n // The form's native GET would send `/?name=value`; the contract is\n // the bare query (`/?foo`), so navigation is done here instead.\n event.preventDefault();\n const query = this.value().trim();\n if (!query) return;\n const href = searchHref(query, this.action());\n this.searched.emit({ query, href });\n this.closePanel(false);\n const navigate = this.navigate();\n if (navigate) navigate(href);\n else if (typeof location !== \"undefined\") location.assign(href);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lilydesignsystem-angular-search-picker.mjs","names":[],"sources":["../../search-picker.component.ts"],"sourcesContent":["import { NgTemplateOutlet } from \"@angular/common\";\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n contentChild,\n Directive,\n effect,\n ElementRef,\n input,\n model,\n output,\n signal,\n TemplateRef,\n viewChild,\n} from \"@angular/core\";\n// Only the trigger button composes a headless primitive. The panel is a\n// real <form role=\"search\"> with a real search field and a real submit\n// button — a disclosure, not a listbox or a menu — so headless `Listbox`\n// is the wrong widget for it, not merely an unmigrated one.\nimport { IconButton } from \"@lilydesignsystem/angular-headless\";\n\n/**\n * The submit button's visible content: U+23CE RETURN SYMBOL, a bare\n * literal character (never an escape — see `bin/test`'s glyph check).\n * It is the button's visible label only; the accessible name comes from\n * the required `submitLabel` input, so assistive technology never has to\n * announce a symbol.\n */\nexport const RETURN_SYMBOL = \"⏎\";\n\n/** Context passed to a custom icon `<ng-template>` (the button icon). */\nexport type ChildArgs = {\n /** Is the search panel open? */\n open: boolean;\n /** The current text in the search field. */\n query: string;\n};\n\n/** Payload of the `searched` output: the trimmed query and its destination. */\nexport type SearchEvent = {\n /** The trimmed, non-empty query. */\n query: string;\n /** Where the search navigates: `searchHref(query, action)`. */\n href: string;\n};\n\n/**\n * The destination for a query: `action` + `?` + the URI-encoded, trimmed\n * query. `searchHref(\"foo\")` is `\"/?foo\"`; `searchHref(\"foo bar\")` is\n * `\"/?foo%20bar\"`.\n */\nexport function searchHref(query: string, action = \"/\"): string {\n return `${action}?${encodeURIComponent(query.trim())}`;\n}\n\nlet uid = 0;\n/** Stable per-instance id prefix; SSR-safe (no Math.random / Date.now). */\nexport function nextSearchPickerId(): string {\n uid += 1;\n return `search-picker-${uid}`;\n}\n\n/**\n * Optional marker for the projected icon template. Gives consumers typed\n * `let-` variables:\n *\n * ```html\n * <lily-search-picker label=\"Search\" inputLabel=\"Search terms\" submitLabel=\"Search\">\n * <ng-template lilySearchPickerIcon let-args>{{ args.open ? \"×\" : \"🔍\" }}</ng-template>\n * </lily-search-picker>\n * ```\n *\n * The component queries any projected `<ng-template>`, so the marker is\n * for type-checking and readability, not for matching.\n */\n@Directive({\n selector: \"ng-template[lilySearchPickerIcon]\",\n standalone: true,\n})\nexport class SearchPickerIcon {\n static ngTemplateContextGuard(\n _dir: SearchPickerIcon,\n _ctx: unknown,\n ): _ctx is ChildArgs & { $implicit: ChildArgs } {\n return true;\n }\n}\n\n/**\n * SearchPicker — a headless site-search control.\n *\n * A single-icon button (a bundled magnifying-glass SVG) that opens a\n * dropdown holding a search field and, at its right, a `⏎` submit button.\n * Return in the field, or the `⏎` button, navigates to `/?<query>` — a\n * search for `foo` goes to `/?foo`.\n *\n * Like `share-picker` this owns an *action*, not a preference: it applies\n * nothing to the document and persists nothing. See `spec/index.md` for\n * the full contract.\n */\n@Component({\n selector: \"lily-search-picker\",\n standalone: true,\n imports: [NgTemplateOutlet, IconButton],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n \"(document:click)\": \"onDocumentClick($event)\",\n },\n template: `\n <div\n #rootEl\n class=\"search-picker {{ className() }}\"\n (focusout)=\"onRootFocusOut($event)\"\n >\n <lily-icon-button\n [disabled]=\"disabled()\"\n #buttonEl\n [label]=\"label()\"\n baseClass=\"search-picker-button\"\n [ariaExpanded]=\"open()\"\n [ariaControls]=\"panelId\"\n (click)=\"onButtonClick(); hoverButton.set(false)\"\n (mouseenter)=\"onTooltipButtonEnter()\"\n (mouseleave)=\"hoverButton.set(false)\"\n (focusin)=\"onTooltipButtonFocusIn()\"\n (focusout)=\"onTooltipButtonFocusOut()\"\n (keydown)=\"onTooltipKeydown($event)\"\n >\n @if (iconTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"childContext()\"\n />\n } @else {\n <svg\n class=\"search-picker-icon\"\n viewBox=\"0 0 16 16\"\n width=\"1.05rem\"\n height=\"1.05rem\"\n aria-hidden=\"true\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.6\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <circle cx=\"7\" cy=\"7\" r=\"4.5\" />\n <path d=\"M10.5 10.5 14 14\" />\n </svg>\n }\n </lily-icon-button>\n\n <!-- Purely visual: the same text is already the button's aria-label,\n so it is not wired with aria-describedby (that would announce\n the name twice). -->\n <div\n class=\"search-picker-tooltip\"\n role=\"tooltip\"\n [id]=\"tooltipId\"\n [attr.hidden]=\"tooltipVisible() ? null : ''\"\n (mouseenter)=\"hoverTooltip.set(true)\"\n (mouseleave)=\"hoverTooltip.set(false)\"\n >{{ label() }}</div>\n\n <!-- The keydown handler only listens for Escape bubbling up from the\n field and the submit button inside; the panel itself takes no\n focus. -->\n <div\n #panelEl\n class=\"search-picker-panel\"\n [id]=\"panelId\"\n [attr.hidden]=\"open() ? null : ''\"\n (keydown)=\"onPanelKeydown($event)\"\n >\n <form\n class=\"search-picker-form\"\n role=\"search\"\n [attr.aria-label]=\"label()\"\n [attr.action]=\"action()\"\n method=\"get\"\n (submit)=\"onSubmit($event)\"\n >\n <input\n #inputEl\n class=\"search-picker-input\"\n type=\"search\"\n [attr.aria-label]=\"inputLabel()\"\n [attr.placeholder]=\"placeholder() ?? null\"\n enterkeyhint=\"search\"\n [value]=\"value()\"\n (input)=\"value.set($any($event.target).value)\"\n />\n <button\n type=\"submit\"\n class=\"search-picker-submit\"\n [attr.aria-label]=\"submitLabel()\"\n >\n <span class=\"search-picker-submit-symbol\" aria-hidden=\"true\">{{\n returnSymbol\n }}</span>\n </button>\n </form>\n </div>\n </div>\n `,\n})\nexport class SearchPicker {\n /** Accessible name for the icon button and the search landmark. */\n readonly label = input.required<string>();\n /** Accessible name for the search text field. */\n readonly inputLabel = input.required<string>();\n /** Accessible name for the ⏎ submit button. */\n readonly submitLabel = input.required<string>();\n /** Placeholder text for the search field. No default. */\n readonly placeholder = input<string | undefined>(undefined);\n /** The search text. Two-way bindable: `[(value)]`. */\n readonly value = model<string>(\"\");\n /**\n * Path the query is appended to. The search for `foo` navigates to\n * `${action}?foo`; the default `\"/\"` gives `/?foo`.\n */\n readonly action = input<string>(\"/\");\n /**\n * Performs the navigation. Defaults to `location.assign(href)` — a real\n * GET request. Pass a client-side router's navigate function (e.g.\n * `(href) => router.navigateByUrl(href)`) to keep the navigation in-app.\n */\n readonly navigate = input<((href: string) => void) | undefined>(undefined);\n /** Extra CSS class on the root div. */\n readonly className = input<string>(\"\");\n\n /**\n * Fires with the trimmed query and the destination, before navigating.\n * Named `searched`, not `search`: `<input type=\"search\">` fires a\n * native, bubbling `search` DOM event in Chromium and WebKit, and\n * Angular binds `(search)` on a component host to BOTH the output and\n * any same-named DOM event — the handler would receive a raw `Event`\n * on every Return.\n */\n readonly searched = output<SearchEvent>();\n\n /** Projected icon template; replaces the default icon when supplied. */\n protected readonly iconTemplate = contentChild(TemplateRef);\n\n private readonly rootRef = viewChild.required<ElementRef<HTMLDivElement>>(\"rootEl\");\n // A template-ref-variable on a component tag resolves to the component\n // INSTANCE — exactly what's needed to call IconButton's own `focus()`.\n private readonly buttonRef = viewChild.required<IconButton>(\"buttonEl\");\n private readonly panelRef = viewChild.required<ElementRef<HTMLDivElement>>(\"panelEl\");\n private readonly inputRef = viewChild.required<ElementRef<HTMLInputElement>>(\"inputEl\");\n\n private readonly baseId = nextSearchPickerId();\n protected readonly panelId = `${this.baseId}-panel`;\n protected readonly returnSymbol = RETURN_SYMBOL;\n\n /** Is the popup open? Two-way bindable: `[(open)]`; `openChange` fires for each change. */\n readonly open = model<boolean>(false);\n /** Disables the button; an open popup closes. */\n readonly disabled = input<boolean>(false);\n private readonly closeWhenDisabled = effect(() => {\n if (this.disabled() && this.open()) this.open.set(false);\n });\n\n // Tooltip: shown while the pointer is over the button or the tooltip\n // itself (hoverable, WCAG 1.4.13) or while the button has keyboard\n // focus; Escape dismisses it without moving focus; never shown while\n // the popup is open, since the popup then explains the control.\n protected readonly tooltipId = `${this.baseId}-tooltip`;\n protected readonly hoverButton = signal(false);\n protected readonly hoverTooltip = signal(false);\n protected readonly focusButton = signal(false);\n protected readonly dismissed = signal(false);\n protected readonly tooltipVisible = computed(\n () =>\n !this.open() &&\n !this.dismissed() &&\n (this.hoverButton() || this.hoverTooltip() || this.focusButton()),\n );\n\n protected onTooltipButtonEnter(): void {\n this.hoverButton.set(true);\n this.dismissed.set(false);\n }\n\n protected onTooltipButtonFocusIn(): void {\n // Keyboard focus only: a mouse click also focuses the button in\n // Chromium, and the tooltip should not stick after a click.\n try {\n this.focusButton.set(this.buttonRef().element?.matches(\":focus-visible\") ?? false);\n } catch {\n this.focusButton.set(true); // engine without :focus-visible — err towards showing\n }\n }\n\n protected onTooltipButtonFocusOut(): void {\n this.focusButton.set(false);\n this.dismissed.set(false);\n }\n\n protected onTooltipKeydown(event: KeyboardEvent): void {\n if (event.key === \"Escape\" && this.tooltipVisible()) this.dismissed.set(true);\n }\n\n // WCAG 1.4.13 \"dismissable\": while the tooltip is visible, Escape works\n // wherever focus is (hover-only tooltips have focus elsewhere). The\n // listener exists only while visible; the effect cleanup removes it on\n // hide and on destroy. No preventDefault/stopPropagation, focus untouched.\n private readonly tooltipEscapeListener = effect((onCleanup) => {\n if (!this.tooltipVisible() || typeof document === \"undefined\") return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") this.dismissed.set(true);\n };\n document.addEventListener(\"keydown\", onKey);\n onCleanup(() => document.removeEventListener(\"keydown\", onKey));\n });\n\n protected readonly childContext = computed(() => {\n const args: ChildArgs = { open: this.open(), query: this.value() };\n return { $implicit: args, ...args };\n });\n\n // ---------------------------------------------------------------\n // Open / close\n // ---------------------------------------------------------------\n\n /** Open the panel and move focus into the search field. */\n openPanel(): void {\n this.open.set(true);\n // Unhide the panel now rather than waiting for change detection, so\n // the field is focusable in this same task — whether the host app is\n // zone-based or zoneless, the template binding settles on the same\n // value afterwards. preventScroll: the panel is positioned by\n // consumer CSS, and focusing a field rendered partly off-screen would\n // otherwise scroll the whole page.\n this.panelRef().nativeElement.removeAttribute(\"hidden\");\n this.inputRef().nativeElement.focus({ preventScroll: true });\n }\n\n /** Close the panel; `refocus` returns focus to the trigger. */\n closePanel(refocus = true): void {\n if (!this.open()) return;\n // Focus the trigger BEFORE hiding the panel, so focus never drops to\n // <body> in between.\n if (refocus) this.buttonRef().focus({ preventScroll: true });\n this.open.set(false);\n }\n\n protected onButtonClick(): void {\n if (this.open()) this.closePanel();\n else this.openPanel();\n }\n\n protected onPanelKeydown(event: KeyboardEvent): void {\n if (event.key === \"Escape\") {\n event.preventDefault();\n this.closePanel();\n }\n }\n\n protected onRootFocusOut(event: FocusEvent): void {\n // Close only when focus moves to a known element outside the picker.\n // A focusout with no relatedTarget is not \"focus left\": Safari does\n // not focus a <button> on click, so pressing ⏎ (or the icon button)\n // blurs the field with relatedTarget = null. Closing there hid the\n // panel before the click landed, so ⏎ never searched and the icon\n // button re-opened instead of closing. Clicks outside the picker are\n // handled by the document click listener below.\n const next = event.relatedTarget as Node | null;\n if (!next || this.rootRef().nativeElement.contains(next)) return;\n this.closePanel(false);\n }\n\n protected onDocumentClick(event: Event): void {\n if (!this.open()) return;\n const t = event.target as Node | null;\n if (t && !this.rootRef().nativeElement.contains(t)) this.closePanel(false);\n }\n\n // ---------------------------------------------------------------\n // Searching\n // ---------------------------------------------------------------\n\n protected onSubmit(event: Event): void {\n // The form's native GET would send `/?name=value`; the contract is\n // the bare query (`/?foo`), so navigation is done here instead.\n event.preventDefault();\n const query = this.value().trim();\n if (!query) return;\n const href = searchHref(query, this.action());\n this.searched.emit({ query, href });\n this.closePanel(false);\n const navigate = this.navigate();\n if (navigate) navigate(href);\n else if (typeof location !== \"undefined\") location.assign(href);\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lilydesignsystem/angular-search-picker",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"engines": {
|
|
5
|
-
"node": "
|
|
5
|
+
"node": ">=26"
|
|
6
6
|
},
|
|
7
7
|
"description": "Lily Design System Angular 20 search picker: a magnifying-glass icon button opening a search field with a ⏎ submit button that navigates to /?<query>. Headless, SSR-safe, no CSS.",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@lilydesignsystem/angular-headless": "^0.
|
|
9
|
+
"@lilydesignsystem/angular-headless": "^0.8.0",
|
|
10
10
|
"tslib": "^2.3.0"
|
|
11
11
|
},
|
|
12
12
|
"peerDependencies": {
|
|
13
|
-
"@angular/
|
|
14
|
-
"@angular/
|
|
13
|
+
"@angular/common": ">=20.0.0 <23.0.0",
|
|
14
|
+
"@angular/core": ">=20.0.0 <23.0.0"
|
|
15
15
|
},
|
|
16
16
|
"sideEffects": false,
|
|
17
17
|
"keywords": [
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"license": "MIT OR Apache-2.0 OR GPL-2.0-only OR GPL-3.0-only OR BSD-3-Clause",
|
|
27
27
|
"repository": {
|
|
28
28
|
"type": "git",
|
|
29
|
-
"url": "git+https://github.com/LilyDesignSystem/lily-design-system
|
|
29
|
+
"url": "git+https://github.com/LilyDesignSystem/lily-design-system.git",
|
|
30
30
|
"directory": "lily-design-system-angular-search-picker"
|
|
31
31
|
},
|
|
32
32
|
"homepage": "https://lilydesignsystem.com/",
|
|
@@ -64,28 +64,28 @@ export declare class SearchPickerIcon {
|
|
|
64
64
|
*/
|
|
65
65
|
export declare class SearchPicker {
|
|
66
66
|
/** Accessible name for the icon button and the search landmark. */
|
|
67
|
-
readonly label: import("@angular/core").InputSignal<string>;
|
|
67
|
+
readonly label: import("@angular/core/types/core").InputSignal<string>;
|
|
68
68
|
/** Accessible name for the search text field. */
|
|
69
|
-
readonly inputLabel: import("@angular/core").InputSignal<string>;
|
|
69
|
+
readonly inputLabel: import("@angular/core/types/core").InputSignal<string>;
|
|
70
70
|
/** Accessible name for the ⏎ submit button. */
|
|
71
|
-
readonly submitLabel: import("@angular/core").InputSignal<string>;
|
|
71
|
+
readonly submitLabel: import("@angular/core/types/core").InputSignal<string>;
|
|
72
72
|
/** Placeholder text for the search field. No default. */
|
|
73
|
-
readonly placeholder: import("@angular/core").InputSignal<string | undefined>;
|
|
73
|
+
readonly placeholder: import("@angular/core/types/core").InputSignal<string | undefined>;
|
|
74
74
|
/** The search text. Two-way bindable: `[(value)]`. */
|
|
75
|
-
readonly value: import("@angular/core").ModelSignal<string>;
|
|
75
|
+
readonly value: import("@angular/core/types/core").ModelSignal<string>;
|
|
76
76
|
/**
|
|
77
77
|
* Path the query is appended to. The search for `foo` navigates to
|
|
78
78
|
* `${action}?foo`; the default `"/"` gives `/?foo`.
|
|
79
79
|
*/
|
|
80
|
-
readonly action: import("@angular/core").InputSignal<string>;
|
|
80
|
+
readonly action: import("@angular/core/types/core").InputSignal<string>;
|
|
81
81
|
/**
|
|
82
82
|
* Performs the navigation. Defaults to `location.assign(href)` — a real
|
|
83
83
|
* GET request. Pass a client-side router's navigate function (e.g.
|
|
84
84
|
* `(href) => router.navigateByUrl(href)`) to keep the navigation in-app.
|
|
85
85
|
*/
|
|
86
|
-
readonly navigate: import("@angular/core").InputSignal<((href: string) => void) | undefined>;
|
|
86
|
+
readonly navigate: import("@angular/core/types/core").InputSignal<((href: string) => void) | undefined>;
|
|
87
87
|
/** Extra CSS class on the root div. */
|
|
88
|
-
readonly className: import("@angular/core").InputSignal<string>;
|
|
88
|
+
readonly className: import("@angular/core/types/core").InputSignal<string>;
|
|
89
89
|
/**
|
|
90
90
|
* Fires with the trimmed query and the destination, before navigating.
|
|
91
91
|
* Named `searched`, not `search`: `<input type="search">` fires a
|
|
@@ -94,9 +94,9 @@ export declare class SearchPicker {
|
|
|
94
94
|
* any same-named DOM event — the handler would receive a raw `Event`
|
|
95
95
|
* on every Return.
|
|
96
96
|
*/
|
|
97
|
-
readonly searched: import("@angular/core").OutputEmitterRef<SearchEvent>;
|
|
97
|
+
readonly searched: import("@angular/core/types/_api-chunk").OutputEmitterRef<SearchEvent>;
|
|
98
98
|
/** Projected icon template; replaces the default icon when supplied. */
|
|
99
|
-
protected readonly iconTemplate: import("@angular/core").Signal<TemplateRef<any> | undefined>;
|
|
99
|
+
protected readonly iconTemplate: import("@angular/core/types/_chrome_dev_tools_performance-chunk").Signal<TemplateRef<any> | undefined>;
|
|
100
100
|
private readonly rootRef;
|
|
101
101
|
private readonly buttonRef;
|
|
102
102
|
private readonly panelRef;
|
|
@@ -104,19 +104,23 @@ export declare class SearchPicker {
|
|
|
104
104
|
private readonly baseId;
|
|
105
105
|
protected readonly panelId: string;
|
|
106
106
|
protected readonly returnSymbol = "⏎";
|
|
107
|
-
|
|
107
|
+
/** Is the popup open? Two-way bindable: `[(open)]`; `openChange` fires for each change. */
|
|
108
|
+
readonly open: import("@angular/core/types/core").ModelSignal<boolean>;
|
|
109
|
+
/** Disables the button; an open popup closes. */
|
|
110
|
+
readonly disabled: import("@angular/core/types/core").InputSignal<boolean>;
|
|
111
|
+
private readonly closeWhenDisabled;
|
|
108
112
|
protected readonly tooltipId: string;
|
|
109
|
-
protected readonly hoverButton: import("@angular/core").WritableSignal<boolean>;
|
|
110
|
-
protected readonly hoverTooltip: import("@angular/core").WritableSignal<boolean>;
|
|
111
|
-
protected readonly focusButton: import("@angular/core").WritableSignal<boolean>;
|
|
112
|
-
protected readonly dismissed: import("@angular/core").WritableSignal<boolean>;
|
|
113
|
-
protected readonly tooltipVisible: import("@angular/core").Signal<boolean>;
|
|
113
|
+
protected readonly hoverButton: import("@angular/core/types/_chrome_dev_tools_performance-chunk").WritableSignal<boolean>;
|
|
114
|
+
protected readonly hoverTooltip: import("@angular/core/types/_chrome_dev_tools_performance-chunk").WritableSignal<boolean>;
|
|
115
|
+
protected readonly focusButton: import("@angular/core/types/_chrome_dev_tools_performance-chunk").WritableSignal<boolean>;
|
|
116
|
+
protected readonly dismissed: import("@angular/core/types/_chrome_dev_tools_performance-chunk").WritableSignal<boolean>;
|
|
117
|
+
protected readonly tooltipVisible: import("@angular/core/types/_chrome_dev_tools_performance-chunk").Signal<boolean>;
|
|
114
118
|
protected onTooltipButtonEnter(): void;
|
|
115
119
|
protected onTooltipButtonFocusIn(): void;
|
|
116
120
|
protected onTooltipButtonFocusOut(): void;
|
|
117
121
|
protected onTooltipKeydown(event: KeyboardEvent): void;
|
|
118
122
|
private readonly tooltipEscapeListener;
|
|
119
|
-
protected readonly childContext: import("@angular/core").Signal<{
|
|
123
|
+
protected readonly childContext: import("@angular/core/types/_chrome_dev_tools_performance-chunk").Signal<{
|
|
120
124
|
/** Is the search panel open? */
|
|
121
125
|
open: boolean;
|
|
122
126
|
/** The current text in the search field. */
|
|
@@ -174,9 +178,20 @@ export declare class SearchPicker {
|
|
|
174
178
|
"required": false;
|
|
175
179
|
"isSignal": true;
|
|
176
180
|
};
|
|
181
|
+
"open": {
|
|
182
|
+
"alias": "open";
|
|
183
|
+
"required": false;
|
|
184
|
+
"isSignal": true;
|
|
185
|
+
};
|
|
186
|
+
"disabled": {
|
|
187
|
+
"alias": "disabled";
|
|
188
|
+
"required": false;
|
|
189
|
+
"isSignal": true;
|
|
190
|
+
};
|
|
177
191
|
}, {
|
|
178
192
|
"value": "valueChange";
|
|
179
193
|
"searched": "searched";
|
|
194
|
+
"open": "openChange";
|
|
180
195
|
}, ["iconTemplate"], never, true, never>;
|
|
181
196
|
}
|
|
182
197
|
export type { ChildArgs, SearchEvent };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lilydesignsystem-angular-search-picker.d.ts","names":[],"sources":["../../search-picker.component.ts"],"mappings":";;;;;;;;;qBA6Ba;;KAGD;;
|
|
1
|
+
{"version":3,"file":"lilydesignsystem-angular-search-picker.d.ts","names":[],"sources":["../../search-picker.component.ts"],"mappings":";;;;;;;;;qBA6Ba;;KAGD;;EAER;;EAEA;;;KAIQ;;EAER;;EAEA;;;;;;;wBAQY,WAAW,eAAe;;wBAM1B;;;;;;;;;;;;;;qBAsBH;SACF,uBACH,MAAM,kBACN,gBACD,QAAQ;IAAc,WAAW;;SAJ3B,MAAA,GAAA,qBAAA;SAAA,MAAA,GAAA,uBAAA;;;;;;;;;;;;;;qBA+HA;;WAEA,0CAAK;;WAEL,+CAAU;;WAEV,gDAAW;;WAEX,gDAAW;;WAEX,0CAAK;;;;;WAKL,2CAAM;;;;;;WAMN,6CAAQ,cAAA;;WAER,8CAAS;;;;;;;;;WAUT,mDAAQ,iBAAA;;qBAGE,gFAAY,OAAA;mBAEd;mBAGA;mBACA;mBACA;mBAEA;qBACE;qBACA;;WAGV,yCAAI;;WAEJ,6CAAQ;mBACA;qBAQE;qBACA,+EAAW;qBACX,gFAAY;qBACZ,+EAAW;qBACX,6EAAS;qBACT,kFAAc;YAOvB;YAKA;YAUA;YAKA,iBAAiB,OAAO;mBAQjB;qBASE,gFAAY;;IA3RzB;;IAEC;;;;EAmSP;;EAaA,WAAW;YAQD;YAKA,eAAe,OAAO;YAOtB,eAAe,OAAO;YAatB,gBAAgB,OAAO;YAUvB,SAAS,OAAO;SAhLjB,MAAA,GAAA,qBAAA;SAAA,MAAA,GAAA,uBAAA;IAAY;MAAA;MAAA;MAAA;;IAAA;MAAA;MAAA;MAAA;;IAAA;MAAA;MAAA;MAAA;;IAAA;MAAA;MAAA;MAAA;;IAAA;MAAA;MAAA;MAAA;;IAAA;MAAA;MAAA;MAAA;;IAAA;MAAA;MAAA;MAAA;;IAAA;MAAA;MAAA;MAAA;;IAAA;MAAA;MAAA;MAAA;;IAAA;MAAA;MAAA;MAAA;;;IAAA;IAAA;IAAA"}
|