@oscarpalmer/oui 0.37.0 → 0.38.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 (96) hide show
  1. package/dist/css/a11y.css +1 -0
  2. package/dist/css/badge.css +1 -0
  3. package/dist/css/button.css +5 -0
  4. package/dist/css/color-picker.css +377 -0
  5. package/dist/css/color.css +0 -1
  6. package/dist/css/combobox.css +7 -0
  7. package/dist/css/content.css +1 -0
  8. package/dist/css/floatable.css +2 -0
  9. package/dist/css/form.css +7 -0
  10. package/dist/css/listbox.css +2 -0
  11. package/dist/css/message.css +2 -0
  12. package/dist/css/mixins/badge.mixins.css +2 -0
  13. package/dist/css/mixins/button.mixins.css +2 -0
  14. package/dist/css/mixins/message.mixins.css +1 -0
  15. package/dist/css/mixins/misc.mixins.css +2 -0
  16. package/dist/css/mixins/shadow.mixins.css +1 -0
  17. package/dist/css/reset.css +2 -2
  18. package/dist/css/splitter.css +1 -1
  19. package/dist/css/tabs.css +21 -21
  20. package/dist/js/color-picker.d.mts +213 -0
  21. package/dist/js/color-picker.mjs +924 -0
  22. package/dist/js/combobox/dom.combobox.mjs +8 -4
  23. package/dist/js/combobox/event.combobox.mjs +1 -1
  24. package/dist/js/combobox/index.d.mts +1 -1
  25. package/dist/js/combobox/index.mjs +56 -2
  26. package/dist/js/combobox/misc.combobox.d.mts +1 -1
  27. package/dist/js/combobox/misc.combobox.mjs +1 -1
  28. package/dist/js/combobox/state.combobox.d.mts +1 -1
  29. package/dist/js/combobox/state.combobox.mjs +1 -1
  30. package/dist/js/dialog.mjs +2 -2
  31. package/dist/js/floatable/floatable.embedded.mjs +13 -10
  32. package/dist/js/focus-trap/focus-trap.embedded.mjs +3 -3
  33. package/dist/js/{index-EfkUSals.d.mts → index-DJdFlTLm.d.mts} +39 -1
  34. package/dist/js/index.d.mts +3 -2
  35. package/dist/js/index.mjs +4 -3
  36. package/dist/js/internal/draggable.mjs +4 -4
  37. package/dist/js/internal/listbox/listbox.data.mjs +1 -1
  38. package/dist/js/internal/listbox/listbox.dom.mjs +1 -1
  39. package/dist/js/internal/listbox/listbox.event.mjs +1 -1
  40. package/dist/js/internal/listbox/listbox.form.mjs +16 -15
  41. package/dist/js/internal/listbox/listbox.misc.mjs +1 -1
  42. package/dist/js/internal/listbox/listbox.state.mjs +3 -3
  43. package/dist/js/internal/navigable.mjs +4 -3
  44. package/dist/js/listbox/event.listbox.mjs +1 -1
  45. package/dist/js/listbox/index.d.mts +38 -0
  46. package/dist/js/listbox/index.mjs +56 -2
  47. package/dist/js/listbox/misc.listbox.mjs +1 -1
  48. package/dist/js/listbox/state.listbox.mjs +1 -1
  49. package/dist/js/{listbox.data-BWd0PCy7.mjs → listbox.data-C7CRa5Ya.mjs} +5 -4
  50. package/dist/js/menu.mjs +1 -1
  51. package/dist/js/{misc.listbox-BI-XsTOf.mjs → misc.listbox-Cyz2CRDy.mjs} +2 -3
  52. package/dist/js/movable/movable.embedded.mjs +2 -2
  53. package/dist/js/popover.mjs +2 -2
  54. package/dist/js/resizable/resizable.embedded.mjs +5 -5
  55. package/dist/js/sortable/sortable.embedded.mjs +4 -4
  56. package/dist/js/splitter.mjs +6 -6
  57. package/dist/js/{state.combobox-4xq0FPCF.mjs → state.combobox-Qm3FWzYW.mjs} +3 -3
  58. package/dist/js/tabs.mjs +4 -7
  59. package/dist/js/tooltip.mjs +3 -3
  60. package/package.json +7 -5
  61. package/src/css/a11y.scss +2 -2
  62. package/src/css/badge.scss +8 -8
  63. package/src/css/button.scss +8 -8
  64. package/src/css/color-picker.scss +344 -0
  65. package/src/css/color.scss +0 -2
  66. package/src/css/combobox.scss +12 -9
  67. package/src/css/content.scss +11 -10
  68. package/src/css/floatable.scss +6 -3
  69. package/src/css/form.scss +12 -10
  70. package/src/css/listbox.scss +4 -3
  71. package/src/css/message.scss +9 -8
  72. package/src/css/mixins/badge.mixins.scss +53 -0
  73. package/src/css/mixins/button.mixins.scss +112 -0
  74. package/src/css/mixins/input.mixins.scss +114 -0
  75. package/src/css/mixins/message.mixins.scss +41 -0
  76. package/src/css/mixins/misc.mixins.scss +48 -0
  77. package/src/css/mixins/shadow.mixins.scss +57 -0
  78. package/src/css/reset.scss +2 -2
  79. package/src/css/splitter.scss +1 -1
  80. package/src/css/tabs.scss +20 -31
  81. package/src/js/color-picker.ts +1653 -0
  82. package/src/js/combobox/dom.combobox.ts +4 -0
  83. package/src/js/combobox/index.ts +63 -1
  84. package/src/js/dialog.ts +1 -1
  85. package/src/js/floatable/floatable.embedded.ts +18 -14
  86. package/src/js/index.ts +1 -0
  87. package/src/js/internal/listbox/listbox.dom.ts +2 -1
  88. package/src/js/internal/listbox/listbox.form.ts +31 -21
  89. package/src/js/internal/listbox/listbox.state.ts +2 -2
  90. package/src/js/internal/navigable.ts +2 -0
  91. package/src/js/listbox/index.ts +62 -0
  92. package/src/js/listbox/state.listbox.ts +0 -2
  93. package/src/js/popover.ts +1 -1
  94. package/src/js/tabs.ts +4 -10
  95. package/src/css/mixins.scss +0 -419
  96. /package/dist/css/{mixins.css → mixins/input.mixins.css} +0 -0
@@ -73,6 +73,7 @@ export function createComboboxButton(parameters: CreateListboxContentParameters)
73
73
  },
74
74
  property: {
75
75
  textContent: multiple ? '' : (select.selectedOptions[0]?.textContent ?? ''),
76
+ type: 'button',
76
77
  },
77
78
  });
78
79
 
@@ -189,6 +190,9 @@ export function createComboboxSelectionItem(
189
190
  data: {
190
191
  option: option.id,
191
192
  },
193
+ property: {
194
+ type: 'button',
195
+ },
192
196
  });
193
197
 
194
198
  const icon = createElement('span', {
@@ -69,6 +69,20 @@ export class OuiComboboxElement extends HTMLElement {
69
69
  setListboxFilter(this.#state, value);
70
70
  }
71
71
 
72
+ /**
73
+ * Form element associated with the combobox
74
+ */
75
+ get form(): HTMLFormElement | null {
76
+ return this.#internals.form;
77
+ }
78
+
79
+ /**
80
+ * Label elements associated with the combobox
81
+ */
82
+ get labels(): NodeList {
83
+ return this.#internals.labels;
84
+ }
85
+
72
86
  /**
73
87
  * Maximum number of selected options
74
88
  */
@@ -157,6 +171,20 @@ export class OuiComboboxElement extends HTMLElement {
157
171
  return this.#state.type;
158
172
  }
159
173
 
174
+ /**
175
+ * Validation message for the combobox
176
+ */
177
+ get validationMessage(): string {
178
+ return this.#internals.validationMessage;
179
+ }
180
+
181
+ /**
182
+ * Validity state for the combobox
183
+ */
184
+ get validity(): ValidityState {
185
+ return this.#internals.validity;
186
+ }
187
+
160
188
  /**
161
189
  * Current value
162
190
  */
@@ -185,6 +213,13 @@ export class OuiComboboxElement extends HTMLElement {
185
213
  setListboxValues(this.#state, values);
186
214
  }
187
215
 
216
+ /**
217
+ * Can the combobox be validated?
218
+ */
219
+ get willValidate(): boolean {
220
+ return this.#internals.willValidate;
221
+ }
222
+
188
223
  constructor() {
189
224
  super();
190
225
 
@@ -215,6 +250,15 @@ export class OuiComboboxElement extends HTMLElement {
215
250
  }
216
251
  }
217
252
 
253
+ /**
254
+ * Check if the combobox is valid
255
+ *
256
+ * @returns `true` if the combobox is valid, otherwise `false`
257
+ */
258
+ checkValidity(): boolean {
259
+ return this.#internals.checkValidity();
260
+ }
261
+
218
262
  /**
219
263
  * Clear all selected options
220
264
  */
@@ -244,7 +288,7 @@ export class OuiComboboxElement extends HTMLElement {
244
288
  setListboxValues(this.#state, this.#state.values.initial);
245
289
  }
246
290
 
247
- formStateRestoreCallback(state: string | File | FormData, reason: unknown): void {
291
+ formStateRestoreCallback(state: unknown, reason: unknown): void {
248
292
  if (reason !== 'restore') {
249
293
  return;
250
294
  }
@@ -262,6 +306,24 @@ export class OuiComboboxElement extends HTMLElement {
262
306
  }
263
307
  }
264
308
 
309
+ /**
310
+ * Check if the combobox is valid and report any validation errors
311
+ *
312
+ * @returns `true` if the combobox is valid, otherwise `false`
313
+ */
314
+ reportValidity(): boolean {
315
+ return this.#internals.reportValidity();
316
+ }
317
+
318
+ /**
319
+ * Set a custom validation message for the combobox
320
+ *
321
+ * @param message Validation message
322
+ */
323
+ setCustomValidity(message: string): void {
324
+ this.#internals.setValidity({customError: message !== ''}, message, this);
325
+ }
326
+
265
327
  /**
266
328
  * Set combobox localizations for a specific language
267
329
  *
package/src/js/dialog.ts CHANGED
@@ -199,7 +199,7 @@ function getState(element: HTMLDialogElement): OuiDialogState {
199
199
  }
200
200
  }),
201
201
  ],
202
- modal: getAria(element, 'modal') === 'true' || element.matches(':modal'),
202
+ modal: getAria(element, 'modal') === true || element.matches(':modal'),
203
203
  };
204
204
 
205
205
  state.dialog = new OuiDialog(state);
@@ -1,5 +1,5 @@
1
1
  import {isPlainObject} from '@oscarpalmer/atoms/is';
2
- import {getAria} from '@oscarpalmer/toretto/aria';
2
+ import {getAria, setAria} from '@oscarpalmer/toretto/aria';
3
3
  import {getAttribute, setAttribute} from '@oscarpalmer/toretto/attribute';
4
4
  import {on} from '@oscarpalmer/toretto/event';
5
5
  import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
@@ -45,18 +45,7 @@ export class OuiFloatable {
45
45
  constructor(state: OuiFloatableState, standalone: boolean) {
46
46
  this.#state = state;
47
47
 
48
- const {anchor, content} = state;
49
-
50
- if (standalone) {
51
- setAttribute(content, ATTRIBUTE_FLOATABLE, '');
52
-
53
- content.popover = 'manual';
54
- } else {
55
- content.popover ??= 'auto';
56
- }
57
-
58
- setStyle(anchor, 'anchorName', state.name);
59
- setStyle(content, 'positionAnchor', state.name);
48
+ this.#initialize(state, standalone);
60
49
  }
61
50
 
62
51
  /**
@@ -103,6 +92,21 @@ export class OuiFloatable {
103
92
  setPosition(this.#state, position);
104
93
  }
105
94
  }
95
+
96
+ #initialize(state: OuiFloatableState, standalone: boolean): void {
97
+ const {anchor, content} = state;
98
+
99
+ if (standalone) {
100
+ setAttribute(content, ATTRIBUTE_FLOATABLE, '');
101
+
102
+ content.popover = 'manual';
103
+ } else {
104
+ content.popover ??= 'auto';
105
+ }
106
+
107
+ setStyle(anchor, 'anchorName', state.name);
108
+ setStyle(content, 'positionAnchor', state.name);
109
+ }
106
110
  }
107
111
 
108
112
  export type OuiFloatableOptions = {
@@ -220,7 +224,7 @@ export function getOnBeforeToggleListener(element: HTMLElement): RemovableEventL
220
224
  event => {
221
225
  const {newState, source} = event;
222
226
 
223
- if (newState === 'open' && source != null && getAria(source, 'disabled') === 'true') {
227
+ if (newState === 'open' && source != null && getAria(source, 'disabled') === true) {
224
228
  event.preventDefault();
225
229
  }
226
230
  },
package/src/js/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import './menu';
2
2
  import './toolbar';
3
3
 
4
+ export * from './color-picker';
4
5
  export * from './combobox/index';
5
6
  export * from './dialog';
6
7
  export * from './floatable/floatable.standalone';
@@ -199,13 +199,14 @@ export function createListboxInput(
199
199
  autocorrect: 'off',
200
200
  autocomplete: 'off',
201
201
  form: '',
202
+ inputmode: 'text',
202
203
  spellcheck: 'false',
203
- type: 'text',
204
204
  },
205
205
  property: {
206
206
  id,
207
207
  value,
208
208
  role: isAutocomplete || listbox ? 'combobox' : 'searchbox',
209
+ type: 'text',
209
210
  },
210
211
  });
211
212
 
@@ -37,19 +37,33 @@ export function updateListboxForm<State extends OuiListboxState>(
37
37
  }
38
38
 
39
39
  export function updateListboxValidity(state: OuiListboxState): void {
40
- if (state.autocomplete.none && !state.listbox) {
41
- const valueMissing = state.required && state.filter.element.value.length === 0;
40
+ const {
41
+ autocomplete,
42
+ filter,
43
+ host,
44
+ internals,
45
+ listbox,
46
+ localizations,
47
+ max,
48
+ min,
49
+ multiple,
50
+ options,
51
+ required,
52
+ } = state;
53
+
54
+ if (autocomplete.none && !listbox) {
55
+ const valueMissing = required && filter.element.value.length === 0;
42
56
 
43
57
  const localization = getLocalization(
44
- state.localizations.global,
45
- state.host,
58
+ localizations.global,
59
+ host,
46
60
  'invalid',
47
- state.localizations.component,
61
+ localizations.component,
48
62
  );
49
63
 
50
- state.internals.setValidity({valueMissing}, localize(localization, {}, []));
64
+ internals.setValidity({valueMissing}, localize(localization, {}, []));
51
65
 
52
- state.internals.checkValidity();
66
+ internals.checkValidity();
53
67
 
54
68
  return;
55
69
  }
@@ -60,31 +74,27 @@ export function updateListboxValidity(state: OuiListboxState): void {
60
74
  let parameters: unknown[] = [];
61
75
 
62
76
  switch (true) {
63
- case state.required && state.options.selected.length === 0: {
77
+ case required && options.selected.length === 0: {
64
78
  error = 'badInput';
65
79
  key = 'input';
66
80
 
67
81
  break;
68
82
  }
69
83
 
70
- case state.multiple &&
71
- typeof state.max === 'number' &&
72
- state.options.selected.length > state.max: {
73
- data = {amount: state.max};
84
+ case multiple && typeof max === 'number' && options.selected.length > max: {
85
+ data = {amount: max};
74
86
  error = 'rangeOverflow';
75
87
  key = 'max';
76
- parameters = [state.max];
88
+ parameters = [max];
77
89
 
78
90
  break;
79
91
  }
80
92
 
81
- case state.multiple &&
82
- typeof state.min === 'number' &&
83
- state.options.selected.length < state.min: {
84
- data = {amount: state.min};
93
+ case multiple && typeof min === 'number' && options.selected.length < min: {
94
+ data = {amount: min};
85
95
  error = 'rangeUnderflow';
86
96
  key = 'min';
87
- parameters = [state.min];
97
+ parameters = [min];
88
98
 
89
99
  break;
90
100
  }
@@ -96,11 +106,11 @@ export function updateListboxValidity(state: OuiListboxState): void {
96
106
  const localization =
97
107
  key == null
98
108
  ? undefined
99
- : getLocalization(state.localizations.global, state.host, key, state.localizations.component);
109
+ : getLocalization(localizations.global, host, key, localizations.component);
100
110
 
101
111
  const message = localization == null ? undefined : localize(localization, data, parameters);
102
112
 
103
- state.internals.setValidity(
113
+ internals.setValidity(
104
114
  error == null
105
115
  ? {}
106
116
  : {
@@ -109,7 +119,7 @@ export function updateListboxValidity(state: OuiListboxState): void {
109
119
  message,
110
120
  );
111
121
 
112
- state.internals.checkValidity();
122
+ internals.checkValidity();
113
123
  }
114
124
 
115
125
  // #endregion
@@ -18,10 +18,10 @@ export function attachListbox<State extends OuiListboxState>(
18
18
  states: OuiListboxStates<State>,
19
19
  onAttach: (state: State) => void,
20
20
  ): void {
21
- const {filter, host, label} = state;
21
+ const {filter, host, id, label} = state;
22
22
 
23
23
  label.element.htmlFor = filter.element.id;
24
- label.element.id = `${state.id}_label`;
24
+ label.element.id = `${id}_label`;
25
25
 
26
26
  onAttach(state);
27
27
 
@@ -231,6 +231,8 @@ function onKeydown(navigable: OuiNavigable, event: KeyboardEvent): void {
231
231
  return;
232
232
  }
233
233
 
234
+ event.preventDefault();
235
+
234
236
  let index: number | undefined;
235
237
 
236
238
  if (KEYS_ABSOLUTE.has(event.key)) {
@@ -63,6 +63,20 @@ export class OuiListboxElement extends HTMLElement {
63
63
  setListboxFilter(this.#state, value);
64
64
  }
65
65
 
66
+ /**
67
+ * Form element associated with the listbox
68
+ */
69
+ get form(): HTMLFormElement | null {
70
+ return this.#internals.form;
71
+ }
72
+
73
+ /**
74
+ * Label elements associated with the listbox
75
+ */
76
+ get labels(): NodeList {
77
+ return this.#internals.labels;
78
+ }
79
+
66
80
  /**
67
81
  * Maximum number of selected options
68
82
  */
@@ -144,6 +158,20 @@ export class OuiListboxElement extends HTMLElement {
144
158
  return this.#state.options.selected.slice();
145
159
  }
146
160
 
161
+ /**
162
+ * Validation message for the listbox
163
+ */
164
+ get validationMessage(): string {
165
+ return this.#internals.validationMessage;
166
+ }
167
+
168
+ /**
169
+ * Validity state for the listbox
170
+ */
171
+ get validity(): ValidityState {
172
+ return this.#internals.validity;
173
+ }
174
+
147
175
  /**
148
176
  * Current value
149
177
  */
@@ -172,6 +200,13 @@ export class OuiListboxElement extends HTMLElement {
172
200
  setListboxValues(this.#state, values);
173
201
  }
174
202
 
203
+ /**
204
+ * Can the listbox be validated?
205
+ */
206
+ get willValidate(): boolean {
207
+ return this.#internals.willValidate;
208
+ }
209
+
175
210
  constructor() {
176
211
  super();
177
212
 
@@ -202,6 +237,15 @@ export class OuiListboxElement extends HTMLElement {
202
237
  }
203
238
  }
204
239
 
240
+ /**
241
+ * Check if the listbox is valid
242
+ *
243
+ * @returns `true` if the listbox is valid, otherwise `false`
244
+ */
245
+ checkValidity(): boolean {
246
+ return this.#internals.checkValidity();
247
+ }
248
+
205
249
  /**
206
250
  * Clear all selected options
207
251
  */
@@ -249,6 +293,24 @@ export class OuiListboxElement extends HTMLElement {
249
293
  }
250
294
  }
251
295
 
296
+ /**
297
+ * Check if the listbox is valid and report any validation errors
298
+ *
299
+ * @returns `true` if the listbox is valid, otherwise `false`
300
+ */
301
+ reportValidity(): boolean {
302
+ return this.#internals.reportValidity();
303
+ }
304
+
305
+ /**
306
+ * Set a custom validation message for the listbox
307
+ *
308
+ * @param message Validation message
309
+ */
310
+ setCustomValidity(message: string): void {
311
+ this.#internals.setValidity({customError: message !== ''}, message, this);
312
+ }
313
+
252
314
  /**
253
315
  * Set listbox localizations for a specific language
254
316
  *
@@ -43,8 +43,6 @@ export function createListboxState(
43
43
 
44
44
  element.id = select.id;
45
45
 
46
- setAttribute(element, 'name', select.name === '' ? select.id : select.name);
47
-
48
46
  const disabled = getBooleanAriaValue(element, 'disabled') || select.disabled;
49
47
  const multiple = element.multiple || select.multiple;
50
48
  const readonly = getBooleanAriaValue(element, 'readonly');
package/src/js/popover.ts CHANGED
@@ -125,7 +125,7 @@ function getState(anchor: HTMLElement, content: HTMLElement): OuiPopoverState {
125
125
  content,
126
126
  floatable: createEmbeddedFloatable(anchor, content, options),
127
127
  focusTrap:
128
- getAria(content, 'modal') === 'true'
128
+ getAria(content, 'modal') === true
129
129
  ? createFocusTrap(content, {
130
130
  noescape: true,
131
131
  })
package/src/js/tabs.ts CHANGED
@@ -144,7 +144,7 @@ function attachTabs(state: OuiTabsState): void {
144
144
  function createContent(prefix: string, panels: HTMLElement[], active: number): OuiTabsContent {
145
145
  const content = createElement('div', {
146
146
  attribute: {
147
- [ATTRIBUTE_CONTENT]: '',
147
+ [`${TAGNAME}-content`]: '',
148
148
  },
149
149
  });
150
150
 
@@ -189,7 +189,7 @@ function createTab(
189
189
  controls: panel.id,
190
190
  },
191
191
  attribute: {
192
- [ATTRIBUTE_TAB]: '',
192
+ [`${TAGNAME}-tab`]: '',
193
193
  },
194
194
  property: {
195
195
  id: `${prefix}_tab_${index}`,
@@ -209,7 +209,7 @@ function createTabList(
209
209
  ): OuiTabsTabList {
210
210
  const element = createElement('div', {
211
211
  attribute: {
212
- [ATTRIBUTE_TABLIST]: '',
212
+ [`${TAGNAME}-tablist`]: '',
213
213
  },
214
214
  property: {
215
215
  role: 'tablist',
@@ -420,21 +420,15 @@ function setActive(state: OuiTabsState | undefined, value: unknown): void {
420
420
 
421
421
  const TAGNAME = 'oui-tabs';
422
422
 
423
- const ATTRIBUTE_CONTENT = `${TAGNAME}-content`;
424
-
425
423
  const ATTRIBUTE_PANEL = `${TAGNAME}-panel`;
426
424
 
427
- const ATTRIBUTE_TAB = `${TAGNAME}-tab`;
428
-
429
- const ATTRIBUTE_TABLIST = `${TAGNAME}-tablist`;
430
-
431
425
  const MESSAGE_EMPTY = `<${TAGNAME}> must contain at least one [${ATTRIBUTE_PANEL}] child element`;
432
426
 
433
427
  const MESSAGE_INVALID = `A [${ATTRIBUTE_PANEL}] child element of <${TAGNAME}> must have a valid 'aria-label' or 'aria-labelledby' attribute`;
434
428
 
435
429
  const SELECTOR_PANEL = `:scope > [${ATTRIBUTE_PANEL}]`;
436
430
 
437
- const SELECTOR_TAB = `[${ATTRIBUTE_TAB}]`;
431
+ const SELECTOR_TAB = `[${TAGNAME}-tab]`;
438
432
 
439
433
  let index = 0;
440
434