@itenthusiasm/custom-elements 0.0.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.
@@ -0,0 +1,100 @@
1
+ select-enhancer {
2
+ --blocks: 5; /* Number of option blocks to display before needing to scroll */
3
+
4
+ position: relative;
5
+ display: inline-block;
6
+ box-sizing: border-box;
7
+ width: inherit;
8
+ height: inherit;
9
+
10
+ @media only screen and (min-width: 600px) {
11
+ --blocks: 10;
12
+ }
13
+
14
+ & > [role="combobox"] {
15
+ --border-width: 1.5px;
16
+
17
+ display: block;
18
+ box-sizing: border-box;
19
+ width: 100%;
20
+ height: 100%;
21
+ padding: 8px;
22
+ border: var(--border-width) solid #cecece;
23
+ border-radius: 4px;
24
+ outline: none;
25
+ overflow-x: auto;
26
+
27
+ cursor: pointer;
28
+ white-space: pre;
29
+ text-align: center;
30
+ font-size: inherit;
31
+ font-family: inherit;
32
+ color: currentcolor;
33
+ background-color: white;
34
+
35
+ &[filter] {
36
+ cursor: auto;
37
+ }
38
+
39
+ &:focus {
40
+ border-color: dodgerblue;
41
+ }
42
+
43
+ & + [role="listbox"] {
44
+ --listbox-border: 2px;
45
+ --option-height: 38px;
46
+ --option-padding: 8px;
47
+
48
+ position: absolute;
49
+ z-index: 2;
50
+
51
+ box-sizing: border-box;
52
+ width: 100%;
53
+ max-height: calc(var(--option-height) * var(--blocks) + var(--listbox-border) * 2);
54
+ padding: 0;
55
+ border: var(--listbox-border) solid #cecece;
56
+ border-radius: 4px;
57
+ margin: 0;
58
+
59
+ overflow: auto;
60
+ background-color: white;
61
+
62
+ &:is([role="combobox"]:not([aria-expanded="true"]) + [role="listbox"]) {
63
+ display: none;
64
+ }
65
+
66
+ & > [role="option"],
67
+ &:where([role="combobox"][data-bad-filter] + [role="listbox"])::after {
68
+ display: block;
69
+ box-sizing: border-box;
70
+ height: var(--option-height);
71
+ padding: var(--option-padding);
72
+ cursor: pointer;
73
+
74
+ &[data-active="true"]:not([aria-selected="true"]) {
75
+ background-color: #bddaff; /* `background-color` for `selected` items, brightened by 70% */
76
+ }
77
+
78
+ &[aria-selected="true"] {
79
+ color: white;
80
+ background-color: #2684ff;
81
+ }
82
+
83
+ &[data-filtered-out] {
84
+ display: none;
85
+ visibility: hidden; /* Needed to hide filtered-out `option`s in Safari + VoiceOver (Bug in Browser) */
86
+ }
87
+
88
+ /* TODO: Add clearer `disabled` styles. */
89
+ &[aria-disabled="true"] {
90
+ cursor: auto;
91
+ }
92
+ }
93
+
94
+ &:where([role="combobox"][data-bad-filter] + [role="listbox"])::after {
95
+ content: attr(nomatchesmessage, "No options found") / "";
96
+ cursor: auto;
97
+ }
98
+ }
99
+ }
100
+ }
@@ -0,0 +1,243 @@
1
+ export default ComboboxField;
2
+ export type ExposedInternals = Pick<ElementInternals, "labels" | "form" | "validity" | "validationMessage" | "willValidate" | "checkValidity" | "reportValidity">;
3
+ export type FieldPropertiesAndMethods = Pick<HTMLInputElement, "name" | "required" | "disabled" | "setCustomValidity">;
4
+ /**
5
+ * @typedef {Pick<ElementInternals,
6
+ "labels" | "form" | "validity" | "validationMessage" | "willValidate" | "checkValidity" | "reportValidity"
7
+ >} ExposedInternals
8
+ */
9
+ /**
10
+ * @typedef {Pick<HTMLInputElement, "name" | "required" | "disabled" | "setCustomValidity">} FieldPropertiesAndMethods
11
+ */
12
+ /** @implements {ExposedInternals} @implements {FieldPropertiesAndMethods} */
13
+ declare class ComboboxField extends HTMLElement implements ExposedInternals, FieldPropertiesAndMethods {
14
+ /** @returns {true} */
15
+ static get formAssociated(): true;
16
+ static get observedAttributes(): readonly ["id", "required", "filter", "valueis", "nomatchesmessage", "valuemissingerror"];
17
+ /** Internally used to indicate when the `combobox` is actively transitioning out of {@link filter} mode. */
18
+ static "__#private@#filterDisabedKey": symbol;
19
+ static defaultNoMatchesMessage: string;
20
+ /**
21
+ * @param {MouseEvent} event
22
+ * @returns {void}
23
+ */
24
+ static "__#private@#handleClick"(event: MouseEvent): void;
25
+ /**
26
+ * Used to determine if a {@link filter filterable} `combobox` was `:focus`ed by a `click` event.
27
+ * @param {MouseEvent} event
28
+ * @returns {void}
29
+ */
30
+ static "__#private@#handleMousedown"(event: MouseEvent): void;
31
+ /**
32
+ * (For {@link filter filtered} `combobox`es only)
33
+ * @param {FocusEvent} event
34
+ * @returns {void}
35
+ */
36
+ static "__#private@#handleFocus"(event: FocusEvent): void;
37
+ /**
38
+ * @param {FocusEvent} event
39
+ * @returns {void}
40
+ */
41
+ static "__#private@#handleBlur"(event: FocusEvent): void;
42
+ /**
43
+ * @param {MouseEvent} event
44
+ * @returns {void}
45
+ */
46
+ static "__#private@#handleDelegatedOptionHover"(event: MouseEvent): void;
47
+ /**
48
+ * @param {MouseEvent} event
49
+ * @returns {void}
50
+ */
51
+ static "__#private@#handleDelegatedOptionClick"(event: MouseEvent): void;
52
+ /**
53
+ * @param {MouseEvent} event
54
+ * @returns {void}
55
+ */
56
+ static "__#private@#handleDelegatedMousedown"(event: MouseEvent): void;
57
+ /**
58
+ * @param {MutationRecord[]} mutations
59
+ * @returns {void}
60
+ */
61
+ static "__#private@#preserveTextNode"(mutations: MutationRecord[]): void;
62
+ /**
63
+ * @param {MutationRecord[]} mutations
64
+ * @returns {void}
65
+ */
66
+ static "__#private@#watchActiveDescendant"(mutations: MutationRecord[]): void;
67
+ /**
68
+ * @param {typeof ComboboxField.observedAttributes[number]} name
69
+ * @param {string | null} oldValue
70
+ * @param {string | null} newValue
71
+ * @returns {void}
72
+ */
73
+ attributeChangedCallback(name: (typeof ComboboxField.observedAttributes)[number], oldValue: string | null, newValue: string | null): void;
74
+ /** @param {string} v */
75
+ set value(v: string);
76
+ /** Sets or retrieves the `value` of the `combobox` @returns {string | null} */
77
+ get value(): string | null;
78
+ /** "On Mount" for Custom Elements @returns {void} */
79
+ connectedCallback(): void;
80
+ set noMatchesMessage(value: string);
81
+ /**
82
+ * The message displayed to users when none of the `combobox`'s `option`s match their filter.
83
+ * @returns {string}
84
+ */
85
+ get noMatchesMessage(): string;
86
+ /** "On Unmount" for Custom Elements @returns {void} */
87
+ disconnectedCallback(): void;
88
+ /**
89
+ * Updates the {@link ComboboxOption.filteredOut `filteredOut`} property for all of the `option`s,
90
+ * then returns the `option`s that match the user's current filter.
91
+ *
92
+ * @returns {GetFilteredOptionsReturnType}
93
+ */
94
+ getFilteredOptions(): {
95
+ /**
96
+ * The `option`s which match the user's current filter
97
+ */
98
+ matchingOptions: ComboboxOption[];
99
+ /**
100
+ * (Optional): The `option` which is a candidate for
101
+ * automatic selection. See: {@link ComboboxField.autoselectableOption}.
102
+ */
103
+ autoselectableOption?: ComboboxOption | undefined;
104
+ };
105
+ /**
106
+ * @typedef GetFilteredOptionsReturnType
107
+ * @property {ComboboxOption[]} matchingOptions The `option`s which match the user's current filter
108
+ * @property {ComboboxOption} [autoselectableOption] (Optional): The `option` which is a candidate for
109
+ * automatic selection. See: {@link ComboboxField.autoselectableOption}.
110
+ */
111
+ /**
112
+ * The logic used by {@link filter filterable} `combobox`es to determine if an `option` matches the user's filter.
113
+ *
114
+ * **Note**: If {@link getFilteredOptions} is overridden, this method will do nothing unless it is
115
+ * used directly within the new implementation.
116
+ *
117
+ * @param {ComboboxOption} option
118
+ * @returns {boolean}
119
+ */
120
+ optionMatchesFilter(option: ComboboxOption): boolean;
121
+ /**
122
+ * Coerces the value and filter of the `combobox` to an empty string, and deselects the currently-selected `option`
123
+ * if one exists (including any `option` whose value is an empty string).
124
+ *
125
+ * @returns {void}
126
+ * @throws {TypeError} if the `combobox` is not {@link filter filterable}, or if its value
127
+ * {@link valueIs cannot be cleared}.
128
+ */
129
+ forceEmptyValue(): void;
130
+ /**
131
+ * Retrieves the `option` with the provided `value` (if it exists)
132
+ * @param {string} value
133
+ * @returns {ComboboxOption | null}
134
+ */
135
+ getOptionByValue(value: string): ComboboxOption | null;
136
+ set name(value: HTMLInputElement["name"]);
137
+ /** @returns {HTMLInputElement["name"]} */
138
+ get name(): HTMLInputElement["name"];
139
+ set disabled(value: HTMLInputElement["disabled"]);
140
+ /** @returns {HTMLInputElement["disabled"]} */
141
+ get disabled(): HTMLInputElement["disabled"];
142
+ set required(value: HTMLInputElement["required"]);
143
+ /** @returns {HTMLInputElement["required"]} */
144
+ get required(): HTMLInputElement["required"];
145
+ /**
146
+ * The `listbox` that this `combobox` controls.
147
+ * @returns {ListboxWithChildren<ComboboxOption>}
148
+ */
149
+ get listbox(): ListboxWithChildren<ComboboxOption>;
150
+ /**
151
+ * The _singular_ {@link Text} Node associated with the `combobox`.
152
+ *
153
+ * To alter the `combobox`'s text content, update this node **_instead of_** using {@link textContent}.
154
+ * @returns {Text}
155
+ */
156
+ get text(): Text;
157
+ set filter(value: boolean);
158
+ /** Activates a textbox that can be used to filter the list of `combobox` `option`s. @returns {boolean} */
159
+ get filter(): boolean;
160
+ set filterMethod(value: Extract<number | typeof Symbol.iterator | "length" | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "valueOf" | "codePointAt" | "includes" | "endsWith" | "normalize" | "repeat" | "startsWith" | "anchor" | "big" | "blink" | "bold" | "fixed" | "fontcolor" | "fontsize" | "italics" | "link" | "small" | "strike" | "sub" | "sup" | "padStart" | "padEnd" | "trimEnd" | "trimStart" | "trimLeft" | "trimRight" | "matchAll" | "replaceAll" | "at" | "isWellFormed" | "toWellFormed", "startsWith" | "includes">);
161
+ /**
162
+ * Determines the method used to filter the `option`s as the user types.
163
+ * - `startsWith`: {@link String.startsWith} will be used to filter the `option`s.
164
+ * - `includes`: {@link String.includes} will be used to filter the `option`s.
165
+ *
166
+ * **Note**: This property does nothing if {@link optionMatchesFilter} or {@link getFilteredOptions} is overridden.
167
+ *
168
+ * @returns {Extract<keyof String, "startsWith" | "includes">}
169
+ */
170
+ get filterMethod(): Extract<number | typeof Symbol.iterator | "length" | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "valueOf" | "codePointAt" | "includes" | "endsWith" | "normalize" | "repeat" | "startsWith" | "anchor" | "big" | "blink" | "bold" | "fixed" | "fontcolor" | "fontsize" | "italics" | "link" | "small" | "strike" | "sub" | "sup" | "padStart" | "padEnd" | "trimEnd" | "trimStart" | "trimLeft" | "trimRight" | "matchAll" | "replaceAll" | "at" | "isWellFormed" | "toWellFormed", "startsWith" | "includes">;
171
+ set valueIs(value: "unclearable" | "clearable" | "anyvalue");
172
+ /**
173
+ * Indicates how a `combobox`'s value will behave.
174
+ * - `unclearable`: The field's {@link value `value`} must be a string matching one of the `option`s,
175
+ * and it cannot be cleared. (Default when {@link filter `filter`} mode is off.)
176
+ * - `clearable`: The field's `value` must be a string matching one of the `option`s,
177
+ * but it can be cleared. (Default in `filter` mode. Requires enabling `filter` mode.)
178
+ * - `anyvalue`: The field's `value` can be any string, and it will automatically be set to
179
+ * whatever value the user types. (Requires enabling `filter` mode.)
180
+ *
181
+ * <!--
182
+ * TODO: Link to Documentation for More Details (like TS does for MDN). The deeper details of the behavior
183
+ * are too sophisticated to place them all in a JSDoc, which should be [sufficiently] clear and succinct
184
+ * -->
185
+ *
186
+ * @returns {"unclearable" | "clearable" | "anyvalue"}
187
+ */
188
+ get valueIs(): "unclearable" | "clearable" | "anyvalue";
189
+ /**
190
+ * @param {string} value
191
+ * @returns {boolean} `true` if the `combobox` will accept the provided `value` when no corresponding `option` exists.
192
+ * Otherwise, returns `false`.
193
+ */
194
+ acceptsValue(value: string): boolean;
195
+ /**
196
+ * Returns the `option` whose `label` matches the user's most recent filter input (if one exists).
197
+ *
198
+ * Value will be `null` if:
199
+ * - The user's filter didn't match any `option`s
200
+ * - The `combobox`'s text content was altered by a `value` change
201
+ * - The `combobox` was just recently expanded
202
+ * @returns {ComboboxOption | null}
203
+ */
204
+ get autoselectableOption(): ComboboxOption | null;
205
+ set valueMissingError(value: string);
206
+ /** The error message displayed to users when the `combobox`'s `required` constraint is broken. @returns {string} */
207
+ get valueMissingError(): string;
208
+ /** @returns {ElementInternals["labels"]} */
209
+ get labels(): ElementInternals["labels"];
210
+ /** @returns {ElementInternals["form"]} */
211
+ get form(): ElementInternals["form"];
212
+ /** @returns {ElementInternals["validity"]} */
213
+ get validity(): ElementInternals["validity"];
214
+ /** @returns {ElementInternals["validationMessage"]} */
215
+ get validationMessage(): ElementInternals["validationMessage"];
216
+ /** @returns {ElementInternals["willValidate"]} */
217
+ get willValidate(): ElementInternals["willValidate"];
218
+ checkValidity(): boolean;
219
+ reportValidity(): boolean;
220
+ setCustomValidity(error: string): void;
221
+ /** @returns {void} */
222
+ formResetCallback(): void;
223
+ /**
224
+ * @param {string} state
225
+ * @param {"restore" | "autocomplete"} _mode
226
+ * @returns {void}
227
+ */
228
+ formStateRestoreCallback(state: string, _mode: "restore" | "autocomplete"): void;
229
+ /**
230
+ * @param {boolean} disabled
231
+ * @returns {void}
232
+ */
233
+ formDisabledCallback(disabled: boolean): void;
234
+ /** @private @type {string | null} */ private [valueOnFocusKey];
235
+ /** @private @type {boolean} */ private [editingKey];
236
+ #private;
237
+ }
238
+ import ComboboxOption from "./ComboboxOption.js";
239
+ import type { ListboxWithChildren } from "./types/helpers.js";
240
+ /** Internally used to retrieve the value that the `combobox` had when it was focused. */
241
+ declare const valueOnFocusKey: unique symbol;
242
+ /** Internally used to determine if the `combobox`'s value is actively being modified through user's filter changes. */
243
+ declare const editingKey: unique symbol;