@fusion.dev/helix-headless 0.22.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/custom-elements.json +48397 -26631
  2. package/dist/announcement/root/announcement-root.d.ts +19 -5
  3. package/dist/area-chart/index.d.ts +1 -0
  4. package/dist/area-chart/index.js +1 -0
  5. package/dist/area-chart/root/area-chart-root.d.ts +182 -0
  6. package/dist/area-chart/root/area-chart-root.js +205 -0
  7. package/dist/carousel/root/carousel-root.d.ts +1 -0
  8. package/dist/carousel/root/carousel-root.js +18 -2
  9. package/dist/chat/index.d.ts +1 -1
  10. package/dist/chat/message/chat-message.d.ts +7 -0
  11. package/dist/chat/message/chat-message.js +2 -1
  12. package/dist/chat/root/chat-root.d.ts +195 -4
  13. package/dist/chat/root/chat-root.js +316 -23
  14. package/dist/checkbox-group/root/checkbox-group-root.d.ts +2 -0
  15. package/dist/checkbox-group/root/checkbox-group-root.js +12 -2
  16. package/dist/code-block/index.d.ts +1 -0
  17. package/dist/code-block/index.js +1 -0
  18. package/dist/code-block/patch.d.ts +62 -0
  19. package/dist/code-block/patch.js +93 -0
  20. package/dist/code-block/root/code-block-root.d.ts +82 -17
  21. package/dist/code-block/root/code-block-root.js +83 -6
  22. package/dist/color-picker/root/color-picker-root.d.ts +4 -3
  23. package/dist/color-picker/root/color-picker-root.js +4 -1
  24. package/dist/data-table/root/data-table-root.d.ts +195 -23
  25. package/dist/data-table/root/data-table-root.js +308 -27
  26. package/dist/description-list/index.d.ts +2 -0
  27. package/dist/description-list/index.js +2 -0
  28. package/dist/description-list/item/description-list-item.d.ts +39 -0
  29. package/dist/description-list/item/description-list-item.js +26 -0
  30. package/dist/description-list/root/description-list-root.d.ts +56 -0
  31. package/dist/description-list/root/description-list-root.js +21 -0
  32. package/dist/element-events.generated.js +27 -6
  33. package/dist/filter-input/chip/filter-chip.d.ts +114 -0
  34. package/dist/filter-input/chip/filter-chip.js +61 -0
  35. package/dist/filter-input/field/filter-field.d.ts +128 -0
  36. package/dist/filter-input/field/filter-field.js +70 -0
  37. package/dist/filter-input/grammar.d.ts +96 -0
  38. package/dist/filter-input/grammar.js +133 -0
  39. package/dist/filter-input/index.d.ts +5 -0
  40. package/dist/filter-input/index.js +5 -0
  41. package/dist/filter-input/menu/filter-menu.d.ts +153 -0
  42. package/dist/filter-input/menu/filter-menu.js +164 -0
  43. package/dist/filter-input/operator/filter-operator.d.ts +105 -0
  44. package/dist/filter-input/operator/filter-operator.js +39 -0
  45. package/dist/filter-input/root/filter-input-root.d.ts +118 -9
  46. package/dist/filter-input/root/filter-input-root.js +96 -2
  47. package/dist/format-date/root/format-date-root.js +1 -1
  48. package/dist/index.d.ts +5 -0
  49. package/dist/index.js +5 -0
  50. package/dist/line-chart/root/line-chart-root.d.ts +9 -2
  51. package/dist/listbox/root/listbox-root.d.ts +8 -7
  52. package/dist/listbox/root/listbox-root.js +7 -2
  53. package/dist/menu/item/menu-item.d.ts +54 -6
  54. package/dist/menu/item/menu-item.js +31 -6
  55. package/dist/menu/root/menu-root.d.ts +23 -0
  56. package/dist/menu/root/menu-root.js +13 -2
  57. package/dist/notifications/group/notification-group.d.ts +129 -0
  58. package/dist/notifications/group/notification-group.js +106 -0
  59. package/dist/notifications/index.d.ts +1 -0
  60. package/dist/notifications/index.js +1 -0
  61. package/dist/notifications/notification/notification.d.ts +39 -13
  62. package/dist/notifications/notification/notification.js +11 -8
  63. package/dist/notifications/root/notifications-root.d.ts +57 -1
  64. package/dist/notifications/root/notifications-root.js +35 -4
  65. package/dist/number-input/root/number-input-root.d.ts +21 -1
  66. package/dist/number-input/root/number-input-root.js +18 -5
  67. package/dist/object-inspector/index.d.ts +1 -0
  68. package/dist/object-inspector/index.js +1 -0
  69. package/dist/object-inspector/root/object-inspector-root.d.ts +183 -0
  70. package/dist/object-inspector/root/object-inspector-root.js +348 -0
  71. package/dist/outline/root/outline-root.d.ts +16 -1
  72. package/dist/outline/root/outline-root.js +27 -9
  73. package/dist/radio/group/radio-group.d.ts +1 -0
  74. package/dist/scatter-chart/index.d.ts +1 -0
  75. package/dist/scatter-chart/index.js +1 -0
  76. package/dist/scatter-chart/root/scatter-chart-root.d.ts +144 -0
  77. package/dist/scatter-chart/root/scatter-chart-root.js +205 -0
  78. package/dist/skip-link/index.d.ts +1 -0
  79. package/dist/skip-link/index.js +1 -0
  80. package/dist/skip-link/root/skip-link-root.d.ts +81 -0
  81. package/dist/skip-link/root/skip-link-root.js +58 -0
  82. package/dist/speed-dial/root/speed-dial-root.d.ts +1 -0
  83. package/dist/speed-dial/root/speed-dial-root.js +6 -2
  84. package/dist/steps/root/steps-root.d.ts +1 -0
  85. package/dist/table/column/table-column.d.ts +79 -0
  86. package/dist/table/column/table-column.js +125 -5
  87. package/dist/table/root/table-root.d.ts +2 -0
  88. package/dist/table/root/table-root.js +12 -3
  89. package/dist/time-picker/root/time-picker-root.d.ts +9 -0
  90. package/dist/time-picker/root/time-picker-root.js +5 -0
  91. package/dist/toast/root/toast-root.d.ts +2 -0
  92. package/dist/tooltip/root/tooltip-root.js +1 -1
  93. package/dist/tour/root/tour-root.d.ts +1 -0
  94. package/dist/tree/item/tree-item.d.ts +7 -4
  95. package/dist/tree/item/tree-item.js +4 -1
  96. package/dist/video/root/video-root.d.ts +23 -1
  97. package/dist/video/root/video-root.js +42 -22
  98. package/dist/virtual-list/root/virtual-list-root.d.ts +10 -6
  99. package/dist/virtual-list/root/virtual-list-root.js +10 -4
  100. package/package.json +5 -5
  101. package/types/jsx.d.ts +4535 -346
  102. package/types/svelte.d.ts +11 -0
  103. package/types/vue.d.ts +737 -39
@@ -5,7 +5,7 @@ registerElementEvents({
5
5
  'helix-headless-app-header': ['helix-scrolled'],
6
6
  'helix-headless-avatar': ['error', 'load'],
7
7
  'helix-headless-carousel': ['helix-select'],
8
- 'helix-headless-chat': ['helix-before-pin'],
8
+ 'helix-headless-chat': ['helix-before-pin', 'helix-history', 'helix-send'],
9
9
  'helix-headless-chat-composer': ['helix-send'],
10
10
  'helix-headless-chat-message': ['helix-send'],
11
11
  'helix-headless-checkbox': ['change', 'helix-before-toggle', 'input'],
@@ -38,7 +38,13 @@ registerElementEvents({
38
38
  ],
39
39
  'helix-headless-command': ['helix-before-close', 'helix-before-open', 'helix-select', 'toggle'],
40
40
  'helix-headless-copy-button': ['helix-copy'],
41
- 'helix-headless-data-table': ['helix-filter', 'helix-navigate', 'helix-sort'],
41
+ 'helix-headless-data-table': [
42
+ 'helix-filter',
43
+ 'helix-navigate',
44
+ 'helix-reorder',
45
+ 'helix-select',
46
+ 'helix-sort',
47
+ ],
42
48
  'helix-headless-date-input': [
43
49
  'change',
44
50
  'helix-before-change',
@@ -53,14 +59,19 @@ registerElementEvents({
53
59
  'helix-headless-drilldown': ['helix-select'],
54
60
  'helix-headless-drilldown-item': ['helix-select'],
55
61
  'helix-headless-file-input': ['change', 'helix-before-change', 'input'],
62
+ 'helix-headless-filter-chip': ['helix-dismiss'],
56
63
  'helix-headless-filter-input': [
57
64
  'change',
58
65
  'helix-before-change',
59
66
  'helix-before-close',
60
67
  'helix-before-open',
68
+ 'helix-dismiss',
69
+ 'helix-facet',
70
+ 'helix-select',
61
71
  'input',
62
72
  'toggle',
63
73
  ],
74
+ 'helix-headless-filter-menu': ['helix-facet'],
64
75
  'helix-headless-heatmap': ['helix-resize'],
65
76
  'helix-headless-image': ['error', 'load'],
66
77
  'helix-headless-input': ['change', 'helix-before-change', 'input'],
@@ -70,8 +81,10 @@ registerElementEvents({
70
81
  'helix-headless-menu': ['helix-before-close', 'helix-before-open', 'helix-select', 'toggle'],
71
82
  'helix-headless-menu-item': ['helix-select'],
72
83
  'helix-headless-notification': ['helix-dismiss', 'helix-read'],
73
- 'helix-headless-notifications': ['helix-read'],
84
+ 'helix-headless-notification-group': ['helix-before-close', 'helix-before-open'],
85
+ 'helix-headless-notifications': ['helix-dismiss', 'helix-read'],
74
86
  'helix-headless-number-input': ['change', 'helix-before-change', 'input'],
87
+ 'helix-headless-object-inspector': ['helix-copy'],
75
88
  'helix-headless-option': ['helix-select'],
76
89
  'helix-headless-outline': ['helix-before-activate'],
77
90
  'helix-headless-pagination': ['helix-navigate'],
@@ -79,7 +92,7 @@ registerElementEvents({
79
92
  'helix-headless-pin-input': ['change', 'helix-before-change', 'helix-complete', 'input'],
80
93
  'helix-headless-popover': ['helix-before-close', 'helix-before-open', 'toggle'],
81
94
  'helix-headless-radio': ['helix-select'],
82
- 'helix-headless-radio-group': ['change', 'helix-before-change', 'input'],
95
+ 'helix-headless-radio-group': ['change', 'helix-before-change', 'helix-select', 'input'],
83
96
  'helix-headless-rating': ['change', 'helix-before-change', 'input'],
84
97
  'helix-headless-select': [
85
98
  'change',
@@ -90,13 +103,19 @@ registerElementEvents({
90
103
  'toggle',
91
104
  ],
92
105
  'helix-headless-slider': ['change', 'helix-before-change', 'input'],
93
- 'helix-headless-speed-dial': ['helix-before-close', 'helix-before-open', 'toggle'],
106
+ 'helix-headless-speed-dial': [
107
+ 'helix-before-close',
108
+ 'helix-before-open',
109
+ 'helix-select',
110
+ 'toggle',
111
+ ],
94
112
  'helix-headless-speed-dial-item': ['helix-select'],
95
113
  'helix-headless-splitter': ['helix-before-collapse', 'helix-before-resize'],
96
114
  'helix-headless-step': ['helix-select'],
97
- 'helix-headless-steps': ['helix-activate', 'helix-before-activate'],
115
+ 'helix-headless-steps': ['helix-activate', 'helix-before-activate', 'helix-select'],
98
116
  'helix-headless-switch': ['change', 'helix-before-toggle', 'input'],
99
117
  'helix-headless-tab': ['helix-select'],
118
+ 'helix-headless-table-column': ['helix-before-resize'],
100
119
  'helix-headless-tabs': ['helix-activate', 'helix-before-activate', 'helix-select'],
101
120
  'helix-headless-tag': ['helix-dismiss'],
102
121
  'helix-headless-textarea': ['change', 'helix-before-change', 'input'],
@@ -109,6 +128,7 @@ registerElementEvents({
109
128
  'toggle',
110
129
  ],
111
130
  'helix-headless-time-picker': ['change', 'helix-before-change', 'input'],
131
+ 'helix-headless-toast': ['helix-dismiss'],
112
132
  'helix-headless-toast-item': ['helix-dismiss'],
113
133
  'helix-headless-toggle': ['helix-select'],
114
134
  'helix-headless-toggle-group': ['change', 'helix-before-change', 'helix-select', 'input'],
@@ -117,6 +137,7 @@ registerElementEvents({
117
137
  'helix-before-activate',
118
138
  'helix-before-close',
119
139
  'helix-complete',
140
+ 'helix-tour-ask',
120
141
  'toggle',
121
142
  ],
122
143
  'helix-headless-tour-stop': ['helix-tour-ask'],
@@ -0,0 +1,114 @@
1
+ import { HelixElement } from '@fusion.dev/helix-core';
2
+ import { type PropertyValues, type TemplateResult } from 'lit';
3
+ declare const HelixHeadlessFilterChip_base: (new (...args: any[]) => import("@fusion.dev/helix-core").DismissibleElement) & typeof HelixElement;
4
+ /**
5
+ * @summary One filter in force: a field, how it is compared, and to what.
6
+ *
7
+ * @description
8
+ * A chip is the readable form of the thing `FilterField` declares. It carries
9
+ * the three parts a filter has and a control that asks for it to go.
10
+ *
11
+ * **The operator is drawn only when it is worth drawing**, which is the whole
12
+ * reason the common case reads as plainly as it does:
13
+ *
14
+ * ```
15
+ * Disposition refunded, chargeback the field's usual comparison, unwritten
16
+ * Disposition is not refunded a comparison somebody chose, so it shows
17
+ * Created after 1 Aug a scalar, which has no usual comparison
18
+ * ```
19
+ *
20
+ * A reader ticking two values in a menu gets the first, with no jargon on it. A
21
+ * reader who reaches for a comparison gets the third, which explains itself the
22
+ * first time they see one. The rule is one line: **omit `operator-label` for the
23
+ * field's default operator and pass it for every other.** Whoever builds the
24
+ * chip holds the field, and the field knows which its default is.
25
+ *
26
+ * **It shows a filter and never applies one.** Dismissing is a request, the way
27
+ * it is on `Tag` and `Announcement`: the chip is the consumer's element in a
28
+ * consumer's list, and taking it out from under them would put their model and
29
+ * the document out of step. `preventDefault()` on the `helix-dismiss` is a
30
+ * refusal, and that is also how a consumer takes control of the interaction for
31
+ * one press: `requestChange` documents the whole pattern, and its first line is
32
+ * that control is an action rather than a mode.
33
+ *
34
+ * **Changing the comparison belongs to whatever is slotted into `operator`.** A
35
+ * menu trigger there makes the segment a control; the text fallback makes it a
36
+ * label. This element does not learn either, for the reason `FilterOperator`
37
+ * gives: what the words mean is the domain's.
38
+ *
39
+ * @since 0.23.0
40
+ * @status experimental
41
+ * @category forms
42
+ * @aliases FilterToken, FacetChip
43
+ * @documentation https://helix.fusion.dev/components/filter-input
44
+ *
45
+ * @slot - What the field is compared to, which is usually text and may be
46
+ * anything a consumer wants read there.
47
+ * @slot field - Replaces the field's name, for a chip that wants a mark beside
48
+ * it or a shortened form.
49
+ * @slot operator - Replaces the comparison, and is where a control that changes
50
+ * it goes.
51
+ *
52
+ * @event {HelixDismissEvent} helix-dismiss - Fired when the reader asks to remove the filter. Cancelable: refusing it takes the decision and nothing moves.
53
+ *
54
+ * @csspart dismiss - The control that asks to remove the filter.
55
+ * @csspart dismiss-mark - The glyph on the dismiss control, where none is slotted.
56
+ * @csspart field - The name of what is being filtered.
57
+ * @csspart operator - How it is compared, drawn only where there is one to draw.
58
+ * @csspart values - What it is compared to.
59
+ */
60
+ export declare class HelixHeadlessFilterChip extends HelixHeadlessFilterChip_base {
61
+ dismissLabel: string;
62
+ /**
63
+ * The field this filter is on, as a reader sees it.
64
+ *
65
+ * The slot's fallback rather than the slot's replacement: a consumer who wants
66
+ * a mark beside the name slots `field` and this goes unused.
67
+ */
68
+ label: string;
69
+ /**
70
+ * The field this filter is on, as the consumer's model calls it.
71
+ *
72
+ * Never shown. Reflected, because a consumer removing one filter from a list
73
+ * of them selects on this, and so undefaulted for the reason `operator` gives.
74
+ *
75
+ * **A chip built in script should be given the attribute, not the property**,
76
+ * if the next thing to happen might select on it. Reflection is written on
77
+ * this element's next update rather than during the assignment, so a second
78
+ * `helix-facet` for the same field arriving in the same frame selects nothing,
79
+ * builds a second chip, and leaves the row holding two of one filter. The
80
+ * lookup then finds whichever came first, so dismissing one chip takes the
81
+ * other away. `setAttribute('name', field)` is true immediately and costs
82
+ * nothing; the property is for markup and for frameworks that write both.
83
+ */
84
+ name?: string;
85
+ /**
86
+ * What the consumer's own model calls the comparison in force.
87
+ *
88
+ * Never shown, and it is what a consumer matches on when the chip comes back
89
+ * to them. `label` translates; this does not.
90
+ *
91
+ * Reflected and so undefaulted: a reflected property with a default writes an
92
+ * attribute on upgrade that the server never wrote, and the two markups differ
93
+ * before a reader has touched anything.
94
+ */
95
+ operator?: string;
96
+ /**
97
+ * The comparison, as a reader sees it, and empty for the field's usual one.
98
+ *
99
+ * **Empty is the whole design and not an oversight.** A chip drawn for the
100
+ * comparison a field is normally filtered by says the field and the values and
101
+ * nothing between them, because a word every chip carries is a word nobody
102
+ * reads. The moment somebody picks a different comparison it appears, which
103
+ * makes an unusual filter look unusual.
104
+ */
105
+ operatorLabel: string;
106
+ protected firstUpdated(changed: PropertyValues): void;
107
+ protected render(): TemplateResult;
108
+ }
109
+ declare global {
110
+ interface HTMLElementTagNameMap {
111
+ 'helix-headless-filter-chip': HelixHeadlessFilterChip;
112
+ }
113
+ }
114
+ export {};
@@ -0,0 +1,61 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { customElement, HelixElement, MixinDismissible } from '@fusion.dev/helix-core';
8
+ import { x } from '@fusion.dev/helix-icons';
9
+ import { html, nothing } from 'lit';
10
+ import { property } from 'lit/decorators.js';
11
+ import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
12
+ let HelixHeadlessFilterChip = class HelixHeadlessFilterChip extends MixinDismissible(HelixElement) {
13
+ constructor() {
14
+ super(...arguments);
15
+ this.dismissLabel = 'Remove filter';
16
+ this.label = '';
17
+ this.operatorLabel = '';
18
+ }
19
+ firstUpdated(changed) {
20
+ super.firstUpdated(changed);
21
+ this.finishHydration();
22
+ }
23
+ render() {
24
+ const operator = this.operatorLabel !== '' || this.knownChildren.some((child) => child.slot === 'operator');
25
+ const values = !this.childrenKnown ||
26
+ [...this.childNodes].some((node) => node instanceof Element ? node.slot === '' : (node.textContent ?? '').trim() !== '');
27
+ return html `<span part="field"><slot name="field">${this.label}</slot></span>
28
+ ${operator
29
+ ? html `<span part="operator"><slot name="operator">${this.operatorLabel}</slot></span>`
30
+ : nothing}
31
+ ${values ? html `<span part="values"><slot></slot></span>` : nothing}
32
+ ${this.dismissible
33
+ ? html `<button
34
+ part="dismiss"
35
+ type="button"
36
+ aria-label=${this.dismissLabel}
37
+ @click=${() => this.dismiss()}
38
+ >
39
+ <svg part="dismiss-mark" fill="currentColor" viewBox=${x.viewBox} aria-hidden="true">
40
+ ${unsafeSVG(x.content)}
41
+ </svg>
42
+ </button>`
43
+ : nothing}`;
44
+ }
45
+ };
46
+ __decorate([
47
+ property()
48
+ ], HelixHeadlessFilterChip.prototype, "label", void 0);
49
+ __decorate([
50
+ property({ reflect: true })
51
+ ], HelixHeadlessFilterChip.prototype, "name", void 0);
52
+ __decorate([
53
+ property({ reflect: true })
54
+ ], HelixHeadlessFilterChip.prototype, "operator", void 0);
55
+ __decorate([
56
+ property({ attribute: 'operator-label' })
57
+ ], HelixHeadlessFilterChip.prototype, "operatorLabel", void 0);
58
+ HelixHeadlessFilterChip = __decorate([
59
+ customElement('helix-headless-filter-chip')
60
+ ], HelixHeadlessFilterChip);
61
+ export { HelixHeadlessFilterChip };
@@ -0,0 +1,128 @@
1
+ import { HelixElement } from '@fusion.dev/helix-core';
2
+ import { nothing, type PropertyValues } from 'lit';
3
+ import type { HelixHeadlessOption } from '../../option/index.js';
4
+ import { type FilterGrammarField } from '../grammar.js';
5
+ import type { HelixHeadlessFilterOperator } from '../operator/filter-operator.js';
6
+ /**
7
+ * @summary One thing a set can be filtered by, and how.
8
+ *
9
+ * @description
10
+ * **A declaration, not a control.** It renders nothing and is never seen. A
11
+ * consumer writes one per field, with the comparisons that field supports and
12
+ * the values it supports them on, and the filter input reads them to build its
13
+ * menu:
14
+ *
15
+ * ```html
16
+ * <helix-headless-filter-field name="disposition" label="Disposition">
17
+ * <helix-headless-filter-operator name="any" label="is any of" default>
18
+ * </helix-headless-filter-operator>
19
+ * <helix-headless-filter-operator name="none" label="is none of">
20
+ * </helix-headless-filter-operator>
21
+ * <helix-headless-option value="refunded">Refunded</helix-headless-option>
22
+ * </helix-headless-filter-field>
23
+ * ```
24
+ *
25
+ * **Operators are per field and never global.** `is after` belongs to a date and
26
+ * `has all of` to a set, and a field that supports one comparison shows no
27
+ * chooser at all. A global list would offer every field every word and leave the
28
+ * component deciding which of them made sense, which is the domain question it
29
+ * declines to answer.
30
+ *
31
+ * **A field that declares none gets two, and they are the ones the punctuation
32
+ * can say.** `is any of` and `is none of` are what `field:a,b` and `-field:a,b`
33
+ * mean, so declaring them was ceremony for the case every product has; the
34
+ * example above is what a consumer writes when the words matter, not the price
35
+ * of admission. Declaring one comparison replaces both rather than joining
36
+ * them — see `vocabulary`, which is where that is decided once for the menu and
37
+ * the grammar together.
38
+ *
39
+ * **Values are `Option`s, which is the element a consumer already has.** They
40
+ * carry a value, a label and a disabled state, and a picker built on `Menu` or
41
+ * `Listbox` can render them without this element inventing a second way to say
42
+ * the same thing. A field whose values cannot be enumerated declares none, and
43
+ * what fills a chip then is whatever the consumer types.
44
+ *
45
+ * @since 0.23.0
46
+ * @status experimental
47
+ * @category forms
48
+ * @aliases Facet, Dimension
49
+ * @documentation https://helix.fusion.dev/components/filter-input
50
+ *
51
+ * @slot - The comparisons this field supports, as `FilterOperator`s, and the
52
+ * values it supports them on, as `Option`s. Neither is rendered: they are read.
53
+ */
54
+ export declare class HelixHeadlessFilterField extends HelixElement {
55
+ /**
56
+ * What a reader is shown, in the menu and on every chip built from this field.
57
+ */
58
+ label: string;
59
+ /**
60
+ * What the consumer's own model calls this field.
61
+ *
62
+ * Never shown, and what arrives in the filter this helps build.
63
+ *
64
+ * Reflected and so undefaulted: a reflected property with a default writes an
65
+ * attribute on upgrade that the server never wrote.
66
+ */
67
+ name?: string;
68
+ /**
69
+ * The comparisons declared under this field, in the order they were written.
70
+ *
71
+ * Read from the light DOM rather than from a slot, because there is no slot:
72
+ * this element renders nothing, so nothing is ever assigned. Order is the
73
+ * consumer's, which is the order a chooser should offer them in.
74
+ *
75
+ * **As declared, so empty where nothing was.** The defaults are applied by
76
+ * `vocabulary` and not here: this getter answers what the consumer wrote, and
77
+ * a caller that wants what the field *supports* wants the other one.
78
+ */
79
+ get operators(): HelixHeadlessFilterOperator[];
80
+ /**
81
+ * The comparison a chip leaves unwritten, or nothing where the consumer named
82
+ * none.
83
+ *
84
+ * **The first one marked, and a second is reported rather than resolved.** Two
85
+ * defaults on one field is a consumer saying two different things, and picking
86
+ * one of them quietly would show a chip whose operator is missing and whose
87
+ * meaning is not the one they wrote. A field with no default has none: every
88
+ * chip built from it then spells its operator out, which is correct for a
89
+ * field whose comparisons are all equally surprising.
90
+ */
91
+ get defaultOperator(): HelixHeadlessFilterOperator | null;
92
+ /**
93
+ * The values declared under this field, in the order they were written.
94
+ *
95
+ * Empty is meaningful rather than missing: a field whose values cannot be
96
+ * listed, a free text search or an open ended number, declares none and the
97
+ * picker offers typing instead of choosing.
98
+ */
99
+ get values(): HelixHeadlessOption[];
100
+ /**
101
+ * This field flattened into what the menu renders and the grammar reads.
102
+ *
103
+ * **The one place the defaults are applied.** A field that declares no
104
+ * comparisons supports `is any of` and `is none of`, which is what the typed
105
+ * grammar can say and so what the picker offers; declaring one replaces both,
106
+ * because a set half inherited and half written is one nobody can read off the
107
+ * markup. Two things need this answer and neither should be resolving it
108
+ * itself.
109
+ *
110
+ * Data rather than elements, so the grammar stays a module with no DOM in it
111
+ * and the menu renders rows from the same shape whether the consumer wrote the
112
+ * operators or not.
113
+ */
114
+ get vocabulary(): FilterGrammarField;
115
+ protected willUpdate(changed: PropertyValues): void;
116
+ /**
117
+ * Nothing, which is what makes this a declaration.
118
+ *
119
+ * No `<slot>`, so none of the children a consumer wrote is painted. See
120
+ * `FilterOperator`'s note, which records the mechanism.
121
+ */
122
+ protected render(): typeof nothing;
123
+ }
124
+ declare global {
125
+ interface HTMLElementTagNameMap {
126
+ 'helix-headless-filter-field': HelixHeadlessFilterField;
127
+ }
128
+ }
@@ -0,0 +1,70 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { customElement, HelixElement } from '@fusion.dev/helix-core';
8
+ import { nothing } from 'lit';
9
+ import { property } from 'lit/decorators.js';
10
+ import { DEFAULT_FILTER_OPERATORS } from '../grammar.js';
11
+ let HelixHeadlessFilterField = class HelixHeadlessFilterField extends HelixElement {
12
+ constructor() {
13
+ super(...arguments);
14
+ this.label = '';
15
+ }
16
+ get operators() {
17
+ return [
18
+ ...this.querySelectorAll(':scope > helix-headless-filter-operator, :scope > helix-filter-operator'),
19
+ ];
20
+ }
21
+ get defaultOperator() {
22
+ const marked = this.operators.filter((operator) => operator.default);
23
+ if (marked.length > 1) {
24
+ console.warn(`\`${this.name || 'A filter field'}\` names ${marked.length} default operators, and a chip can only leave one unwritten. Mark one.`);
25
+ }
26
+ return marked[0] ?? null;
27
+ }
28
+ get values() {
29
+ return [
30
+ ...this.querySelectorAll(':scope > helix-headless-option, :scope > helix-option'),
31
+ ];
32
+ }
33
+ get vocabulary() {
34
+ const declared = this.operators;
35
+ const unwritten = this.defaultOperator;
36
+ return {
37
+ label: this.label,
38
+ name: this.name ?? '',
39
+ operators: declared.length > 0
40
+ ? declared.map((operator) => ({
41
+ arity: operator.arity ?? 'many',
42
+ default: operator === unwritten,
43
+ label: operator.label,
44
+ name: operator.name ?? '',
45
+ }))
46
+ : DEFAULT_FILTER_OPERATORS,
47
+ values: this.values.map((option) => ({
48
+ label: option.textContent?.trim() || option.value,
49
+ value: option.value,
50
+ })),
51
+ };
52
+ }
53
+ willUpdate(changed) {
54
+ super.willUpdate(changed);
55
+ this.internals.role = 'none';
56
+ }
57
+ render() {
58
+ return nothing;
59
+ }
60
+ };
61
+ __decorate([
62
+ property()
63
+ ], HelixHeadlessFilterField.prototype, "label", void 0);
64
+ __decorate([
65
+ property({ reflect: true })
66
+ ], HelixHeadlessFilterField.prototype, "name", void 0);
67
+ HelixHeadlessFilterField = __decorate([
68
+ customElement('helix-headless-filter-field')
69
+ ], HelixHeadlessFilterField);
70
+ export { HelixHeadlessFilterField };
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Reading a typed filter against what a consumer declared.
3
+ *
4
+ * **Helix owns the punctuation, the consumer owns the words.** `field:a,b` is a
5
+ * filter in every product that has ever had one, and a library that made every
6
+ * consumer write that parse again was charging them for a decision nobody
7
+ * disagrees about. So the shape is here: a key, a colon, a comma separated list,
8
+ * and a leading `-` for the negation. What a key or a value *means* is still
9
+ * never asked, which is the line `FilterField` draws and this does not cross:
10
+ * nothing here compares anything, and what comes out is the same `helix-facet`
11
+ * the picker announces, for the consumer to answer however they answer it.
12
+ *
13
+ * **It reads only what was declared.** A key is a field's `name` or its `label`;
14
+ * a value is an `Option`'s `value` or its text. Anything else is left alone and
15
+ * comes back as text, which is what makes a search box that is also a filter
16
+ * possible: a reader types `refund tuesday disposition:settled` and two thirds
17
+ * of that is still a search.
18
+ *
19
+ * **Where the consumer's vocabulary starts, this stops.** The negation is bound
20
+ * to the operator named `none` and to nothing else, so a field that declares its
21
+ * own comparisons and no `none` has no negation to type: `-created:today` is
22
+ * text, and the picker is where that filter gets built. Guessing which of a
23
+ * consumer's operators a `-` meant is the domain question, and this is the one
24
+ * place it would be tempting to answer.
25
+ *
26
+ * Its own module rather than sitting in the element, because it is string
27
+ * handling with no DOM in it: this is the half worth testing directly.
28
+ */
29
+ import type { FacetDetail } from '@fusion.dev/helix-core';
30
+ /** One comparison a field supports, as the grammar needs it. */
31
+ export interface FilterGrammarOperator {
32
+ /**
33
+ * How many values it takes: `none` is complete on its own, `one` is a scalar,
34
+ * `many` is a set. Undeclared reads as `many`, which `FilterOperator` records.
35
+ */
36
+ arity: 'many' | 'none' | 'one';
37
+ /** Whether this is the one a chip leaves unwritten. */
38
+ default: boolean;
39
+ /** What a reader is shown. */
40
+ label: string;
41
+ /** What the consumer's model calls it. */
42
+ name: string;
43
+ }
44
+ /** One value a field can be compared to, as the grammar needs it. */
45
+ export interface FilterGrammarValue {
46
+ /** What a reader is shown, and one of the two spellings it can be typed as. */
47
+ label: string;
48
+ /** What the consumer's model calls it. */
49
+ value: string;
50
+ }
51
+ /** One field and everything declared under it, as the grammar needs it. */
52
+ export interface FilterGrammarField {
53
+ /** What a reader is shown. */
54
+ label: string;
55
+ /** What the consumer's model calls it. */
56
+ name: string;
57
+ /** The comparisons it supports, the field's own or the defaults. */
58
+ operators: readonly FilterGrammarOperator[];
59
+ /** The values it can be compared to, or none where they cannot be listed. */
60
+ values: readonly FilterGrammarValue[];
61
+ }
62
+ /** One filter read out of the text. */
63
+ export interface FilterGrammarTerm {
64
+ /** What the picker would have announced for this, unchanged. */
65
+ facet: FacetDetail;
66
+ /** The pieces of text it was read from, so a caller can take exactly them. */
67
+ written: readonly string[];
68
+ }
69
+ /** What a line of text turned out to say. */
70
+ export interface FilterGrammarReading {
71
+ /** What it said, one per field. */
72
+ terms: readonly FilterGrammarTerm[];
73
+ /** What it did not, in the order it was written. */
74
+ text: string;
75
+ }
76
+ /**
77
+ * The comparisons a field has when it declares none.
78
+ *
79
+ * **Two, because two is what the punctuation can say.** `field:a,b` and
80
+ * `-field:a,b` are the whole default grammar, so a third default would be one
81
+ * the picker offered and nobody could type. A field wanting `is after` declares
82
+ * it, and declaring anything replaces these rather than joining them: a set half
83
+ * inherited from a library and half written by hand is one nobody can read off
84
+ * the markup.
85
+ */
86
+ export declare const DEFAULT_FILTER_OPERATORS: readonly FilterGrammarOperator[];
87
+ /**
88
+ * Reads a line of text as filters and whatever was not one.
89
+ *
90
+ * **One comparison per field, and a second is left as text.** `helix-facet`
91
+ * carries a field's whole set, so `disposition:a -disposition:b` has no event
92
+ * shape to arrive in and the picker cannot build it either. Announcing the
93
+ * second over the first would drop a filter the reader wrote and spelled
94
+ * correctly; leaving it in the box shows them it was not taken.
95
+ */
96
+ export declare function readFilterText(text: string, fields: readonly FilterGrammarField[]): FilterGrammarReading;