@zeyos/zx 2.1.0 → 2.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/CHANGELOG.md +51 -0
- package/README.md +15 -0
- package/dist/types/compat/base.d.ts +156 -0
- package/dist/types/compat/globals.d.ts +20 -0
- package/dist/types/compat/index.d.ts +128 -0
- package/dist/types/compat/map/basic.d.ts +175 -0
- package/dist/types/compat/map/data.d.ts +102 -0
- package/dist/types/compat/map/date-time.d.ts +80 -0
- package/dist/types/compat/map/factory.d.ts +9 -0
- package/dist/types/compat/map/form.d.ts +98 -0
- package/dist/types/compat/map/helpers.d.ts +12 -0
- package/dist/types/compat/map/http.d.ts +36 -0
- package/dist/types/compat/map/layout.d.ts +106 -0
- package/dist/types/compat/map/options.d.ts +80 -0
- package/dist/types/compat/map/select.d.ts +95 -0
- package/dist/types/compat/map/stubs.d.ts +32 -0
- package/dist/types/compat/map/utilities.d.ts +37 -0
- package/dist/types/compat-entry.d.ts +5 -0
- package/dist/types/components/badge/badge.d.ts +46 -0
- package/dist/types/components/breadcrumb/breadcrumb.d.ts +200 -0
- package/dist/types/components/button/button.d.ts +42 -0
- package/dist/types/components/check-button/check-button.d.ts +98 -0
- package/dist/types/components/checklist/checklist.d.ts +143 -0
- package/dist/types/components/checklist/field-adapter.d.ts +5 -0
- package/dist/types/components/context-menu/context-menu.d.ts +130 -0
- package/dist/types/components/copy/copy-button.d.ts +48 -0
- package/dist/types/components/copy/copy-input.d.ts +91 -0
- package/dist/types/components/data-filter/data-filter.d.ts +250 -0
- package/dist/types/components/data-filter/filter-core.d.ts +52 -0
- package/dist/types/components/date-picker/date-picker.d.ts +137 -0
- package/dist/types/components/date-picker/date-range-picker.d.ts +350 -0
- package/dist/types/components/date-picker/field-adapter.d.ts +6 -0
- package/dist/types/components/date-picker/month-picker.d.ts +75 -0
- package/dist/types/components/date-picker/range-field-adapter.d.ts +7 -0
- package/dist/types/components/date-picker/time-picker.d.ts +127 -0
- package/dist/types/components/datebox/date-range-box.d.ts +218 -0
- package/dist/types/components/datebox/datebox.d.ts +160 -0
- package/dist/types/components/datebox/field-adapter.d.ts +5 -0
- package/dist/types/components/dialog/dialog.d.ts +284 -0
- package/dist/types/components/dropdown/dropdown.d.ts +118 -0
- package/dist/types/components/empty-state/empty-state.d.ts +38 -0
- package/dist/types/components/field/field.d.ts +231 -0
- package/dist/types/components/field-upload/field-adapter.d.ts +5 -0
- package/dist/types/components/field-upload/field-upload.d.ts +213 -0
- package/dist/types/components/fieldset/fieldset.d.ts +125 -0
- package/dist/types/components/finder/finder.d.ts +252 -0
- package/dist/types/components/form/form.d.ts +194 -0
- package/dist/types/components/groupbox/groupbox.d.ts +89 -0
- package/dist/types/components/layout/layout.d.ts +132 -0
- package/dist/types/components/loading/inline-loading.d.ts +86 -0
- package/dist/types/components/loading/progress-bar.d.ts +168 -0
- package/dist/types/components/loading/spinner.d.ts +34 -0
- package/dist/types/components/master-panel/master-panel.d.ts +112 -0
- package/dist/types/components/menu-button/menu-button.d.ts +142 -0
- package/dist/types/components/message/message.d.ts +212 -0
- package/dist/types/components/modal/modal.d.ts +105 -0
- package/dist/types/components/multi-value-editor/field-adapter.d.ts +5 -0
- package/dist/types/components/multi-value-editor/multi-value-editor.d.ts +66 -0
- package/dist/types/components/navigation-bar/navigation-bar.d.ts +229 -0
- package/dist/types/components/number-field/field-adapter.d.ts +5 -0
- package/dist/types/components/number-field/number-field.d.ts +292 -0
- package/dist/types/components/pagination/pagination.d.ts +267 -0
- package/dist/types/components/panel/panel.d.ts +194 -0
- package/dist/types/components/permission/permission.d.ts +93 -0
- package/dist/types/components/rating/field-adapter.d.ts +5 -0
- package/dist/types/components/rating/rating.d.ts +213 -0
- package/dist/types/components/search/search.d.ts +98 -0
- package/dist/types/components/select/field-adapter.d.ts +5 -0
- package/dist/types/components/select/filter.d.ts +11 -0
- package/dist/types/components/select/select.d.ts +235 -0
- package/dist/types/components/skeleton/skeleton.d.ts +97 -0
- package/dist/types/components/slider/field-adapter.d.ts +5 -0
- package/dist/types/components/slider/slider.d.ts +216 -0
- package/dist/types/components/split-view/split-view.d.ts +335 -0
- package/dist/types/components/stepper/stepper.d.ts +314 -0
- package/dist/types/components/stepper/steps.d.ts +131 -0
- package/dist/types/components/tabbox/tabbox.d.ts +230 -0
- package/dist/types/components/table/sort.d.ts +49 -0
- package/dist/types/components/table/table.d.ts +872 -0
- package/dist/types/components/tag-picker/field-adapter.d.ts +5 -0
- package/dist/types/components/tag-picker/tag-picker.d.ts +375 -0
- package/dist/types/components/timebox/field-adapter.d.ts +5 -0
- package/dist/types/components/timebox/timebox.d.ts +121 -0
- package/dist/types/components/toggle/field-adapter.d.ts +5 -0
- package/dist/types/components/toggle/toggle.d.ts +94 -0
- package/dist/types/components/toolbar/toolbar.d.ts +230 -0
- package/dist/types/components/tooltip/tooltip.d.ts +183 -0
- package/dist/types/components/tree/hierarchy.d.ts +113 -0
- package/dist/types/components/tree/tree.d.ts +383 -0
- package/dist/types/components/truncate/truncate.d.ts +61 -0
- package/dist/types/components/value-list/field-adapter.d.ts +5 -0
- package/dist/types/components/value-list/value-list.d.ts +143 -0
- package/dist/types/core/breakpoint.d.ts +68 -0
- package/dist/types/core/component.d.ts +115 -0
- package/dist/types/core/date.d.ts +50 -0
- package/dist/types/core/dom.d.ts +86 -0
- package/dist/types/core/export.d.ts +70 -0
- package/dist/types/core/fontawesome.d.ts +107 -0
- package/dist/types/core/format.d.ts +107 -0
- package/dist/types/core/http.d.ts +122 -0
- package/dist/types/core/i18n.d.ts +35 -0
- package/dist/types/core/icons.d.ts +137 -0
- package/dist/types/core/keyboard.d.ts +72 -0
- package/dist/types/core/position.d.ts +45 -0
- package/dist/types/core/storage.d.ts +46 -0
- package/dist/types/core/util.d.ts +117 -0
- package/dist/types/elements/define.d.ts +73 -0
- package/dist/types/elements/reflect.d.ts +27 -0
- package/dist/types/index.d.ts +79 -0
- package/dist/types/zeyos/connect.d.ts +65 -0
- package/dist/types/zeyos/form.d.ts +140 -0
- package/dist/types/zeyos/icons.d.ts +104 -0
- package/dist/types/zeyos/index.d.ts +12 -0
- package/dist/types/zeyos/modules.d.ts +110 -0
- package/dist/types/zeyos/query.d.ts +157 -0
- package/dist/types/zeyos/schema.d.ts +79 -0
- package/dist/types/zeyos/select.d.ts +167 -0
- package/dist/types/zeyos/table.d.ts +205 -0
- package/dist/zx-compat.esm.js +375 -39
- package/dist/zx-compat.esm.min.js +1 -1
- package/dist/zx-compat.global.js +372 -73
- package/dist/zx-compat.global.min.js +3 -3
- package/dist/zx-zeyos.esm.js +355 -49
- package/dist/zx-zeyos.esm.min.js +1 -1
- package/dist/zx.css +68 -6
- package/dist/zx.esm.js +359 -73
- package/dist/zx.esm.min.js +3 -3
- package/dist/zx.global.js +359 -73
- package/dist/zx.global.min.js +3 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +48 -2
- package/docs/llms.md +31 -0
- package/package.json +37 -9
- package/src/compat/base.js +291 -0
- package/src/compat/globals.js +165 -0
- package/src/compat/index.js +84 -0
- package/src/compat/map/basic.js +464 -0
- package/src/compat/map/data.js +232 -0
- package/src/compat/map/date-time.js +177 -0
- package/src/compat/map/factory.js +45 -0
- package/src/compat/map/form.js +330 -0
- package/src/compat/map/helpers.js +46 -0
- package/src/compat/map/http.js +117 -0
- package/src/compat/map/layout.js +256 -0
- package/src/compat/map/options.js +267 -0
- package/src/compat/map/select.js +198 -0
- package/src/compat/map/stubs.js +26 -0
- package/src/compat/map/utilities.js +113 -0
- package/src/compat-entry.js +11 -0
- package/src/components/badge/badge.css +147 -0
- package/src/components/badge/badge.js +92 -0
- package/src/components/breadcrumb/breadcrumb.css +105 -0
- package/src/components/breadcrumb/breadcrumb.js +331 -0
- package/src/components/button/button.css +104 -0
- package/src/components/button/button.js +76 -0
- package/src/components/check-button/check-button.css +66 -0
- package/src/components/check-button/check-button.js +167 -0
- package/src/components/checklist/checklist.css +65 -0
- package/src/components/checklist/checklist.js +282 -0
- package/src/components/checklist/field-adapter.js +46 -0
- package/src/components/context-menu/context-menu.css +54 -0
- package/src/components/context-menu/context-menu.js +334 -0
- package/src/components/copy/copy-button.css +27 -0
- package/src/components/copy/copy-button.js +78 -0
- package/src/components/copy/copy-input.css +59 -0
- package/src/components/copy/copy-input.js +123 -0
- package/src/components/data-filter/data-filter.css +53 -0
- package/src/components/data-filter/data-filter.js +400 -0
- package/src/components/data-filter/filter-core.js +128 -0
- package/src/components/date-picker/date-picker.css +181 -0
- package/src/components/date-picker/date-picker.js +500 -0
- package/src/components/date-picker/date-range-picker.css +264 -0
- package/src/components/date-picker/date-range-picker.js +990 -0
- package/src/components/date-picker/field-adapter.js +36 -0
- package/src/components/date-picker/month-picker.js +223 -0
- package/src/components/date-picker/range-field-adapter.js +31 -0
- package/src/components/date-picker/time-picker.css +191 -0
- package/src/components/date-picker/time-picker.js +406 -0
- package/src/components/datebox/date-range-box.css +128 -0
- package/src/components/datebox/date-range-box.js +492 -0
- package/src/components/datebox/datebox.css +108 -0
- package/src/components/datebox/datebox.js +358 -0
- package/src/components/datebox/field-adapter.js +34 -0
- package/src/components/dialog/dialog.css +74 -0
- package/src/components/dialog/dialog.js +450 -0
- package/src/components/dropdown/dropdown.css +24 -0
- package/src/components/dropdown/dropdown.js +288 -0
- package/src/components/empty-state/empty-state.css +80 -0
- package/src/components/empty-state/empty-state.js +92 -0
- package/src/components/field/field.css +127 -0
- package/src/components/field/field.js +605 -0
- package/src/components/field-upload/field-adapter.js +21 -0
- package/src/components/field-upload/field-upload.css +108 -0
- package/src/components/field-upload/field-upload.js +475 -0
- package/src/components/fieldset/fieldset.css +37 -0
- package/src/components/fieldset/fieldset.js +307 -0
- package/src/components/finder/finder.css +98 -0
- package/src/components/finder/finder.js +509 -0
- package/src/components/form/form.css +32 -0
- package/src/components/form/form.js +302 -0
- package/src/components/groupbox/groupbox.css +51 -0
- package/src/components/groupbox/groupbox.js +199 -0
- package/src/components/layout/layout.css +117 -0
- package/src/components/layout/layout.js +136 -0
- package/src/components/loading/inline-loading.css +34 -0
- package/src/components/loading/inline-loading.js +140 -0
- package/src/components/loading/progress-bar.css +115 -0
- package/src/components/loading/progress-bar.js +259 -0
- package/src/components/loading/spinner.css +58 -0
- package/src/components/loading/spinner.js +57 -0
- package/src/components/master-panel/master-panel.css +51 -0
- package/src/components/master-panel/master-panel.js +203 -0
- package/src/components/menu-button/menu-button.css +49 -0
- package/src/components/menu-button/menu-button.js +294 -0
- package/src/components/message/message.css +122 -0
- package/src/components/message/message.js +499 -0
- package/src/components/modal/modal.css +47 -0
- package/src/components/modal/modal.js +203 -0
- package/src/components/multi-value-editor/field-adapter.js +44 -0
- package/src/components/multi-value-editor/multi-value-editor.css +56 -0
- package/src/components/multi-value-editor/multi-value-editor.js +170 -0
- package/src/components/navigation-bar/navigation-bar.css +130 -0
- package/src/components/navigation-bar/navigation-bar.js +450 -0
- package/src/components/number-field/field-adapter.js +40 -0
- package/src/components/number-field/number-field.css +77 -0
- package/src/components/number-field/number-field.js +545 -0
- package/src/components/pagination/pagination.css +95 -0
- package/src/components/pagination/pagination.js +481 -0
- package/src/components/panel/panel.css +123 -0
- package/src/components/panel/panel.js +336 -0
- package/src/components/permission/permission.css +47 -0
- package/src/components/permission/permission.js +198 -0
- package/src/components/rating/field-adapter.js +34 -0
- package/src/components/rating/rating.css +97 -0
- package/src/components/rating/rating.js +395 -0
- package/src/components/search/search.css +70 -0
- package/src/components/search/search.js +193 -0
- package/src/components/select/field-adapter.js +24 -0
- package/src/components/select/filter.js +39 -0
- package/src/components/select/select.css +180 -0
- package/src/components/select/select.js +663 -0
- package/src/components/skeleton/skeleton.css +76 -0
- package/src/components/skeleton/skeleton.js +139 -0
- package/src/components/slider/field-adapter.js +36 -0
- package/src/components/slider/slider.css +181 -0
- package/src/components/slider/slider.js +399 -0
- package/src/components/split-view/split-view.css +139 -0
- package/src/components/split-view/split-view.js +806 -0
- package/src/components/stepper/stepper.css +234 -0
- package/src/components/stepper/stepper.js +459 -0
- package/src/components/stepper/steps.js +131 -0
- package/src/components/tabbox/tabbox.css +281 -0
- package/src/components/tabbox/tabbox.js +484 -0
- package/src/components/table/sort.js +85 -0
- package/src/components/table/table.css +364 -0
- package/src/components/table/table.js +1711 -0
- package/src/components/tag-picker/field-adapter.js +35 -0
- package/src/components/tag-picker/tag-picker.css +169 -0
- package/src/components/tag-picker/tag-picker.js +837 -0
- package/src/components/timebox/field-adapter.js +24 -0
- package/src/components/timebox/timebox.css +51 -0
- package/src/components/timebox/timebox.js +276 -0
- package/src/components/toggle/field-adapter.js +24 -0
- package/src/components/toggle/toggle.css +66 -0
- package/src/components/toggle/toggle.js +150 -0
- package/src/components/toolbar/toolbar.css +81 -0
- package/src/components/toolbar/toolbar.js +511 -0
- package/src/components/tooltip/tooltip.css +36 -0
- package/src/components/tooltip/tooltip.js +420 -0
- package/src/components/tree/hierarchy.js +151 -0
- package/src/components/tree/tree.css +99 -0
- package/src/components/tree/tree.js +784 -0
- package/src/components/truncate/truncate.css +20 -0
- package/src/components/truncate/truncate.js +92 -0
- package/src/components/value-list/field-adapter.js +24 -0
- package/src/components/value-list/value-list.css +108 -0
- package/src/components/value-list/value-list.js +336 -0
- package/src/core/breakpoint.js +150 -0
- package/src/core/component.js +292 -0
- package/src/core/date.js +216 -0
- package/src/core/dom.js +260 -0
- package/src/core/export.js +204 -0
- package/src/core/fontawesome.js +177 -0
- package/src/core/format.js +283 -0
- package/src/core/http.js +246 -0
- package/src/core/i18n.js +83 -0
- package/src/core/icons.js +283 -0
- package/src/core/keyboard.js +240 -0
- package/src/core/position.js +177 -0
- package/src/core/storage.js +175 -0
- package/src/core/util.js +336 -0
- package/src/elements/define.js +715 -0
- package/src/elements/reflect.js +79 -0
- package/src/index.js +131 -0
- package/src/zeyos/connect.js +142 -0
- package/src/zeyos/form.js +254 -0
- package/src/zeyos/icons.js +92 -0
- package/src/zeyos/index.js +21 -0
- package/src/zeyos/modules.js +269 -0
- package/src/zeyos/query.js +199 -0
- package/src/zeyos/schema.js +474 -0
- package/src/zeyos/select.js +175 -0
- package/src/zeyos/table.js +272 -0
- package/styles/base.css +124 -0
- package/styles/icons.css +34 -0
- package/styles/tokens/dark.css +84 -0
- package/styles/tokens/density.css +9 -0
- package/styles/tokens/global.css +80 -0
- package/styles/tokens/modules.css +109 -0
- package/styles/tokens/semantic.css +49 -0
- package/styles/zx.css +61 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* The icon layer. Two renderers sit behind one `icon()` call:
|
|
4
|
+
*
|
|
5
|
+
* - **built-in** (default) — the inline SVG glyphs below, no network, no stylesheet, no font.
|
|
6
|
+
* - **Font Awesome** — `<i>` elements carrying Font Awesome classes, after an application opts in
|
|
7
|
+
* with `useFontAwesome()` or `loadFontAwesome(kit)`. Zx never loads a kit by itself.
|
|
8
|
+
*
|
|
9
|
+
* Both renderers put `zx-icon` on the element, so component CSS can style either.
|
|
10
|
+
* @module zx/core/icons
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { faIconClasses, kitUrl, loadFontAwesomeKit, parseIconSpec } from './fontawesome.js';
|
|
14
|
+
|
|
15
|
+
const SVG_NS = 'http://www.w3.org/2000/svg';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {Object} IconOptions
|
|
19
|
+
* @property {number|string} [size=16] Rendered width and height.
|
|
20
|
+
* @property {string|null} [label=null] Accessible label; null marks the icon decorative.
|
|
21
|
+
* @property {string|string[]} [class] Extra class names added to the element.
|
|
22
|
+
* @property {string} [style] Font Awesome style for this icon only (`'solid'`, `'duotone'`, …).
|
|
23
|
+
* @property {string} [family] Font Awesome family for this icon only (`'classic'`, `'sharp'`, …).
|
|
24
|
+
* @property {boolean} [fixedWidth] Adds Font Awesome's `fa-fw` for column-aligned glyphs.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Icon glyphs from Font Awesome Free 6 (solid, plus `square` from regular), fontawesome.com,
|
|
29
|
+
* licensed CC BY 4.0 (https://fontawesome.com/license/free). Path data is embedded unmodified so
|
|
30
|
+
* the library carries no webfont, stylesheet, or package dependency. Each entry is
|
|
31
|
+
* `[viewBox, path]`; Font Awesome glyphs are filled (not stroked) and use per-glyph 512-unit view
|
|
32
|
+
* boxes — outlines such as `square` are a filled path with a counter-wound hole.
|
|
33
|
+
* Extend the set with `registerIcons()`; read every available name with `iconNames()`.
|
|
34
|
+
* @type {Readonly<Record<string, [string, string]>>}
|
|
35
|
+
*/
|
|
36
|
+
export const icons = Object.freeze({
|
|
37
|
+
'chevron-down': ['0 0 512 512', 'M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z'],
|
|
38
|
+
'chevron-up': ['0 0 512 512', 'M233.4 105.4c12.5-12.5 32.8-12.5 45.3 0l192 192c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L256 173.3 86.6 342.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l192-192z'],
|
|
39
|
+
'chevron-left': ['0 0 320 512', 'M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l192 192c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.3 256 246.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-192 192z'],
|
|
40
|
+
'chevron-right': ['0 0 320 512', 'M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L210.7 256 73.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z'],
|
|
41
|
+
check: ['0 0 448 512', 'M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z'],
|
|
42
|
+
x: ['0 0 384 512', 'M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z'],
|
|
43
|
+
plus: ['0 0 448 512', 'M256 80c0-17.7-14.3-32-32-32s-32 14.3-32 32V224H48c-17.7 0-32 14.3-32 32s14.3 32 32 32H192V432c0 17.7 14.3 32 32 32s32-14.3 32-32V288H400c17.7 0 32-14.3 32-32s-14.3-32-32-32H256V80z'],
|
|
44
|
+
minus: ['0 0 448 512', 'M432 256c0 17.7-14.3 32-32 32L48 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l352 0c17.7 0 32 14.3 32 32z'],
|
|
45
|
+
search: ['0 0 512 512', 'M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z'],
|
|
46
|
+
calendar: ['0 0 448 512', 'M128 0c17.7 0 32 14.3 32 32V64H288V32c0-17.7 14.3-32 32-32s32 14.3 32 32V64h48c26.5 0 48 21.5 48 48v48H0V112C0 85.5 21.5 64 48 64H96V32c0-17.7 14.3-32 32-32zM0 192H448V464c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V192z'],
|
|
47
|
+
clock: ['0 0 512 512', 'M256 0a256 256 0 1 1 0 512A256 256 0 1 1 256 0zM232 120V256c0 8 4 15.5 10.7 20l96 64c11 7.4 25.9 4.4 33.3-6.7s4.4-25.9-6.7-33.3L280 243.2V120c0-13.3-10.7-24-24-24s-24 10.7-24 24z'],
|
|
48
|
+
trash: ['0 0 448 512', 'M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8 296.3 0 284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6 45 47.9 45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z'],
|
|
49
|
+
gear: ['0 0 512 512', 'M481.9 166.6c3.2 8.7 .5 18.4-6.4 24.6l-43.3 39.4c1.1 8.3 1.7 16.8 1.7 25.4s-.6 17.1-1.7 25.4l43.3 39.4c6.9 6.2 9.6 15.9 6.4 24.6c-4.4 11.9-9.7 23.3-15.8 34.3l-4.7 8.1c-6.6 11-14 21.4-22.1 31.2c-5.9 7.2-15.7 9.6-24.5 6.8l-55.7-17.7c-13.4 10.3-28.2 18.9-44 25.4l-12.5 57.1c-2 9.1-9 16.3-18.2 17.8c-13.8 2.3-28 3.5-42.5 3.5s-28.7-1.2-42.5-3.5c-9.2-1.5-16.2-8.7-18.2-17.8l-12.5-57.1c-15.8-6.5-30.6-15.1-44-25.4L83.1 425.9c-8.8 2.8-18.6 .3-24.5-6.8c-8.1-9.8-15.5-20.2-22.1-31.2l-4.7-8.1c-6.1-11-11.4-22.4-15.8-34.3c-3.2-8.7-.5-18.4 6.4-24.6l43.3-39.4C64.6 273.1 64 264.6 64 256s.6-17.1 1.7-25.4L22.4 191.2c-6.9-6.2-9.6-15.9-6.4-24.6c4.4-11.9 9.7-23.3 15.8-34.3l4.7-8.1c6.6-11 14-21.4 22.1-31.2c5.9-7.2 15.7-9.6 24.5-6.8l55.7 17.7c13.4-10.3 28.2-18.9 44-25.4l12.5-57.1c2-9.1 9-16.3 18.2-17.8C227.3 1.2 241.5 0 256 0s28.7 1.2 42.5 3.5c9.2 1.5 16.2 8.7 18.2 17.8l12.5 57.1c15.8 6.5 30.6 15.1 44 25.4l55.7-17.7c8.8-2.8 18.6-.3 24.5 6.8c8.1 9.8 15.5 20.2 22.1 31.2l4.7 8.1c6.1 11 11.4 22.4 15.8 34.3zM256 336a80 80 0 1 0 0-160 80 80 0 1 0 0 160z'],
|
|
50
|
+
eye: ['0 0 576 512', 'M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM144 256a144 144 0 1 1 288 0 144 144 0 1 1-288 0zm144-64c0 35.3-28.7 64-64 64c-7.1 0-13.9-1.2-20.3-3.3c-5.5-1.8-11.9 1.6-11.7 7.4c.3 6.9 1.3 13.8 3.2 20.7c13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3z'],
|
|
51
|
+
lock: ['0 0 448 512', 'M144 144v48H304V144c0-44.2-35.8-80-80-80s-80 35.8-80 80zM80 192V144C80 64.5 144.5 0 224 0s144 64.5 144 144v48h16c35.3 0 64 28.7 64 64V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V256c0-35.3 28.7-64 64-64H80z'],
|
|
52
|
+
reload: ['0 0 512 512', 'M386.3 160H336c-17.7 0-32 14.3-32 32s14.3 32 32 32H464c17.7 0 32-14.3 32-32V64c0-17.7-14.3-32-32-32s-32 14.3-32 32v51.2L414.4 97.6c-87.5-87.5-229.3-87.5-316.8 0s-87.5 229.3 0 316.8s229.3 87.5 316.8 0c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0c-62.5 62.5-163.8 62.5-226.3 0s-62.5-163.8 0-226.3s163.8-62.5 226.3 0L386.3 160z'],
|
|
53
|
+
list: ['0 0 512 512', 'M40 48C26.7 48 16 58.7 16 72v48c0 13.3 10.7 24 24 24H88c13.3 0 24-10.7 24-24V72c0-13.3-10.7-24-24-24H40zM192 64c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H192zm0 160c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H192zm0 160c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H192zM16 232v48c0 13.3 10.7 24 24 24H88c13.3 0 24-10.7 24-24V232c0-13.3-10.7-24-24-24H40c-13.3 0-24 10.7-24 24zM40 368c-13.3 0-24 10.7-24 24v48c0 13.3 10.7 24 24 24H88c13.3 0 24-10.7 24-24V392c0-13.3-10.7-24-24-24H40z'],
|
|
54
|
+
filter: ['0 0 512 512', 'M3.9 54.9C10.5 40.9 24.5 32 40 32H472c15.5 0 29.5 8.9 36.1 22.9s4.6 30.5-5.2 42.5L320 320.9V448c0 12.1-6.8 23.2-17.7 28.6s-23.8 4.3-33.5-3l-64-48c-8.1-6-12.8-15.5-12.8-25.6V320.9L9 97.3C-.7 85.4-2.8 68.8 3.9 54.9z'],
|
|
55
|
+
dots: ['0 0 448 512', 'M8 256a56 56 0 1 1 112 0A56 56 0 1 1 8 256zm160 0a56 56 0 1 1 112 0 56 56 0 1 1-112 0zm216-56a56 56 0 1 1 0 112 56 56 0 1 1 0-112z'],
|
|
56
|
+
info: ['0 0 512 512', 'M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z'],
|
|
57
|
+
warning: ['0 0 512 512', 'M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480H40c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24V296c0 13.3 10.7 24 24 24s24-10.7 24-24V184c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0-64 0 32 32 0 1 0 64 0z'],
|
|
58
|
+
error: ['0 0 512 512', 'M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24V264c0 13.3-10.7 24-24 24s-24-10.7-24-24V152c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1-64 0z'],
|
|
59
|
+
success: ['0 0 512 512', 'M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM369 209L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z'],
|
|
60
|
+
upload: ['0 0 512 512', 'M288 109.3V352c0 17.7-14.3 32-32 32s-32-14.3-32-32V109.3l-73.4 73.4c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l128-128c12.5-12.5 32.8-12.5 45.3 0l128 128c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L288 109.3zM64 352H192c0 35.3 28.7 64 64 64s64-28.7 64-64H448c35.3 0 64 28.7 64 64v32c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V416c0-35.3 28.7-64 64-64z'],
|
|
61
|
+
drag: ['0 0 320 512', 'M40 352l48 0c22.1 0 40 17.9 40 40l0 48c0 22.1-17.9 40-40 40l-48 0c-22.1 0-40-17.9-40-40l0-48c0-22.1 17.9-40 40-40zm192 0l48 0c22.1 0 40 17.9 40 40l0 48c0 22.1-17.9 40-40 40l-48 0c-22.1 0-40-17.9-40-40l0-48c0-22.1 17.9-40 40-40zM40 176l48 0c22.1 0 40 17.9 40 40l0 48c0 22.1-17.9 40-40 40l-48 0c-22.1 0-40-17.9-40-40l0-48c0-22.1 17.9-40 40-40zm192 0l48 0c22.1 0 40 17.9 40 40l0 48c0 22.1-17.9 40-40 40l-48 0c-22.1 0-40-17.9-40-40l0-48c0-22.1 17.9-40 40-40zM40 0L88 0c22.1 0 40 17.9 40 40l0 48c0 22.1-17.9 40-40 40L40 128C17.9 128 0 110.1 0 88L0 40C0 17.9 17.9 0 40 0zM232 0l48 0c22.1 0 40 17.9 40 40l0 48c0 22.1-17.9 40-40 40l-48 0c-22.1 0-40-17.9-40-40l0-48c0-22.1 17.9-40 40-40z'],
|
|
62
|
+
star: ['0 0 576 512', 'M287.9 0c9.2 0 17.6 5.2 21.6 13.5l68.6 141.3 153.2 22.6c9 1.3 16.5 7.6 19.3 16.3s.5 18.1-5.9 24.5L433.6 328.4l26.2 155.6c1.5 9-2.2 18.1-9.7 23.5s-17.3 6-25.3 1.7l-137-73.2L151 509.1c-8.1 4.3-17.9 3.7-25.3-1.7s-11.2-14.5-9.7-23.5l26.2-155.6L31.1 218.2c-6.5-6.4-8.7-15.9-5.9-24.5s10.3-14.9 19.3-16.3l153.2-22.6L266.3 13.5C270.4 5.2 278.7 0 287.9 0z'],
|
|
63
|
+
heart: ['0 0 512 512', 'M47.6 300.4L228.3 469.1c7.5 7 17.4 10.9 27.7 10.9s20.2-3.9 27.7-10.9L464.4 300.4c30.4-28.3 47.6-68 47.6-109.5v-5.8c0-69.9-50.5-129.5-119.4-141C347 36.5 300.6 51.4 268 84L256 96 244 84c-32.6-32.6-79-47.5-124.6-39.9C50.5 55.6 0 115.2 0 185.1v5.8c0 41.5 17.2 81.2 47.6 109.5z'],
|
|
64
|
+
tag: ['0 0 448 512', 'M0 80V229.5c0 17 6.7 33.3 18.7 45.3l176 176c25 25 65.5 25 90.5 0L418.7 317.3c25-25 25-65.5 0-90.5l-176-176c-12-12-28.3-18.7-45.3-18.7H48C21.5 32 0 53.5 0 80zm112 32a32 32 0 1 1 0 64 32 32 0 1 1 0-64z'],
|
|
65
|
+
folder: ['0 0 512 512', 'M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H298.5c-17 0-33.3-6.7-45.3-18.7L226.7 50.7c-12-12-28.3-18.7-45.3-18.7H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z'],
|
|
66
|
+
'folder-open': ['0 0 576 512', 'M88.7 223.8L0 375.8V96C0 60.7 28.7 32 64 32H181.5c17 0 33.3 6.7 45.3 18.7l26.5 26.5c12 12 28.3 18.7 45.3 18.7H416c35.3 0 64 28.7 64 64v32H144c-22.8 0-43.8 12.1-55.3 31.8zm27.6 16.1C122.1 230 132.6 224 144 224H544c11.5 0 22 6.1 27.7 16.1s5.7 22.2-.1 32.1l-112 192C453.9 474 443.4 480 432 480H32c-11.5 0-22-6.1-27.7-16.1s-5.7-22.2 .1-32.1l112-192z'],
|
|
67
|
+
file: ['0 0 384 512', 'M0 64C0 28.7 28.7 0 64 0H224V128c0 17.7 14.3 32 32 32H384V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V64zm384 64H256V0L384 128z'],
|
|
68
|
+
copy: ['0 0 448 512', 'M208 0L332.1 0c12.7 0 24.9 5.1 33.9 14.1l67.9 67.9c9 9 14.1 21.2 14.1 33.9L448 336c0 26.5-21.5 48-48 48l-192 0c-26.5 0-48-21.5-48-48l0-288c0-26.5 21.5-48 48-48zM48 128l80 0 0 64-64 0 0 256 192 0 0-32 64 0 0 48c0 26.5-21.5 48-48 48L48 512c-26.5 0-48-21.5-48-48L0 176c0-26.5 21.5-48 48-48z'],
|
|
69
|
+
square: ['0 0 448 512', 'M384 80c8.8 0 16 7.2 16 16V416c0 8.8-7.2 16-16 16H64c-8.8 0-16-7.2-16-16V96c0-8.8 7.2-16 16-16H384zM64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64z'],
|
|
70
|
+
code: ['0 0 640 512', 'M392.8 1.2c-17-4.9-34.7 5-39.6 22l-128 448c-4.9 17 5 34.7 22 39.6s34.7-5 39.6-22l128-448c4.9-17-5-34.7-22-39.6zm80.6 120.1c-12.5 12.5-12.5 32.8 0 45.3L562.7 256l-89.4 89.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l112-112c12.5-12.5 12.5-32.8 0-45.3l-112-112c-12.5-12.5-32.8-12.5-45.3 0zm-306.7 0c-12.5-12.5-32.8-12.5-45.3 0l-112 112c-12.5 12.5-12.5 32.8 0 45.3l112 112c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256l89.4-89.4c12.5-12.5 12.5-32.8 0-45.3z'],
|
|
71
|
+
book: ['0 0 448 512', 'M96 0C43 0 0 43 0 96V416c0 53 43 96 96 96H384h32c17.7 0 32-14.3 32-32s-14.3-32-32-32V384c17.7 0 32-14.3 32-32V32c0-17.7-14.3-32-32-32H384 96zm0 384H352v64H96c-17.7 0-32-14.3-32-32s14.3-32 32-32zm32-240c0-8.8 7.2-16 16-16H336c8.8 0 16 7.2 16 16s-7.2 16-16 16H144c-8.8 0-16-7.2-16-16zm16 48H336c8.8 0 16 7.2 16 16s-7.2 16-16 16H144c-8.8 0-16-7.2-16-16s7.2-16 16-16z']
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
/** Legacy gx Factory icon names mapped to their Zx equivalents. */
|
|
75
|
+
const aliases = Object.freeze({
|
|
76
|
+
settings: 'gear',
|
|
77
|
+
clear: 'x',
|
|
78
|
+
checked: 'check',
|
|
79
|
+
question: 'info',
|
|
80
|
+
range: 'filter',
|
|
81
|
+
fields: 'list'
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
/** @type {Record<string, [string, string]>} Glyphs added at runtime by `registerIcons`. */
|
|
85
|
+
const extraGlyphs = Object.create(null);
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @typedef {Object} IconConfig
|
|
89
|
+
* @property {'builtin'|'fontawesome'} provider Renderer used for bare icon names.
|
|
90
|
+
* @property {string} style Default Font Awesome style (`'solid'`, `'regular'`, `'duotone'`, …).
|
|
91
|
+
* @property {string} family Default Font Awesome family (`'classic'`, `'sharp'`, …).
|
|
92
|
+
* @property {boolean} fixedWidth Whether Font Awesome icons get `fa-fw` by default.
|
|
93
|
+
* @property {string|null} kit Kit token or URL passed to the last `loadFontAwesome()` call.
|
|
94
|
+
*/
|
|
95
|
+
|
|
96
|
+
/** @type {IconConfig} */
|
|
97
|
+
const config = {
|
|
98
|
+
provider: 'builtin',
|
|
99
|
+
style: 'solid',
|
|
100
|
+
family: 'classic',
|
|
101
|
+
fixedWidth: false,
|
|
102
|
+
kit: null
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Reads the active icon configuration.
|
|
107
|
+
* @returns {IconConfig} A copy — mutating it does not change the configuration.
|
|
108
|
+
*/
|
|
109
|
+
export function getIconConfig() {
|
|
110
|
+
return { ...config };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Sets how bare icon names are rendered. Explicitly prefixed names (`'fa:user'`,
|
|
115
|
+
* `'builtin:check'`) and literal class lists (`'fa-solid fa-user'`) always ignore this.
|
|
116
|
+
* @param {Partial<IconConfig>} options Values to change.
|
|
117
|
+
* @returns {IconConfig} The configuration after the change.
|
|
118
|
+
*/
|
|
119
|
+
export function configureIcons(options = {}) {
|
|
120
|
+
for (const key of ['provider', 'style', 'family', 'fixedWidth', 'kit']) {
|
|
121
|
+
if (options[key] !== undefined) config[key] = options[key];
|
|
122
|
+
}
|
|
123
|
+
return getIconConfig();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Switches bare icon names to Font Awesome. Use this when the kit, or a self-hosted Font Awesome
|
|
128
|
+
* stylesheet, is already on the page; use `loadFontAwesome()` to inject a kit as well.
|
|
129
|
+
* @param {Partial<Omit<IconConfig, 'provider'>>} [options={}] Default style, family, modifiers.
|
|
130
|
+
* @returns {IconConfig} The configuration after the change.
|
|
131
|
+
*/
|
|
132
|
+
export function useFontAwesome(options = {}) {
|
|
133
|
+
return configureIcons({ ...options, provider: 'fontawesome' });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Switches bare icon names back to the inline SVG glyphs bundled with Zx.
|
|
138
|
+
* @returns {IconConfig} The configuration after the change.
|
|
139
|
+
*/
|
|
140
|
+
export function useBuiltinIcons() {
|
|
141
|
+
return configureIcons({ provider: 'builtin' });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Loads a Font Awesome kit and renders bare icon names with it. The script is injected once per
|
|
146
|
+
* URL; a kit the page already embeds is adopted instead of loaded again.
|
|
147
|
+
* @param {string|(Partial<Omit<IconConfig, 'provider'>> & {kit: string, activate?: boolean})} options
|
|
148
|
+
* Kit token/URL, or an options object naming one.
|
|
149
|
+
* @returns {Promise<IconConfig>} Resolves with the configuration once the kit has loaded.
|
|
150
|
+
*/
|
|
151
|
+
export async function loadFontAwesome(options) {
|
|
152
|
+
const settings = typeof options === 'string' ? { kit: options } : { ...options };
|
|
153
|
+
const { kit, activate = true, ...rest } = settings;
|
|
154
|
+
if (!kit) throw new TypeError('loadFontAwesome() needs a kit token or URL');
|
|
155
|
+
configureIcons({ ...rest, kit: kitUrl(kit) });
|
|
156
|
+
await loadFontAwesomeKit(kit);
|
|
157
|
+
return activate ? useFontAwesome() : getIconConfig();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Adds inline SVG glyphs to the built-in set, or replaces existing ones. Entries use the same
|
|
162
|
+
* `[viewBox, path]` shape as `icons`.
|
|
163
|
+
* @param {Record<string, [string, string]>} glyphs Glyphs keyed by icon name.
|
|
164
|
+
* @returns {void}
|
|
165
|
+
*/
|
|
166
|
+
export function registerIcons(glyphs) {
|
|
167
|
+
for (const [name, entry] of Object.entries(glyphs)) {
|
|
168
|
+
if (!Array.isArray(entry) || entry.length !== 2) {
|
|
169
|
+
throw new TypeError(`Icon "${name}" must be a [viewBox, path] pair`);
|
|
170
|
+
}
|
|
171
|
+
extraGlyphs[name] = [String(entry[0]), String(entry[1])];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Lists every inline SVG glyph name available, built-in and registered.
|
|
177
|
+
* @returns {string[]} Sorted icon names.
|
|
178
|
+
*/
|
|
179
|
+
export function iconNames() {
|
|
180
|
+
return [...new Set([...Object.keys(icons), ...Object.keys(extraGlyphs)])].sort();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Creates an icon element.
|
|
185
|
+
*
|
|
186
|
+
* Bare names (`'check'`) render through the active provider: the bundled inline SVG set by
|
|
187
|
+
* default, or Font Awesome after `useFontAwesome()` / `loadFontAwesome()`. A name can also select
|
|
188
|
+
* its renderer itself — `'fa:user'`, `'duotone:user'`, `'kit:zeyos-notes'`, `'builtin:check'`, or
|
|
189
|
+
* a literal Font Awesome class list such as `'fa-solid fa-user'`.
|
|
190
|
+
*
|
|
191
|
+
* @param {string} name Icon name or spec.
|
|
192
|
+
* @param {IconOptions} [options={}] Display and accessibility options.
|
|
193
|
+
* @returns {SVGSVGElement|HTMLElement} An `<svg>` for inline glyphs, an `<i>` for Font Awesome.
|
|
194
|
+
*/
|
|
195
|
+
export function icon(name, options = {}) {
|
|
196
|
+
const spec = parseIconSpec(name);
|
|
197
|
+
if (spec.classes) return faIcon(spec.classes, options);
|
|
198
|
+
|
|
199
|
+
const resolved = aliases[spec.name] ?? spec.name;
|
|
200
|
+
if (spec.provider === 'builtin') return builtinIcon(resolved, options);
|
|
201
|
+
if (spec.provider === 'fa') {
|
|
202
|
+
return faIcon(faIconClasses(resolved, {
|
|
203
|
+
style: spec.style ?? options.style ?? config.style,
|
|
204
|
+
family: options.family ?? config.family,
|
|
205
|
+
fixedWidth: options.fixedWidth ?? config.fixedWidth
|
|
206
|
+
}), options);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (config.provider === 'fontawesome') {
|
|
210
|
+
return faIcon(faIconClasses(resolved, {
|
|
211
|
+
style: options.style ?? config.style,
|
|
212
|
+
family: options.family ?? config.family,
|
|
213
|
+
fixedWidth: options.fixedWidth ?? config.fixedWidth
|
|
214
|
+
}), options);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return builtinIcon(resolved, options);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Renders one of the bundled inline SVG glyphs.
|
|
222
|
+
* @param {string} name Resolved glyph name.
|
|
223
|
+
* @param {IconOptions} options Display and accessibility options.
|
|
224
|
+
* @returns {SVGSVGElement}
|
|
225
|
+
*/
|
|
226
|
+
function builtinIcon(name, options) {
|
|
227
|
+
const entry = extraGlyphs[name] ?? icons[name];
|
|
228
|
+
if (!entry) throw new RangeError(`Unknown icon: ${name}`);
|
|
229
|
+
const [viewBox, pathData] = entry;
|
|
230
|
+
const { size = 16 } = options;
|
|
231
|
+
const svg = document.createElementNS(SVG_NS, 'svg');
|
|
232
|
+
const path = document.createElementNS(SVG_NS, 'path');
|
|
233
|
+
svg.setAttribute('class', classList('zx-icon', options.class));
|
|
234
|
+
svg.setAttribute('viewBox', viewBox);
|
|
235
|
+
svg.setAttribute('width', String(size));
|
|
236
|
+
svg.setAttribute('height', String(size));
|
|
237
|
+
svg.setAttribute('fill', 'currentColor');
|
|
238
|
+
svg.setAttribute('focusable', 'false');
|
|
239
|
+
applyLabel(svg, options.label ?? null);
|
|
240
|
+
path.setAttribute('d', pathData);
|
|
241
|
+
svg.append(path);
|
|
242
|
+
return svg;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Renders a Font Awesome icon element. The glyph itself comes from the kit or stylesheet the
|
|
247
|
+
* application loaded; Zx only writes the classes.
|
|
248
|
+
* @param {string[]} classes Font Awesome class names.
|
|
249
|
+
* @param {IconOptions} options Display and accessibility options.
|
|
250
|
+
* @returns {HTMLElement}
|
|
251
|
+
*/
|
|
252
|
+
function faIcon(classes, options) {
|
|
253
|
+
const { size = 16 } = options;
|
|
254
|
+
const element = document.createElement('i');
|
|
255
|
+
element.className = classList('zx-icon', ...classes, options.class);
|
|
256
|
+
element.style.fontSize = typeof size === 'number' ? `${size}px` : String(size);
|
|
257
|
+
applyLabel(element, options.label ?? null);
|
|
258
|
+
return element;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Marks an icon decorative, or names it for assistive technology.
|
|
263
|
+
* @param {Element} element Icon element.
|
|
264
|
+
* @param {string|null} label Accessible label; null marks the icon decorative.
|
|
265
|
+
* @returns {void}
|
|
266
|
+
*/
|
|
267
|
+
function applyLabel(element, label) {
|
|
268
|
+
if (label === null) {
|
|
269
|
+
element.setAttribute('aria-hidden', 'true');
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
element.setAttribute('role', 'img');
|
|
273
|
+
element.setAttribute('aria-label', label);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Joins class names, accepting strings, arrays, and nullish values.
|
|
278
|
+
* @param {...(string|string[]|null|undefined)} values Class name sources.
|
|
279
|
+
* @returns {string}
|
|
280
|
+
*/
|
|
281
|
+
function classList(...values) {
|
|
282
|
+
return values.flat().filter(Boolean).join(' ');
|
|
283
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
const FOCUSABLE_SELECTOR = [
|
|
3
|
+
'a[href]', 'area[href]', 'button:not([disabled])', 'input:not([disabled])',
|
|
4
|
+
'select:not([disabled])', 'textarea:not([disabled])', 'iframe', 'object', 'embed',
|
|
5
|
+
'[contenteditable="true"]', '[tabindex]:not([tabindex="-1"])'
|
|
6
|
+
].join(',');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {Object} RovingOptions
|
|
10
|
+
* @property {'vertical'|'horizontal'} [orientation='vertical'] Arrow-key axis.
|
|
11
|
+
* @property {boolean} [wrap=true] Whether navigation wraps at either end.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} FocusTrapController
|
|
15
|
+
* @property {() => void} activate Activate the focus trap.
|
|
16
|
+
* @property {() => void} deactivate Deactivate the focus trap and restore focus.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {Object} RovingController
|
|
20
|
+
* @property {() => void} focusFirst Focus the first item.
|
|
21
|
+
* @property {() => void} focusLast Focus the last item.
|
|
22
|
+
* @property {() => void} destroy Remove behavior and restore tabindex values.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Creates a focus trap that cycles Tab focus within a container while active.
|
|
27
|
+
* @param {Element} container Focus boundary.
|
|
28
|
+
* @returns {FocusTrapController}
|
|
29
|
+
*/
|
|
30
|
+
export function focusTrap(container) {
|
|
31
|
+
let active = false;
|
|
32
|
+
let previousFocus = null;
|
|
33
|
+
|
|
34
|
+
/** @returns {void} */
|
|
35
|
+
function onKeydown(event) {
|
|
36
|
+
if (event.key !== 'Tab') return;
|
|
37
|
+
const items = focusableItems(container);
|
|
38
|
+
if (items.length === 0) {
|
|
39
|
+
event.preventDefault();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const first = items[0];
|
|
43
|
+
const last = items[items.length - 1];
|
|
44
|
+
if (event.shiftKey && (document.activeElement === first || !container.contains(document.activeElement))) {
|
|
45
|
+
event.preventDefault();
|
|
46
|
+
last.focus();
|
|
47
|
+
} else if (!event.shiftKey && (document.activeElement === last || !container.contains(document.activeElement))) {
|
|
48
|
+
event.preventDefault();
|
|
49
|
+
first.focus();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
/** Activates trapping and focuses the first focusable descendant. @returns {void} */
|
|
55
|
+
activate() {
|
|
56
|
+
if (active) return;
|
|
57
|
+
active = true;
|
|
58
|
+
previousFocus = document.activeElement;
|
|
59
|
+
document.addEventListener('keydown', onKeydown);
|
|
60
|
+
focusableItems(container)[0]?.focus();
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
/** Deactivates trapping and restores focus when possible. @returns {void} */
|
|
64
|
+
deactivate() {
|
|
65
|
+
if (!active) return;
|
|
66
|
+
active = false;
|
|
67
|
+
document.removeEventListener('keydown', onKeydown);
|
|
68
|
+
if (previousFocus?.isConnected && typeof previousFocus.focus === 'function') previousFocus.focus();
|
|
69
|
+
previousFocus = null;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Applies the ARIA roving-tabindex keyboard pattern to matching descendants.
|
|
76
|
+
* @param {Element} container Event and query container.
|
|
77
|
+
* @param {string} itemSelector Selector for roving items.
|
|
78
|
+
* @param {RovingOptions} [options={}] Behavior options.
|
|
79
|
+
* @returns {RovingController}
|
|
80
|
+
*/
|
|
81
|
+
export function rovingTabindex(container, itemSelector, { orientation = 'vertical', wrap = true } = {}) {
|
|
82
|
+
const controller = new AbortController();
|
|
83
|
+
const originals = new Map();
|
|
84
|
+
let destroyed = false;
|
|
85
|
+
|
|
86
|
+
/** @returns {HTMLElement[]} */
|
|
87
|
+
function items() {
|
|
88
|
+
return /** @type {HTMLElement[]} */ (Array.from(container.querySelectorAll(itemSelector)))
|
|
89
|
+
.filter((item) => {
|
|
90
|
+
remember(item);
|
|
91
|
+
return isAvailable(item);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** @param {HTMLElement} item @returns {void} */
|
|
96
|
+
function remember(item) {
|
|
97
|
+
if (!originals.has(item)) originals.set(item, item.getAttribute('tabindex'));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** @param {HTMLElement|null} selected @returns {void} */
|
|
101
|
+
function update(selected = null) {
|
|
102
|
+
const available = items();
|
|
103
|
+
const active = selected && available.includes(selected) ? selected :
|
|
104
|
+
available.find((item) => item.tabIndex === 0) ?? available[0];
|
|
105
|
+
for (const item of available) {
|
|
106
|
+
const tabindex = item === active ? 0 : -1;
|
|
107
|
+
if (item.tabIndex !== tabindex) item.tabIndex = tabindex;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** @param {KeyboardEvent} event @returns {void} */
|
|
112
|
+
function onKeydown(event) {
|
|
113
|
+
const available = items();
|
|
114
|
+
const current = /** @type {HTMLElement|null} */ (
|
|
115
|
+
/** @type {Element|null} */ (event.target)?.closest?.(itemSelector) ?? null
|
|
116
|
+
);
|
|
117
|
+
const index = available.indexOf(current);
|
|
118
|
+
if (index < 0) return;
|
|
119
|
+
|
|
120
|
+
const previousKey = orientation === 'horizontal' ? 'ArrowLeft' : 'ArrowUp';
|
|
121
|
+
const nextKey = orientation === 'horizontal' ? 'ArrowRight' : 'ArrowDown';
|
|
122
|
+
let nextIndex = null;
|
|
123
|
+
if (event.key === 'Home') nextIndex = 0;
|
|
124
|
+
else if (event.key === 'End') nextIndex = available.length - 1;
|
|
125
|
+
else if (event.key === previousKey) nextIndex = index - 1;
|
|
126
|
+
else if (event.key === nextKey) nextIndex = index + 1;
|
|
127
|
+
if (nextIndex === null || available.length === 0) return;
|
|
128
|
+
|
|
129
|
+
if (wrap) nextIndex = (nextIndex + available.length) % available.length;
|
|
130
|
+
else nextIndex = Math.max(0, Math.min(available.length - 1, nextIndex));
|
|
131
|
+
event.preventDefault();
|
|
132
|
+
update(available[nextIndex]);
|
|
133
|
+
available[nextIndex].focus();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** @param {FocusEvent} event @returns {void} */
|
|
137
|
+
function onFocus(event) {
|
|
138
|
+
const item = /** @type {HTMLElement|null} */ (
|
|
139
|
+
/** @type {Element|null} */ (event.target)?.closest?.(itemSelector) ?? null
|
|
140
|
+
);
|
|
141
|
+
if (item && container.contains(item)) update(item);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
container.addEventListener('keydown', onKeydown, { signal: controller.signal });
|
|
145
|
+
container.addEventListener('focusin', onFocus, { signal: controller.signal });
|
|
146
|
+
const observer = typeof MutationObserver === 'function' ? new MutationObserver(() => update()) : null;
|
|
147
|
+
observer?.observe(container, { childList: true, subtree: true, attributes: true });
|
|
148
|
+
update();
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
/** Focuses the first available item. @returns {void} */
|
|
152
|
+
focusFirst() {
|
|
153
|
+
const first = items()[0];
|
|
154
|
+
if (first) {
|
|
155
|
+
update(first);
|
|
156
|
+
first.focus();
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
/** Focuses the last available item. @returns {void} */
|
|
161
|
+
focusLast() {
|
|
162
|
+
const available = items();
|
|
163
|
+
const last = available[available.length - 1];
|
|
164
|
+
if (last) {
|
|
165
|
+
update(last);
|
|
166
|
+
last.focus();
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
/** Removes behavior and restores original tabindex attributes. @returns {void} */
|
|
171
|
+
destroy() {
|
|
172
|
+
if (destroyed) return;
|
|
173
|
+
destroyed = true;
|
|
174
|
+
controller.abort();
|
|
175
|
+
observer?.disconnect();
|
|
176
|
+
for (const [item, tabindex] of originals) {
|
|
177
|
+
if (tabindex === null) item.removeAttribute('tabindex');
|
|
178
|
+
else item.setAttribute('tabindex', tabindex);
|
|
179
|
+
}
|
|
180
|
+
originals.clear();
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Creates a 500 ms buffered, case-insensitive first-letter typeahead handler.
|
|
187
|
+
* @template T
|
|
188
|
+
* @param {() => T[]} getItems Returns current candidate items.
|
|
189
|
+
* @param {(item: T, index: number) => void} onMatch Called for the first prefix match.
|
|
190
|
+
* @returns {(event: KeyboardEvent|string) => void}
|
|
191
|
+
*/
|
|
192
|
+
export function typeahead(getItems, onMatch) {
|
|
193
|
+
let buffer = '';
|
|
194
|
+
let timer = null;
|
|
195
|
+
|
|
196
|
+
return function handleTypeahead(event) {
|
|
197
|
+
const key = typeof event === 'string' ? event : event.key;
|
|
198
|
+
if (typeof event !== 'string' && (event.ctrlKey || event.metaKey || event.altKey)) return;
|
|
199
|
+
if (!key || key.length !== 1 || /\s/.test(key)) return;
|
|
200
|
+
|
|
201
|
+
if (timer !== null) clearTimeout(timer);
|
|
202
|
+
buffer += key.toLocaleLowerCase();
|
|
203
|
+
if (buffer.split('').every((character) => character === buffer[0])) buffer = buffer[0];
|
|
204
|
+
timer = setTimeout(() => {
|
|
205
|
+
buffer = '';
|
|
206
|
+
timer = null;
|
|
207
|
+
}, 500);
|
|
208
|
+
|
|
209
|
+
const candidates = getItems();
|
|
210
|
+
const index = candidates.findIndex((item) => itemText(item).startsWith(buffer));
|
|
211
|
+
if (index >= 0) onMatch(candidates[index], index);
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** @param {Element} container @returns {HTMLElement[]} */
|
|
216
|
+
function focusableItems(container) {
|
|
217
|
+
return /** @type {HTMLElement[]} */ (Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR)))
|
|
218
|
+
.filter((item) => isAvailable(item) && item.getClientRects().length > 0);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** @param {Element} item @returns {item is HTMLElement} */
|
|
222
|
+
function isAvailable(item) {
|
|
223
|
+
if (!(item instanceof HTMLElement)) return false;
|
|
224
|
+
if (item.hidden || item.getAttribute('aria-hidden') === 'true' || item.matches(':disabled')) return false;
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** @param {unknown} item @returns {string} */
|
|
229
|
+
function itemText(item) {
|
|
230
|
+
if (typeof item === 'string' || typeof item === 'number') return String(item).trim().toLocaleLowerCase();
|
|
231
|
+
if (item && typeof item === 'object' && 'textContent' in item) {
|
|
232
|
+
return String(item.textContent ?? '').trim().toLocaleLowerCase();
|
|
233
|
+
}
|
|
234
|
+
if (item && typeof item === 'object') {
|
|
235
|
+
const shape = /** @type {{label?: unknown, text?: unknown, value?: unknown}} */ (item);
|
|
236
|
+
const value = shape.label ?? shape.text ?? shape.value;
|
|
237
|
+
if (value != null) return String(value).trim().toLocaleLowerCase();
|
|
238
|
+
}
|
|
239
|
+
return String(item ?? '').trim().toLocaleLowerCase();
|
|
240
|
+
}
|