vanilla-jui 1.4.1 → 1.4.2

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 (44) hide show
  1. package/dist/index.d.ts +7 -14
  2. package/dist/index.js +1 -1
  3. package/dist/index.umd.js +1 -1
  4. package/dist/style.css +1 -1
  5. package/package.json +1 -1
  6. package/src/components/accordion.ts +7 -10
  7. package/src/components/drop.ts +5 -15
  8. package/src/components/parabola.ts +2 -2
  9. package/src/components/tabs.ts +5 -25
  10. package/src/components/theme.ts +25 -22
  11. package/src/components/toast.ts +34 -19
  12. package/src/components/tooltip.ts +3 -9
  13. package/src/core/Component.ts +1 -1
  14. package/src/css/accordion.css +1 -1
  15. package/src/css/components.css +1 -0
  16. package/src/css/content.css +147 -151
  17. package/src/css/drop.css +1 -1
  18. package/src/css/editor.css +16 -0
  19. package/src/css/form.css +27 -14
  20. package/src/css/icon.css +3 -3
  21. package/src/css/index.css +2 -2
  22. package/src/css/layout.css +2 -2
  23. package/src/css/loading.css +77 -29
  24. package/src/css/menu.css +29 -32
  25. package/src/css/pagination.css +4 -4
  26. package/src/css/status.css +2 -1
  27. package/src/css/table.css +20 -20
  28. package/src/css/tabs.css +2 -2
  29. package/src/css/theme-palette.css +10 -17
  30. package/src/css/themes/default/accordion.css +4 -4
  31. package/src/css/themes/default/avatar.css +17 -1
  32. package/src/css/themes/default/content.css +59 -0
  33. package/src/css/themes/default/editor.css +10 -0
  34. package/src/css/themes/default/index.css +8 -1
  35. package/src/css/themes/default/loading.css +16 -16
  36. package/src/css/themes/default/menu.css +15 -0
  37. package/src/css/themes/default/pagination.css +8 -0
  38. package/src/css/themes/default/table.css +18 -0
  39. package/src/css/themes/default/tabs.css +2 -2
  40. package/src/css/themes/default/theme-palette.css +12 -0
  41. package/src/css/themes/default/toolbar.css +34 -0
  42. package/src/css/toast.css +11 -9
  43. package/src/css/toolbar.css +29 -48
  44. package/src/locales/index.ts +3 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vanilla-jui",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "private": false,
5
5
  "description": "A lightweight vanilla JavaScript UI plugin library.",
6
6
  "keywords": [
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  bindAttr,
3
- bindClass,
4
3
  createDeepStore,
5
4
  createRoot,
6
5
  flushSync,
@@ -36,7 +35,6 @@ export interface AccordionClassNames {
36
35
  arrow: string;
37
36
  panel: string;
38
37
  content: string;
39
- active: string;
40
38
  }
41
39
 
42
40
  export type AccordionClassNameConfig = Partial<AccordionClassNames>;
@@ -107,7 +105,6 @@ const DEFAULT_CLASS_NAMES: AccordionClassNames = {
107
105
  arrow: 'header-arrow',
108
106
  panel: 'accordion-panel',
109
107
  content: 'panel-content',
110
- active: 'is-active',
111
108
  };
112
109
 
113
110
  const ACCORDION_ITEMS_RULE = {
@@ -316,18 +313,18 @@ export class Accordion extends Component<
316
313
  this.bindingsDispose = createRoot((dispose) => {
317
314
  this.dom.headers.forEach((header) => {
318
315
  const name = header.dataset.accordionHeader || '';
319
- bindClass(header, this.props.className.active, () =>
320
- this.state.activeNames.includes(name)
321
- );
316
+ // bindClass(header, this.props.className.active, () =>
317
+ // this.state.activeNames.includes(name)
318
+ // );
322
319
  bindAttr(header, 'aria-expanded', () =>
323
- this.state.activeNames.includes(name)
320
+ this.state.activeNames.includes(name) ? 'true' : 'false'
324
321
  );
325
322
  });
326
323
  this.dom.panels.forEach((panel, i) => {
327
324
  const name = this.dom.headers[i]?.dataset.accordionHeader || '';
328
- bindClass(panel, this.props.className.active, () =>
329
- this.state.activeNames.includes(name)
330
- );
325
+ // bindClass(panel, this.props.className.active, () =>
326
+ // this.state.activeNames.includes(name)
327
+ // );
331
328
  bindAttr(panel, 'aria-hidden', () =>
332
329
  this.state.activeNames.includes(name) ? 'false' : 'true'
333
330
  );
@@ -30,7 +30,6 @@ export type DropPosition =
30
30
  export interface DropClassNames {
31
31
  root: string;
32
32
  container: string;
33
- active: string;
34
33
  }
35
34
 
36
35
  export type DropClassNameConfig = Partial<DropClassNames>;
@@ -48,7 +47,6 @@ export interface DropProps extends Record<string, unknown> {
48
47
  content?: RenderableContent<Drop>;
49
48
  className?: DropClassNameConfig;
50
49
  id?: string | null;
51
- containerClassName?: string | null;
52
50
  delay?: number | DropDelay;
53
51
  hoverIntent?: boolean;
54
52
  onShown?: ((drop: Drop) => void | Promise<void>) | null;
@@ -63,7 +61,6 @@ interface ResolvedDropProps extends Record<string, unknown> {
63
61
  content: RenderableContent<Drop>;
64
62
  className: DropClassNames;
65
63
  id: string;
66
- containerClassName: string | null;
67
64
  delay: number | DropDelay;
68
65
  hoverIntent: boolean;
69
66
  onShown: NonNullable<DropProps['onShown']> | null;
@@ -88,7 +85,6 @@ interface HoverIntentData {
88
85
  const DEFAULT_CLASS_NAMES: DropClassNames = {
89
86
  root: 'j-drop',
90
87
  container: 'drop-container',
91
- active: 'is-active',
92
88
  };
93
89
 
94
90
  const DROP_PROPS_SCHEMA = {
@@ -140,7 +136,6 @@ const DROP_PROPS_SCHEMA = {
140
136
  return value;
141
137
  },
142
138
  },
143
- containerClassName: { default: null, types: ['string', 'null'] },
144
139
  delay: { default: 0, types: ['number', 'object'] },
145
140
  hoverIntent: { default: true, type: 'boolean' },
146
141
  onShown: { default: null, types: ['function', 'null'] },
@@ -157,7 +152,6 @@ function normalizeProps(input: DropProps): ResolvedDropProps {
157
152
  content: props.content as RenderableContent<Drop>,
158
153
  className: props.className as DropClassNames,
159
154
  id: props.id as string,
160
- containerClassName: props.containerClassName as string | null,
161
155
  delay: props.delay as number | DropDelay,
162
156
  hoverIntent: props.hoverIntent as boolean,
163
157
  onShown: props.onShown as ResolvedDropProps['onShown'],
@@ -165,12 +159,6 @@ function normalizeProps(input: DropProps): ResolvedDropProps {
165
159
  };
166
160
  }
167
161
 
168
- function joinClasses(
169
- ...classes: Array<string | null | undefined | false>
170
- ): string {
171
- return classes.filter(Boolean).join(' ');
172
- }
173
-
174
162
  function normalizeDelay(delay: number | DropDelay): Required<DropDelay> {
175
163
  if (typeof delay === 'number' && delay >= 0) {
176
164
  return { show: delay, hide: delay };
@@ -232,12 +220,12 @@ export class Drop {
232
220
  }
233
221
 
234
222
  private buildDrop(props: ResolvedDropProps): void {
235
- const { className, content, id, name, containerClassName } = props;
223
+ const { className, content, id, name } = props;
236
224
  const wrapper =
237
225
  isNode(content) && content.nodeType === Node.ELEMENT_NODE
238
226
  ? content
239
227
  : jsx('div', {
240
- className: joinClasses(className.container, containerClassName),
228
+ className: className.container,
241
229
  'data-drop-container': name || id,
242
230
  children: normalizeContentNodes(content, this),
243
231
  });
@@ -433,7 +421,9 @@ export class Drop {
433
421
  this.root.remove();
434
422
  }
435
423
 
436
- this.root.classList.toggle(this.props.className.active, visible);
424
+ this.root.setAttribute('aria-hidden', visible ? 'false' : 'true');
425
+ this.root.setAttribute('aria-expanded', visible ? 'true' : 'false');
426
+
437
427
  this.isVisible = visible;
438
428
  }
439
429
 
@@ -53,8 +53,8 @@ interface ParabolaPath {
53
53
  }
54
54
 
55
55
  const DEFAULT_BALL: ParabolaBallOptions = {
56
- color: 'var(--primary, #3e63dd)',
57
- size: '10px',
56
+ color: 'var(--tone-solid)',
57
+ size: '12px',
58
58
  };
59
59
 
60
60
  const DEFAULT_CLASS_NAMES: ParabolaClassNames = {
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  bindAttr,
3
- bindClass,
4
3
  createDeepStore,
5
4
  createRoot,
6
5
  flushSync,
@@ -43,7 +42,6 @@ export interface TabsClassNames {
43
42
  tab: string;
44
43
  panelWrap: string;
45
44
  panel: string;
46
- active: string;
47
45
  disabled: string;
48
46
  dragging: string;
49
47
  }
@@ -163,7 +161,6 @@ const DEFAULT_CLASS_NAMES: TabsClassNames = {
163
161
  tab: 'tab-item',
164
162
  panelWrap: 'tab-panel',
165
163
  panel: 'panel-item',
166
- active: 'is-active',
167
164
  disabled: 'is-disabled',
168
165
  dragging: 'dragging',
169
166
  };
@@ -389,33 +386,16 @@ export class Tabs extends Component<ResolvedTabsProps, TabsState, TabsDOM> {
389
386
  this.bindingsDispose = createRoot((dispose) => {
390
387
  this.dom.tabs.forEach((tab, index) => {
391
388
  const name = tab.dataset.tabsTab || '';
392
- bindClass(
393
- tab,
394
- this.props.className.active,
395
- () => this.state.current.index === index
396
- );
397
- bindClass(tab, this.props.className.disabled, () =>
398
- this.state.disabled.names.includes(name)
399
- );
400
- bindAttr(
401
- tab,
402
- 'aria-selected',
403
- () => this.state.current.index === index
389
+ bindAttr(tab, 'aria-selected', () =>
390
+ this.state.current.index === index ? 'true' : 'false'
404
391
  );
405
392
  bindAttr(tab, 'aria-disabled', () =>
406
- this.state.disabled.names.includes(name)
393
+ this.state.disabled.names.includes(name) ? 'true' : 'false'
407
394
  );
408
395
  });
409
396
  this.dom.panels.forEach((panel, index) => {
410
- bindClass(
411
- panel,
412
- this.props.className.active,
413
- () => this.state.current.index === index
414
- );
415
- bindAttr(
416
- panel,
417
- 'aria-hidden',
418
- () => this.state.current.index !== index
397
+ bindAttr(panel, 'aria-hidden', () =>
398
+ this.state.current.index !== index ? 'true' : 'false'
419
399
  );
420
400
  });
421
401
  return dispose;
@@ -260,33 +260,36 @@ export class Theme {
260
260
  className: containerClass || className.panel,
261
261
  'data-theme-palette': 'root',
262
262
  children: [
263
- jsx('div', {
264
- className: className.closeWrap,
265
- 'data-theme-close-wrap': '',
266
- style: {
267
- display: 'flex',
268
- justifyContent: 'center',
269
- },
270
- children: jsx('button', {
271
- className: className.closeButton,
272
- 'data-action': 'close',
273
- children: this.translate('b'),
274
- }),
275
- }),
263
+ // jsx('div', {
264
+ // className: className.closeWrap,
265
+ // 'data-theme-close-wrap': '',
266
+ // style: {
267
+ // display: 'flex',
268
+ // justifyContent: 'center',
269
+ // },
270
+ // children: jsx('button', {
271
+ // className: className.closeButton,
272
+ // 'data-action': 'close',
273
+ // children: this.translate('b'),
274
+ // }),
275
+ // }),
276
276
  jsx('h3', {
277
277
  className: className.title,
278
278
  'data-theme-title': '',
279
- children: this.translate('t'),
280
- }),
281
- jsx('p', {
282
- className: className.description,
283
- 'data-theme-description': '',
284
279
  style: {
285
- marginTop: 0,
286
- fontSize: 'var(--text-sm, 0.875rem)',
280
+ margin: '0 0 1rem',
287
281
  },
288
- children: this.translate('d'),
282
+ children: this.translate('t'),
289
283
  }),
284
+ // jsx('p', {
285
+ // className: className.description,
286
+ // 'data-theme-description': '',
287
+ // style: {
288
+ // marginTop: 0,
289
+ // fontSize: 'var(--text-sm, 0.875rem)',
290
+ // },
291
+ // children: this.translate('d'),
292
+ // }),
290
293
  jsx('div', {
291
294
  className: className.container,
292
295
  'data-theme-container': '',
@@ -373,7 +376,7 @@ export class Theme {
373
376
  private defaultPanelConfig(): ThemePanelGroup[] {
374
377
  return [
375
378
  {
376
- title: this.translate('Theme'),
379
+ title: this.translate('Primary'),
377
380
  type: 'theme',
378
381
  buttons: [
379
382
  ['gray', this.translate('Gray')],
@@ -1,5 +1,7 @@
1
1
  import { jsx } from 'vanilla-signal';
2
+ import { t } from 'vanilla-signal-i18n';
2
3
 
4
+ import locales from '../locales/index.ts';
3
5
  import { randomId, timer, validateParam } from '../utilities/core.ts';
4
6
  import { q } from '../utilities/dom.ts';
5
7
  import { listen } from '../utilities/events.ts';
@@ -12,13 +14,12 @@ export interface ToastClassNames {
12
14
  toast: string;
13
15
  icon: string;
14
16
  message: string;
15
- shown: string;
16
17
  hidden: string;
17
18
  lite: string;
18
19
  action: string;
19
20
  actions: string;
20
21
  button: string;
21
- cancelButton: string;
22
+ closeButton: string;
22
23
  actionButton: string;
23
24
  info: string;
24
25
  success: string;
@@ -35,10 +36,11 @@ export interface ToastOptions {
35
36
 
36
37
  export interface ToastActionProps extends ToastOptions {
37
38
  text?: {
38
- cancel?: string;
39
+ close?: string;
39
40
  action?: string;
40
41
  };
41
42
  onAction?: () => void | Promise<void>;
43
+ onClose?: () => void | Promise<void>;
42
44
  }
43
45
 
44
46
  interface ResolvedToastOptions {
@@ -50,13 +52,12 @@ const DEFAULT_CLASS_NAMES: ToastClassNames = {
50
52
  toast: 'j-toast',
51
53
  icon: 'toast-icon',
52
54
  message: 'toast-message',
53
- shown: 'is-shown',
54
55
  hidden: 'is-hidden',
55
56
  lite: 'j-toast-lite',
56
57
  action: 'is-action',
57
58
  actions: 'toast-actions',
58
59
  button: 'j-button is-sm',
59
- cancelButton: 'is-ghost',
60
+ closeButton: 'is-ghost',
60
61
  actionButton: 'is-outline',
61
62
  info: 'is-info',
62
63
  success: 'is-success',
@@ -135,6 +136,8 @@ export class Toast {
135
136
  const toast = jsx('div', {
136
137
  className: joinClasses(className.toast, className[type]),
137
138
  'data-toast': id,
139
+ role: 'alert',
140
+ 'aria-atomic': 'true',
138
141
  children: [
139
142
  jsx('span', {
140
143
  className: className.icon,
@@ -152,7 +155,8 @@ export class Toast {
152
155
  Toast.classNames.set(toast, className);
153
156
  toastContainer.appendChild(toast);
154
157
 
155
- Toast.setTimer(id, 'show', () => toast.classList.add(className.shown), 10);
158
+ const v = type === 'error' ? 'assertive' : 'polite';
159
+ Toast.setTimer(id, 'show', () => toast.setAttribute('aria-live', v), 10);
156
160
 
157
161
  if (duration > 0) {
158
162
  Toast.setTimer(id, 'hide', () => Toast.hide(toast), duration);
@@ -207,12 +211,11 @@ export class Toast {
207
211
  static hide(toast: HTMLElement | null | undefined): void {
208
212
  if (!toast) return;
209
213
 
210
- const className = Toast.classNames.get(toast) || Toast.options.className;
211
214
  Toast.disposers.get(toast)?.();
212
215
  Toast.disposers.delete(toast);
213
216
 
214
- toast.classList.remove(className.shown);
215
- toast.classList.add(className.hidden);
217
+ toast.removeAttribute('aria-live');
218
+ toast.setAttribute('aria-hidden', 'true');
216
219
 
217
220
  const id = toast.dataset.toast || randomId();
218
221
  Toast.setTimer(
@@ -252,13 +255,19 @@ export class Toast {
252
255
  Toast.classNames.set(lite, className);
253
256
  document.body.appendChild(lite);
254
257
 
255
- Toast.setTimer(id, 'show', () => lite.classList.add(className.shown), 10);
258
+ Toast.setTimer(
259
+ id,
260
+ 'show',
261
+ () => lite.setAttribute('aria-live', 'polite'),
262
+ 10
263
+ );
264
+
256
265
  Toast.setTimer(
257
266
  id,
258
267
  'hide',
259
268
  () => {
260
- lite.classList.remove(className.shown);
261
- lite.classList.add(className.hidden);
269
+ lite.removeAttribute('aria-live');
270
+ lite.setAttribute('aria-hidden', 'true');
262
271
  Toast.setTimer(id, 'remove', () => lite.remove(), 300);
263
272
  },
264
273
  duration
@@ -269,7 +278,8 @@ export class Toast {
269
278
 
270
279
  static action(message = '', props: ToastActionProps = {}): HTMLElement {
271
280
  validateParam('message', message, 'string', 'Toast.action');
272
-
281
+ const c = t('Close', locales);
282
+ const a = t('Confirm', locales);
273
283
  const className = Toast.resolveClassNames(props);
274
284
  const toastContainer = Toast.getOrCreateContainer(className);
275
285
  const id = randomId();
@@ -288,15 +298,20 @@ export class Toast {
288
298
  'data-toast-actions': id,
289
299
  children: [
290
300
  jsx('button', {
291
- className: joinClasses(className.button, className.cancelButton),
292
- children: props.text?.cancel || 'cancel',
293
- 'data-action': 'cancel',
294
- onClick: () => Toast.hide(action),
301
+ className: joinClasses(className.button, className.closeButton),
302
+ children: props.text?.close || c,
303
+ 'data-action': 'close',
304
+ 'aria-label': props.text?.close || c,
305
+ onClick: async () => {
306
+ await props.onClose?.();
307
+ Toast.hide(action);
308
+ },
295
309
  }),
296
310
  jsx('button', {
297
311
  className: joinClasses(className.button, className.actionButton),
298
- children: props.text?.action || 'action',
312
+ children: props.text?.action || a,
299
313
  'data-action': 'toast-action',
314
+ 'aria-label': props.text?.action || a,
300
315
  onClick: async () => {
301
316
  await props.onAction?.();
302
317
  Toast.hide(action);
@@ -314,7 +329,7 @@ export class Toast {
314
329
  id,
315
330
  'show',
316
331
  () => {
317
- action.classList.add(className.shown);
332
+ action.setAttribute('aria-live', 'polite');
318
333
  q<HTMLButtonElement>('[data-action="toast-action"]', action)?.focus();
319
334
  },
320
335
  10
@@ -4,14 +4,13 @@ import { type ResolveSchema, resolveProps } from '../utilities/core.ts';
4
4
  import { type DOMReference } from '../utilities/dom.ts';
5
5
  import {
6
6
  Drop,
7
- type DropClassNameConfig,
8
7
  type DropDelay,
9
8
  type DropMode,
10
9
  type DropPosition,
11
10
  } from './drop.ts';
12
11
 
13
12
  export interface TooltipClassNames {
14
- root: string;
13
+ container: string;
15
14
  message: string;
16
15
  }
17
16
 
@@ -24,7 +23,6 @@ export interface TooltipProps extends Record<string, unknown> {
24
23
  offset?: number;
25
24
  message?: string;
26
25
  className?: TooltipClassNameConfig;
27
- dropClassName?: DropClassNameConfig;
28
26
  id?: string | null;
29
27
  delay?: number | DropDelay;
30
28
  hoverIntent?: boolean;
@@ -39,7 +37,6 @@ interface ResolvedTooltipProps extends Record<string, unknown> {
39
37
  offset: number;
40
38
  message: string;
41
39
  className: TooltipClassNames;
42
- dropClassName: DropClassNameConfig;
43
40
  id: string | null;
44
41
  delay: number | DropDelay;
45
42
  hoverIntent: boolean;
@@ -48,7 +45,7 @@ interface ResolvedTooltipProps extends Record<string, unknown> {
48
45
  }
49
46
 
50
47
  const DEFAULT_CLASS_NAMES: TooltipClassNames = {
51
- root: 'j-tooltip',
48
+ container: 'j-tooltip',
52
49
  message: 'tooltip-message',
53
50
  };
54
51
 
@@ -87,7 +84,6 @@ const TOOLTIP_OPTIONS_SCHEMA = {
87
84
  ...(value && typeof value === 'object' ? value : {}),
88
85
  }),
89
86
  },
90
- dropClassName: { default: {}, type: 'object' },
91
87
  id: { default: null, types: ['string', 'null'] },
92
88
  delay: { default: 100, types: ['number', 'object'] },
93
89
  hoverIntent: { default: true, type: 'boolean' },
@@ -104,7 +100,6 @@ function normalizeProps(input: TooltipProps): ResolvedTooltipProps {
104
100
  offset: props.offset as number,
105
101
  message: props.message as string,
106
102
  className: props.className as TooltipClassNames,
107
- dropClassName: props.dropClassName as DropClassNameConfig,
108
103
  id: props.id as string | null,
109
104
  delay: props.delay as number | DropDelay,
110
105
  hoverIntent: props.hoverIntent as boolean,
@@ -135,14 +130,13 @@ export class Tooltip {
135
130
  hoverIntent: settings.hoverIntent,
136
131
  onShown: settings.onShown,
137
132
  onHidden: settings.onHidden,
138
- className: settings.dropClassName,
139
133
  content: this.buildContent(settings),
140
134
  });
141
135
  }
142
136
 
143
137
  private buildContent(settings: ResolvedTooltipProps): HTMLElement {
144
138
  return jsx('div', {
145
- className: settings.className.root,
139
+ className: settings.className.container,
146
140
  'data-tooltip': settings.name || settings.id || '',
147
141
  children: jsx('div', {
148
142
  className: settings.className.message,
@@ -325,7 +325,7 @@ export default class Component<
325
325
  */
326
326
  destroy(): void {
327
327
  if (this.runtime.destroyed) return;
328
- this.runtime = { destroyed: true };
328
+ this.runtime.destroyed = true;
329
329
 
330
330
  if (typeof this.onDestroy === 'function') this.onDestroy();
331
331
  this.emit('destroy');
@@ -21,7 +21,7 @@
21
21
  display: none;
22
22
  }
23
23
 
24
- .j-accordion .accordion-panel.is-active {
24
+ .j-accordion .accordion-panel[aria-hidden="false"] {
25
25
  display: block;
26
26
  }
27
27
  }
@@ -22,6 +22,7 @@
22
22
  @import url('./pagination.css');
23
23
  @import url('./card.css');
24
24
  @import url('./toolbar.css');
25
+ @import url('./editor.css');
25
26
  @import url('./menu.css');
26
27
  @import url('./toc.css');
27
28
  @import url('./form.css');