@u-elements/u-tabs 0.0.2 → 0.0.4

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/dist/u-tabs.cjs CHANGED
@@ -20,10 +20,8 @@ var attachStyle = (element, css) => element.attachShadow({ mode: "closed" }).app
20
20
  );
21
21
  var asButton = (event) => {
22
22
  const isClick = "key" in event && (event.key === " " || event.key === "Enter");
23
- if (isClick)
24
- event.preventDefault();
25
- if (isClick && event.target instanceof HTMLElement)
26
- event.target.click();
23
+ if (isClick) event.preventDefault();
24
+ if (isClick && event.target instanceof HTMLElement) event.target.click();
27
25
  return isClick;
28
26
  };
29
27
  var getRoot = (node) => node.getRootNode();
@@ -45,10 +43,10 @@ var UHTMLTabsElement = class extends UHTMLElement {
45
43
  return queryWithoutNested("u-tablist", this)[0] || null;
46
44
  }
47
45
  get selectedIndex() {
48
- return [...this.tabs].findIndex((tab) => tab.ariaSelected === "true");
46
+ return getSelectedIndex(this.tabs);
49
47
  }
50
48
  set selectedIndex(index) {
51
- this.tabs[index].ariaSelected = "true";
49
+ if (this.tabs[index]) this.tabs[index].ariaSelected = "true";
52
50
  }
53
51
  get tabs() {
54
52
  return queryWithoutNested("u-tab", this);
@@ -72,31 +70,42 @@ var UHTMLTabListElement = class extends UHTMLElement {
72
70
  handleEvent(event) {
73
71
  const { key } = event;
74
72
  const tabs = [...this.getElementsByTagName("u-tab")];
75
- let index = tabs.findIndex((tab) => tab.contains(event.target));
76
- if (event.defaultPrevented || index === -1)
77
- return;
78
- if (event.type === "click")
79
- tabs[index].selected = true;
73
+ const prev = tabs.findIndex((tab) => tab.contains(event.target));
74
+ let next = prev;
75
+ if (event.defaultPrevented || prev === -1) return;
76
+ if (event.type === "click") tabs[prev].selected = true;
80
77
  if (event.type === "keydown" && !asButton(event)) {
81
78
  if (key === "ArrowDown" || key === "ArrowRight")
82
- index = ++index % tabs.length;
79
+ next = (prev + 1) % tabs.length;
83
80
  else if (key === "ArrowUp" || key === "ArrowLeft")
84
- index = (index || tabs.length) - 1;
85
- else if (key === "End")
86
- index = tabs.length - 1;
87
- else if (key === "Home")
88
- index = 0;
89
- else
90
- return;
91
- event.preventDefault();
92
- tabs[index].focus();
81
+ next = (prev || tabs.length) - 1;
82
+ else if (key === "End") next = tabs.length - 1;
83
+ else if (key === "Home") next = 0;
84
+ else if (key === "Tab") next = getSelectedIndex(tabs);
85
+ else return;
86
+ setTimeout(() => {
87
+ tabs[prev].tabIndex = -1;
88
+ tabs[next].tabIndex = 0;
89
+ });
90
+ if (key !== "Tab") {
91
+ event.preventDefault();
92
+ tabs[next].focus();
93
+ }
93
94
  }
94
95
  }
95
96
  get tabsElement() {
96
97
  return this.closest("u-tabs");
97
98
  }
99
+ get tabs() {
100
+ return queryWithoutNested("u-tab", this);
101
+ }
102
+ get selectedIndex() {
103
+ return getSelectedIndex(this.tabs);
104
+ }
105
+ set selectedIndex(index) {
106
+ if (this.tabs[index]) this.tabs[index].ariaSelected = "true";
107
+ }
98
108
  };
99
- var SKIP_ATTR_CHANGE = false;
100
109
  var UHTMLTabElement = class extends UHTMLElement {
101
110
  constructor() {
102
111
  super();
@@ -106,33 +115,33 @@ var UHTMLTabElement = class extends UHTMLElement {
106
115
  );
107
116
  }
108
117
  connectedCallback() {
109
- this.selected = !!this.selected;
110
- }
111
- attributeChangedCallback(_name, prev, next) {
112
- if (!SKIP_ATTR_CHANGE && prev !== next && (SKIP_ATTR_CHANGE = true)) {
113
- const { tabs = [], panels = [], selectedIndex } = this.tabsElement || {};
114
- const selected = this.selected ? this : tabs[selectedIndex || 0] || this;
115
- let selectedPanel;
116
- panels.forEach((panel) => {
117
- panel.removeAttribute(SAFE_LABELLEDBY);
118
- panel.hidden = true;
119
- });
120
- tabs.forEach((tab, index) => {
121
- const panel = getPanel(tab) || panels[index] || null;
122
- if (selected === tab && panel)
123
- selectedPanel = panel;
124
- tab.role = "tab";
125
- tab.tabIndex = selected === tab ? 0 : -1;
126
- tab.ariaSelected = `${selected === tab}`;
127
- tab.setAttribute(ARIA_CONTROLS, useId(panel));
128
- panel?.toggleAttribute("hidden", selectedPanel !== panel);
129
- panel?.setAttribute(
130
- SAFE_LABELLEDBY,
131
- useId(selectedPanel === panel ? selected : tab)
132
- );
118
+ const selected = this.selected || [...queryWithoutNested("u-tab", this.tabList || this)].every(
119
+ (tab) => tab.ariaSelected !== "true"
120
+ );
121
+ this.role = "tab";
122
+ this.tabIndex = selected ? 0 : -1;
123
+ this.ariaSelected = `${selected}`;
124
+ }
125
+ attributeChangedCallback(name, prev) {
126
+ if (!this.selected) return;
127
+ const nextPanel = getPanel(this);
128
+ const nextPanelId = useId(nextPanel);
129
+ if (this.tabList)
130
+ queryWithoutNested("u-tab", this.tabList).forEach((tab) => {
131
+ const isUnselect = tab !== this && tab.ariaSelected === "true";
132
+ const prevPanel = isUnselect && getPanel(tab);
133
+ if (prevPanel && prevPanel !== nextPanel) prevPanel.hidden = true;
134
+ if (isUnselect) {
135
+ tab.ariaSelected = "false";
136
+ tab.tabIndex = -1;
137
+ }
133
138
  });
134
- SKIP_ATTR_CHANGE = false;
135
- }
139
+ if (name === ARIA_CONTROLS) getPanel(this, prev)?.setAttribute("hidden", "");
140
+ if (this.getAttribute(ARIA_CONTROLS) !== nextPanelId)
141
+ this.setAttribute(ARIA_CONTROLS, nextPanelId);
142
+ this.tabIndex = 0;
143
+ nextPanel?.setAttribute(SAFE_LABELLEDBY, useId(this));
144
+ nextPanel?.removeAttribute("hidden");
136
145
  }
137
146
  get tabsElement() {
138
147
  return this.closest("u-tabs");
@@ -144,11 +153,12 @@ var UHTMLTabElement = class extends UHTMLElement {
144
153
  return this.ariaSelected === "true";
145
154
  }
146
155
  set selected(value) {
147
- this.ariaSelected = `${value}`;
156
+ this.ariaSelected = `${!!value}`;
148
157
  }
149
158
  /** Retrieves the ordinal position of an tab in a tablist. */
150
159
  get index() {
151
- return Array.from(this.tabsElement?.tabs || []).indexOf(this);
160
+ const tabList = this.tabList;
161
+ return tabList ? [...queryWithoutNested("u-tab", tabList)].indexOf(this) : 0;
152
162
  }
153
163
  get panel() {
154
164
  return getPanel(this);
@@ -161,33 +171,27 @@ var UHTMLTabPanelElement = class extends UHTMLElement {
161
171
  attachStyle(this, DISPLAY_BLOCK);
162
172
  }
163
173
  connectedCallback() {
174
+ this.hidden = [...this.tabs].every((tab) => tab.ariaSelected !== "true");
164
175
  this.role = "tabpanel";
165
- this.hidden = Array.from(this.tabs).every((tab) => !tab.selected);
166
- }
167
- attributeChangedCallback(_name, prev, next) {
168
- if (SKIP_ATTR_CHANGE || prev === next)
169
- return;
170
- getTabs(this, prev).forEach((tab) => tab.setAttribute(ARIA_CONTROLS, next));
171
176
  }
172
177
  get tabsElement() {
173
178
  return this.closest("u-tabs");
174
179
  }
175
180
  get tabs() {
176
- return getTabs(this, this.id);
181
+ const css = `u-tab[${ARIA_CONTROLS}="${this.id}"]`;
182
+ const root = getRoot(this).querySelectorAll(css);
183
+ return root.length ? root : document.querySelectorAll(css);
177
184
  }
178
185
  };
179
- UHTMLTabPanelElement.observedAttributes = ["id"];
180
186
  var queryWithoutNested = (tag, self) => {
181
- const selector = `${tag}:not(:scope ${self.nodeName}:not(:scope) ${tag})`;
182
- return self.querySelectorAll(selector);
183
- };
184
- var getPanel = (self) => {
185
- const css = `u-tabpanel[id="${self.getAttribute(ARIA_CONTROLS)}"]`;
186
- return getRoot(self).querySelector(css) || document.querySelector(css);
187
+ const css = `${tag}:not(:scope ${self.nodeName}:not(:scope) ${tag})`;
188
+ return self.querySelectorAll(css);
187
189
  };
188
- var getTabs = (self, id2) => {
189
- const css = `u-tab[${ARIA_CONTROLS}="${id2}"]`;
190
- return getRoot(self).querySelectorAll(css);
190
+ var getSelectedIndex = (tabs) => [...tabs].findIndex((tab) => tab.ariaSelected === "true");
191
+ var getPanel = (self, id2) => {
192
+ const css = `u-tabpanel[id="${id2 || self.getAttribute(ARIA_CONTROLS)}"]`;
193
+ const tabsElement = self.closest("u-tabs");
194
+ return getRoot(self).querySelector(css) || document.querySelector(css) || tabsElement && queryWithoutNested("u-tabpanel", tabsElement)[[...queryWithoutNested("u-tab", tabsElement)].indexOf(self)] || null;
191
195
  };
192
196
  customElements.define("u-tabs", UHTMLTabsElement);
193
197
  customElements.define("u-tablist", UHTMLTabListElement);
package/dist/u-tabs.d.cts CHANGED
@@ -33,6 +33,9 @@ declare class UHTMLTabListElement extends UHTMLElement {
33
33
  disconnectedCallback(): void;
34
34
  handleEvent(event: Event): void;
35
35
  get tabsElement(): UHTMLTabsElement | null;
36
+ get tabs(): NodeListOf<UHTMLTabElement>;
37
+ get selectedIndex(): number;
38
+ set selectedIndex(index: number);
36
39
  }
37
40
  /**
38
41
  * The `<u-tab>` HTML element is an interactive element inside a `<u-tablist>` that, when activated, displays its associated `<u-tabpanel>`.
@@ -42,7 +45,7 @@ declare class UHTMLTabElement extends UHTMLElement {
42
45
  static observedAttributes: string[];
43
46
  constructor();
44
47
  connectedCallback(): void;
45
- attributeChangedCallback(_name: string, prev: string, next: string): void;
48
+ attributeChangedCallback(name: string, prev: string): void;
46
49
  get tabsElement(): UHTMLTabsElement | null;
47
50
  get tabList(): UHTMLTabListElement | null;
48
51
  get selected(): boolean;
@@ -56,66 +59,95 @@ declare class UHTMLTabElement extends UHTMLElement {
56
59
  * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/tabpanel_role)
57
60
  */
58
61
  declare class UHTMLTabPanelElement extends UHTMLElement {
59
- static observedAttributes: string[];
60
62
  constructor();
61
63
  connectedCallback(): void;
62
- attributeChangedCallback(_name: string, prev: string, next: string): void;
63
64
  get tabsElement(): UHTMLTabsElement | null;
64
65
  get tabs(): NodeListOf<UHTMLTabElement>;
65
66
  }
66
67
 
67
68
  export { UHTMLTabElement, UHTMLTabListElement, UHTMLTabPanelElement, UHTMLTabsElement };
69
+
70
+ import type * as PreactTypes from 'preact'
71
+ import type * as ReactTypes from 'react'
72
+ import type * as SvelteTypes from 'svelte/elements'
68
73
  import type * as VueJSX from '@vue/runtime-dom'
69
74
  import type { JSX as QwikJSX } from '@builder.io/qwik/jsx-runtime'
70
- import type { JSX as ReactJSX } from 'react'
71
75
  import type { JSX as SolidJSX } from 'solid-js'
72
- import type { SvelteHTMLElements } from 'svelte/elements'
73
76
 
74
- export type VueTabs = VueJSX.IntrinsicElementAttributes['div']
75
- export type QwikTabs = QwikJSX.IntrinsicElements['div']
76
- export type ReactTabs = ReactJSX.IntrinsicElements['div'] & { class?: string }
77
- export type SolidJSTabs = SolidJSX.HTMLElementTags['div']
78
- export type SvelteTabs = SvelteHTMLElements['div']
77
+
78
+ export type PreactUtabs = PreactTypes.JSX.HTMLAttributes<UHTMLTagsElement> & { }
79
+ export type ReactUtabs = ReactTypes.DetailedHTMLProps<ReactTypes.HTMLAttributes<UHTMLTabsElement>, UHTMLTabsElement> & { class?: string }
80
+ export type QwikUtabs = QwikJSX.IntrinsicElements['div']
81
+ export type VueUtabs = VueJSX.HTMLAttributes
82
+ export type SvelteUtabs = SvelteTypes.HTMLAttributes<UHTMLTabsElement> & { }
83
+ export type SolidUtabs = SolidJSX.HTMLAttributes<UHTMLTabsElement>
79
84
 
80
85
  // Augmenting @vue/runtime-dom instead of vue directly to avoid interfering with React JSX
81
- declare module '@vue/runtime-dom' { export interface GlobalComponents { 'u-tabs': VueTabs } }
82
- declare module '@builder.io/qwik/jsx-runtime' { export namespace JSX { export interface IntrinsicElements { 'u-tabs': QwikTabs } } }
83
- declare global { namespace React.JSX { interface IntrinsicElements { 'u-tabs': ReactTabs } } }
84
- declare module 'solid-js' { namespace JSX { interface IntrinsicElements { 'u-tabs': SolidJSTabs } } }
85
- declare module 'svelte/elements' { interface SvelteHTMLElements { 'u-tabs': SvelteTabs } }
86
- export type VueTablist = VueJSX.IntrinsicElementAttributes['div']
87
- export type QwikTablist = QwikJSX.IntrinsicElements['div']
88
- export type ReactTablist = ReactJSX.IntrinsicElements['div'] & { class?: string }
89
- export type SolidJSTablist = SolidJSX.HTMLElementTags['div']
90
- export type SvelteTablist = SvelteHTMLElements['div']
86
+ declare global { namespace React.JSX { interface IntrinsicElements { 'u-tabs': ReactUtabs } } }
87
+ declare global { namespace preact.JSX { interface IntrinsicElements { 'u-tabs': PreactUtabs } } }
88
+ declare module '@builder.io/qwik/jsx-runtime' { export namespace JSX { export interface IntrinsicElements { 'u-tabs': QwikUtabs } } }
89
+ declare module '@vue/runtime-dom' { export interface GlobalComponents { 'u-tabs': VueUtabs } }
90
+ declare module 'svelte/elements' { interface SvelteHTMLElements { 'u-tabs': SvelteUtabs } }
91
+ declare module 'solid-js' {
92
+ namespace JSX {
93
+ interface IntrinsicElements { 'u-tabs': SolidUtabs }
94
+ interface CustomEvents { }
95
+ }
96
+ }
97
+ export type PreactUtablist = PreactTypes.JSX.HTMLAttributes<UHTMLTagsElement> & { }
98
+ export type ReactUtablist = ReactTypes.DetailedHTMLProps<ReactTypes.HTMLAttributes<UHTMLTabListElement>, UHTMLTabListElement> & { class?: string }
99
+ export type QwikUtablist = QwikJSX.IntrinsicElements['div']
100
+ export type VueUtablist = VueJSX.HTMLAttributes
101
+ export type SvelteUtablist = SvelteTypes.HTMLAttributes<UHTMLTabListElement> & { }
102
+ export type SolidUtablist = SolidJSX.HTMLAttributes<UHTMLTabListElement>
91
103
 
92
104
  // Augmenting @vue/runtime-dom instead of vue directly to avoid interfering with React JSX
93
- declare module '@vue/runtime-dom' { export interface GlobalComponents { 'u-tablist': VueTablist } }
94
- declare module '@builder.io/qwik/jsx-runtime' { export namespace JSX { export interface IntrinsicElements { 'u-tablist': QwikTablist } } }
95
- declare global { namespace React.JSX { interface IntrinsicElements { 'u-tablist': ReactTablist } } }
96
- declare module 'solid-js' { namespace JSX { interface IntrinsicElements { 'u-tablist': SolidJSTablist } } }
97
- declare module 'svelte/elements' { interface SvelteHTMLElements { 'u-tablist': SvelteTablist } }
98
- export type VueTab = VueJSX.IntrinsicElementAttributes['div']
99
- export type QwikTab = QwikJSX.IntrinsicElements['div']
100
- export type ReactTab = ReactJSX.IntrinsicElements['div'] & { class?: string }
101
- export type SolidJSTab = SolidJSX.HTMLElementTags['div']
102
- export type SvelteTab = SvelteHTMLElements['div']
105
+ declare global { namespace React.JSX { interface IntrinsicElements { 'u-tablist': ReactUtablist } } }
106
+ declare global { namespace preact.JSX { interface IntrinsicElements { 'u-tablist': PreactUtablist } } }
107
+ declare module '@builder.io/qwik/jsx-runtime' { export namespace JSX { export interface IntrinsicElements { 'u-tablist': QwikUtablist } } }
108
+ declare module '@vue/runtime-dom' { export interface GlobalComponents { 'u-tablist': VueUtablist } }
109
+ declare module 'svelte/elements' { interface SvelteHTMLElements { 'u-tablist': SvelteUtablist } }
110
+ declare module 'solid-js' {
111
+ namespace JSX {
112
+ interface IntrinsicElements { 'u-tablist': SolidUtablist }
113
+ interface CustomEvents { }
114
+ }
115
+ }
116
+ export type PreactUtab = PreactTypes.JSX.HTMLAttributes<UHTMLTagsElement> & { }
117
+ export type ReactUtab = ReactTypes.DetailedHTMLProps<ReactTypes.HTMLAttributes<UHTMLTabElement>, UHTMLTabElement> & { class?: string }
118
+ export type QwikUtab = QwikJSX.IntrinsicElements['div']
119
+ export type VueUtab = VueJSX.HTMLAttributes
120
+ export type SvelteUtab = SvelteTypes.HTMLAttributes<UHTMLTabElement> & { }
121
+ export type SolidUtab = SolidJSX.HTMLAttributes<UHTMLTabElement>
103
122
 
104
123
  // Augmenting @vue/runtime-dom instead of vue directly to avoid interfering with React JSX
105
- declare module '@vue/runtime-dom' { export interface GlobalComponents { 'u-tab': VueTab } }
106
- declare module '@builder.io/qwik/jsx-runtime' { export namespace JSX { export interface IntrinsicElements { 'u-tab': QwikTab } } }
107
- declare global { namespace React.JSX { interface IntrinsicElements { 'u-tab': ReactTab } } }
108
- declare module 'solid-js' { namespace JSX { interface IntrinsicElements { 'u-tab': SolidJSTab } } }
109
- declare module 'svelte/elements' { interface SvelteHTMLElements { 'u-tab': SvelteTab } }
110
- export type VueTabpanel = VueJSX.IntrinsicElementAttributes['div']
111
- export type QwikTabpanel = QwikJSX.IntrinsicElements['div']
112
- export type ReactTabpanel = ReactJSX.IntrinsicElements['div'] & { class?: string }
113
- export type SolidJSTabpanel = SolidJSX.HTMLElementTags['div']
114
- export type SvelteTabpanel = SvelteHTMLElements['div']
124
+ declare global { namespace React.JSX { interface IntrinsicElements { 'u-tab': ReactUtab } } }
125
+ declare global { namespace preact.JSX { interface IntrinsicElements { 'u-tab': PreactUtab } } }
126
+ declare module '@builder.io/qwik/jsx-runtime' { export namespace JSX { export interface IntrinsicElements { 'u-tab': QwikUtab } } }
127
+ declare module '@vue/runtime-dom' { export interface GlobalComponents { 'u-tab': VueUtab } }
128
+ declare module 'svelte/elements' { interface SvelteHTMLElements { 'u-tab': SvelteUtab } }
129
+ declare module 'solid-js' {
130
+ namespace JSX {
131
+ interface IntrinsicElements { 'u-tab': SolidUtab }
132
+ interface CustomEvents { }
133
+ }
134
+ }
135
+ export type PreactUtabpanel = PreactTypes.JSX.HTMLAttributes<UHTMLTagsElement> & { }
136
+ export type ReactUtabpanel = ReactTypes.DetailedHTMLProps<ReactTypes.HTMLAttributes<UHTMLTabPanelElement>, UHTMLTabPanelElement> & { class?: string }
137
+ export type QwikUtabpanel = QwikJSX.IntrinsicElements['div']
138
+ export type VueUtabpanel = VueJSX.HTMLAttributes
139
+ export type SvelteUtabpanel = SvelteTypes.HTMLAttributes<UHTMLTabPanelElement> & { }
140
+ export type SolidUtabpanel = SolidJSX.HTMLAttributes<UHTMLTabPanelElement>
115
141
 
116
142
  // Augmenting @vue/runtime-dom instead of vue directly to avoid interfering with React JSX
117
- declare module '@vue/runtime-dom' { export interface GlobalComponents { 'u-tabpanel': VueTabpanel } }
118
- declare module '@builder.io/qwik/jsx-runtime' { export namespace JSX { export interface IntrinsicElements { 'u-tabpanel': QwikTabpanel } } }
119
- declare global { namespace React.JSX { interface IntrinsicElements { 'u-tabpanel': ReactTabpanel } } }
120
- declare module 'solid-js' { namespace JSX { interface IntrinsicElements { 'u-tabpanel': SolidJSTabpanel } } }
121
- declare module 'svelte/elements' { interface SvelteHTMLElements { 'u-tabpanel': SvelteTabpanel } }
143
+ declare global { namespace React.JSX { interface IntrinsicElements { 'u-tabpanel': ReactUtabpanel } } }
144
+ declare global { namespace preact.JSX { interface IntrinsicElements { 'u-tabpanel': PreactUtabpanel } } }
145
+ declare module '@builder.io/qwik/jsx-runtime' { export namespace JSX { export interface IntrinsicElements { 'u-tabpanel': QwikUtabpanel } } }
146
+ declare module '@vue/runtime-dom' { export interface GlobalComponents { 'u-tabpanel': VueUtabpanel } }
147
+ declare module 'svelte/elements' { interface SvelteHTMLElements { 'u-tabpanel': SvelteUtabpanel } }
148
+ declare module 'solid-js' {
149
+ namespace JSX {
150
+ interface IntrinsicElements { 'u-tabpanel': SolidUtabpanel }
151
+ interface CustomEvents { }
152
+ }
153
+ }
package/dist/u-tabs.d.ts CHANGED
@@ -33,6 +33,9 @@ declare class UHTMLTabListElement extends UHTMLElement {
33
33
  disconnectedCallback(): void;
34
34
  handleEvent(event: Event): void;
35
35
  get tabsElement(): UHTMLTabsElement | null;
36
+ get tabs(): NodeListOf<UHTMLTabElement>;
37
+ get selectedIndex(): number;
38
+ set selectedIndex(index: number);
36
39
  }
37
40
  /**
38
41
  * The `<u-tab>` HTML element is an interactive element inside a `<u-tablist>` that, when activated, displays its associated `<u-tabpanel>`.
@@ -42,7 +45,7 @@ declare class UHTMLTabElement extends UHTMLElement {
42
45
  static observedAttributes: string[];
43
46
  constructor();
44
47
  connectedCallback(): void;
45
- attributeChangedCallback(_name: string, prev: string, next: string): void;
48
+ attributeChangedCallback(name: string, prev: string): void;
46
49
  get tabsElement(): UHTMLTabsElement | null;
47
50
  get tabList(): UHTMLTabListElement | null;
48
51
  get selected(): boolean;
@@ -56,66 +59,95 @@ declare class UHTMLTabElement extends UHTMLElement {
56
59
  * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/tabpanel_role)
57
60
  */
58
61
  declare class UHTMLTabPanelElement extends UHTMLElement {
59
- static observedAttributes: string[];
60
62
  constructor();
61
63
  connectedCallback(): void;
62
- attributeChangedCallback(_name: string, prev: string, next: string): void;
63
64
  get tabsElement(): UHTMLTabsElement | null;
64
65
  get tabs(): NodeListOf<UHTMLTabElement>;
65
66
  }
66
67
 
67
68
  export { UHTMLTabElement, UHTMLTabListElement, UHTMLTabPanelElement, UHTMLTabsElement };
69
+
70
+ import type * as PreactTypes from 'preact'
71
+ import type * as ReactTypes from 'react'
72
+ import type * as SvelteTypes from 'svelte/elements'
68
73
  import type * as VueJSX from '@vue/runtime-dom'
69
74
  import type { JSX as QwikJSX } from '@builder.io/qwik/jsx-runtime'
70
- import type { JSX as ReactJSX } from 'react'
71
75
  import type { JSX as SolidJSX } from 'solid-js'
72
- import type { SvelteHTMLElements } from 'svelte/elements'
73
76
 
74
- export type VueTabs = VueJSX.IntrinsicElementAttributes['div']
75
- export type QwikTabs = QwikJSX.IntrinsicElements['div']
76
- export type ReactTabs = ReactJSX.IntrinsicElements['div'] & { class?: string }
77
- export type SolidJSTabs = SolidJSX.HTMLElementTags['div']
78
- export type SvelteTabs = SvelteHTMLElements['div']
77
+
78
+ export type PreactUtabs = PreactTypes.JSX.HTMLAttributes<UHTMLTagsElement> & { }
79
+ export type ReactUtabs = ReactTypes.DetailedHTMLProps<ReactTypes.HTMLAttributes<UHTMLTabsElement>, UHTMLTabsElement> & { class?: string }
80
+ export type QwikUtabs = QwikJSX.IntrinsicElements['div']
81
+ export type VueUtabs = VueJSX.HTMLAttributes
82
+ export type SvelteUtabs = SvelteTypes.HTMLAttributes<UHTMLTabsElement> & { }
83
+ export type SolidUtabs = SolidJSX.HTMLAttributes<UHTMLTabsElement>
79
84
 
80
85
  // Augmenting @vue/runtime-dom instead of vue directly to avoid interfering with React JSX
81
- declare module '@vue/runtime-dom' { export interface GlobalComponents { 'u-tabs': VueTabs } }
82
- declare module '@builder.io/qwik/jsx-runtime' { export namespace JSX { export interface IntrinsicElements { 'u-tabs': QwikTabs } } }
83
- declare global { namespace React.JSX { interface IntrinsicElements { 'u-tabs': ReactTabs } } }
84
- declare module 'solid-js' { namespace JSX { interface IntrinsicElements { 'u-tabs': SolidJSTabs } } }
85
- declare module 'svelte/elements' { interface SvelteHTMLElements { 'u-tabs': SvelteTabs } }
86
- export type VueTablist = VueJSX.IntrinsicElementAttributes['div']
87
- export type QwikTablist = QwikJSX.IntrinsicElements['div']
88
- export type ReactTablist = ReactJSX.IntrinsicElements['div'] & { class?: string }
89
- export type SolidJSTablist = SolidJSX.HTMLElementTags['div']
90
- export type SvelteTablist = SvelteHTMLElements['div']
86
+ declare global { namespace React.JSX { interface IntrinsicElements { 'u-tabs': ReactUtabs } } }
87
+ declare global { namespace preact.JSX { interface IntrinsicElements { 'u-tabs': PreactUtabs } } }
88
+ declare module '@builder.io/qwik/jsx-runtime' { export namespace JSX { export interface IntrinsicElements { 'u-tabs': QwikUtabs } } }
89
+ declare module '@vue/runtime-dom' { export interface GlobalComponents { 'u-tabs': VueUtabs } }
90
+ declare module 'svelte/elements' { interface SvelteHTMLElements { 'u-tabs': SvelteUtabs } }
91
+ declare module 'solid-js' {
92
+ namespace JSX {
93
+ interface IntrinsicElements { 'u-tabs': SolidUtabs }
94
+ interface CustomEvents { }
95
+ }
96
+ }
97
+ export type PreactUtablist = PreactTypes.JSX.HTMLAttributes<UHTMLTagsElement> & { }
98
+ export type ReactUtablist = ReactTypes.DetailedHTMLProps<ReactTypes.HTMLAttributes<UHTMLTabListElement>, UHTMLTabListElement> & { class?: string }
99
+ export type QwikUtablist = QwikJSX.IntrinsicElements['div']
100
+ export type VueUtablist = VueJSX.HTMLAttributes
101
+ export type SvelteUtablist = SvelteTypes.HTMLAttributes<UHTMLTabListElement> & { }
102
+ export type SolidUtablist = SolidJSX.HTMLAttributes<UHTMLTabListElement>
91
103
 
92
104
  // Augmenting @vue/runtime-dom instead of vue directly to avoid interfering with React JSX
93
- declare module '@vue/runtime-dom' { export interface GlobalComponents { 'u-tablist': VueTablist } }
94
- declare module '@builder.io/qwik/jsx-runtime' { export namespace JSX { export interface IntrinsicElements { 'u-tablist': QwikTablist } } }
95
- declare global { namespace React.JSX { interface IntrinsicElements { 'u-tablist': ReactTablist } } }
96
- declare module 'solid-js' { namespace JSX { interface IntrinsicElements { 'u-tablist': SolidJSTablist } } }
97
- declare module 'svelte/elements' { interface SvelteHTMLElements { 'u-tablist': SvelteTablist } }
98
- export type VueTab = VueJSX.IntrinsicElementAttributes['div']
99
- export type QwikTab = QwikJSX.IntrinsicElements['div']
100
- export type ReactTab = ReactJSX.IntrinsicElements['div'] & { class?: string }
101
- export type SolidJSTab = SolidJSX.HTMLElementTags['div']
102
- export type SvelteTab = SvelteHTMLElements['div']
105
+ declare global { namespace React.JSX { interface IntrinsicElements { 'u-tablist': ReactUtablist } } }
106
+ declare global { namespace preact.JSX { interface IntrinsicElements { 'u-tablist': PreactUtablist } } }
107
+ declare module '@builder.io/qwik/jsx-runtime' { export namespace JSX { export interface IntrinsicElements { 'u-tablist': QwikUtablist } } }
108
+ declare module '@vue/runtime-dom' { export interface GlobalComponents { 'u-tablist': VueUtablist } }
109
+ declare module 'svelte/elements' { interface SvelteHTMLElements { 'u-tablist': SvelteUtablist } }
110
+ declare module 'solid-js' {
111
+ namespace JSX {
112
+ interface IntrinsicElements { 'u-tablist': SolidUtablist }
113
+ interface CustomEvents { }
114
+ }
115
+ }
116
+ export type PreactUtab = PreactTypes.JSX.HTMLAttributes<UHTMLTagsElement> & { }
117
+ export type ReactUtab = ReactTypes.DetailedHTMLProps<ReactTypes.HTMLAttributes<UHTMLTabElement>, UHTMLTabElement> & { class?: string }
118
+ export type QwikUtab = QwikJSX.IntrinsicElements['div']
119
+ export type VueUtab = VueJSX.HTMLAttributes
120
+ export type SvelteUtab = SvelteTypes.HTMLAttributes<UHTMLTabElement> & { }
121
+ export type SolidUtab = SolidJSX.HTMLAttributes<UHTMLTabElement>
103
122
 
104
123
  // Augmenting @vue/runtime-dom instead of vue directly to avoid interfering with React JSX
105
- declare module '@vue/runtime-dom' { export interface GlobalComponents { 'u-tab': VueTab } }
106
- declare module '@builder.io/qwik/jsx-runtime' { export namespace JSX { export interface IntrinsicElements { 'u-tab': QwikTab } } }
107
- declare global { namespace React.JSX { interface IntrinsicElements { 'u-tab': ReactTab } } }
108
- declare module 'solid-js' { namespace JSX { interface IntrinsicElements { 'u-tab': SolidJSTab } } }
109
- declare module 'svelte/elements' { interface SvelteHTMLElements { 'u-tab': SvelteTab } }
110
- export type VueTabpanel = VueJSX.IntrinsicElementAttributes['div']
111
- export type QwikTabpanel = QwikJSX.IntrinsicElements['div']
112
- export type ReactTabpanel = ReactJSX.IntrinsicElements['div'] & { class?: string }
113
- export type SolidJSTabpanel = SolidJSX.HTMLElementTags['div']
114
- export type SvelteTabpanel = SvelteHTMLElements['div']
124
+ declare global { namespace React.JSX { interface IntrinsicElements { 'u-tab': ReactUtab } } }
125
+ declare global { namespace preact.JSX { interface IntrinsicElements { 'u-tab': PreactUtab } } }
126
+ declare module '@builder.io/qwik/jsx-runtime' { export namespace JSX { export interface IntrinsicElements { 'u-tab': QwikUtab } } }
127
+ declare module '@vue/runtime-dom' { export interface GlobalComponents { 'u-tab': VueUtab } }
128
+ declare module 'svelte/elements' { interface SvelteHTMLElements { 'u-tab': SvelteUtab } }
129
+ declare module 'solid-js' {
130
+ namespace JSX {
131
+ interface IntrinsicElements { 'u-tab': SolidUtab }
132
+ interface CustomEvents { }
133
+ }
134
+ }
135
+ export type PreactUtabpanel = PreactTypes.JSX.HTMLAttributes<UHTMLTagsElement> & { }
136
+ export type ReactUtabpanel = ReactTypes.DetailedHTMLProps<ReactTypes.HTMLAttributes<UHTMLTabPanelElement>, UHTMLTabPanelElement> & { class?: string }
137
+ export type QwikUtabpanel = QwikJSX.IntrinsicElements['div']
138
+ export type VueUtabpanel = VueJSX.HTMLAttributes
139
+ export type SvelteUtabpanel = SvelteTypes.HTMLAttributes<UHTMLTabPanelElement> & { }
140
+ export type SolidUtabpanel = SolidJSX.HTMLAttributes<UHTMLTabPanelElement>
115
141
 
116
142
  // Augmenting @vue/runtime-dom instead of vue directly to avoid interfering with React JSX
117
- declare module '@vue/runtime-dom' { export interface GlobalComponents { 'u-tabpanel': VueTabpanel } }
118
- declare module '@builder.io/qwik/jsx-runtime' { export namespace JSX { export interface IntrinsicElements { 'u-tabpanel': QwikTabpanel } } }
119
- declare global { namespace React.JSX { interface IntrinsicElements { 'u-tabpanel': ReactTabpanel } } }
120
- declare module 'solid-js' { namespace JSX { interface IntrinsicElements { 'u-tabpanel': SolidJSTabpanel } } }
121
- declare module 'svelte/elements' { interface SvelteHTMLElements { 'u-tabpanel': SvelteTabpanel } }
143
+ declare global { namespace React.JSX { interface IntrinsicElements { 'u-tabpanel': ReactUtabpanel } } }
144
+ declare global { namespace preact.JSX { interface IntrinsicElements { 'u-tabpanel': PreactUtabpanel } } }
145
+ declare module '@builder.io/qwik/jsx-runtime' { export namespace JSX { export interface IntrinsicElements { 'u-tabpanel': QwikUtabpanel } } }
146
+ declare module '@vue/runtime-dom' { export interface GlobalComponents { 'u-tabpanel': VueUtabpanel } }
147
+ declare module 'svelte/elements' { interface SvelteHTMLElements { 'u-tabpanel': SvelteUtabpanel } }
148
+ declare module 'solid-js' {
149
+ namespace JSX {
150
+ interface IntrinsicElements { 'u-tabpanel': SolidUtabpanel }
151
+ interface CustomEvents { }
152
+ }
153
+ }
package/dist/u-tabs.js CHANGED
@@ -18,10 +18,8 @@ var attachStyle = (element, css) => element.attachShadow({ mode: "closed" }).app
18
18
  );
19
19
  var asButton = (event) => {
20
20
  const isClick = "key" in event && (event.key === " " || event.key === "Enter");
21
- if (isClick)
22
- event.preventDefault();
23
- if (isClick && event.target instanceof HTMLElement)
24
- event.target.click();
21
+ if (isClick) event.preventDefault();
22
+ if (isClick && event.target instanceof HTMLElement) event.target.click();
25
23
  return isClick;
26
24
  };
27
25
  var getRoot = (node) => node.getRootNode();
@@ -43,10 +41,10 @@ var UHTMLTabsElement = class extends UHTMLElement {
43
41
  return queryWithoutNested("u-tablist", this)[0] || null;
44
42
  }
45
43
  get selectedIndex() {
46
- return [...this.tabs].findIndex((tab) => tab.ariaSelected === "true");
44
+ return getSelectedIndex(this.tabs);
47
45
  }
48
46
  set selectedIndex(index) {
49
- this.tabs[index].ariaSelected = "true";
47
+ if (this.tabs[index]) this.tabs[index].ariaSelected = "true";
50
48
  }
51
49
  get tabs() {
52
50
  return queryWithoutNested("u-tab", this);
@@ -70,31 +68,42 @@ var UHTMLTabListElement = class extends UHTMLElement {
70
68
  handleEvent(event) {
71
69
  const { key } = event;
72
70
  const tabs = [...this.getElementsByTagName("u-tab")];
73
- let index = tabs.findIndex((tab) => tab.contains(event.target));
74
- if (event.defaultPrevented || index === -1)
75
- return;
76
- if (event.type === "click")
77
- tabs[index].selected = true;
71
+ const prev = tabs.findIndex((tab) => tab.contains(event.target));
72
+ let next = prev;
73
+ if (event.defaultPrevented || prev === -1) return;
74
+ if (event.type === "click") tabs[prev].selected = true;
78
75
  if (event.type === "keydown" && !asButton(event)) {
79
76
  if (key === "ArrowDown" || key === "ArrowRight")
80
- index = ++index % tabs.length;
77
+ next = (prev + 1) % tabs.length;
81
78
  else if (key === "ArrowUp" || key === "ArrowLeft")
82
- index = (index || tabs.length) - 1;
83
- else if (key === "End")
84
- index = tabs.length - 1;
85
- else if (key === "Home")
86
- index = 0;
87
- else
88
- return;
89
- event.preventDefault();
90
- tabs[index].focus();
79
+ next = (prev || tabs.length) - 1;
80
+ else if (key === "End") next = tabs.length - 1;
81
+ else if (key === "Home") next = 0;
82
+ else if (key === "Tab") next = getSelectedIndex(tabs);
83
+ else return;
84
+ setTimeout(() => {
85
+ tabs[prev].tabIndex = -1;
86
+ tabs[next].tabIndex = 0;
87
+ });
88
+ if (key !== "Tab") {
89
+ event.preventDefault();
90
+ tabs[next].focus();
91
+ }
91
92
  }
92
93
  }
93
94
  get tabsElement() {
94
95
  return this.closest("u-tabs");
95
96
  }
97
+ get tabs() {
98
+ return queryWithoutNested("u-tab", this);
99
+ }
100
+ get selectedIndex() {
101
+ return getSelectedIndex(this.tabs);
102
+ }
103
+ set selectedIndex(index) {
104
+ if (this.tabs[index]) this.tabs[index].ariaSelected = "true";
105
+ }
96
106
  };
97
- var SKIP_ATTR_CHANGE = false;
98
107
  var UHTMLTabElement = class extends UHTMLElement {
99
108
  constructor() {
100
109
  super();
@@ -104,33 +113,33 @@ var UHTMLTabElement = class extends UHTMLElement {
104
113
  );
105
114
  }
106
115
  connectedCallback() {
107
- this.selected = !!this.selected;
108
- }
109
- attributeChangedCallback(_name, prev, next) {
110
- if (!SKIP_ATTR_CHANGE && prev !== next && (SKIP_ATTR_CHANGE = true)) {
111
- const { tabs = [], panels = [], selectedIndex } = this.tabsElement || {};
112
- const selected = this.selected ? this : tabs[selectedIndex || 0] || this;
113
- let selectedPanel;
114
- panels.forEach((panel) => {
115
- panel.removeAttribute(SAFE_LABELLEDBY);
116
- panel.hidden = true;
117
- });
118
- tabs.forEach((tab, index) => {
119
- const panel = getPanel(tab) || panels[index] || null;
120
- if (selected === tab && panel)
121
- selectedPanel = panel;
122
- tab.role = "tab";
123
- tab.tabIndex = selected === tab ? 0 : -1;
124
- tab.ariaSelected = `${selected === tab}`;
125
- tab.setAttribute(ARIA_CONTROLS, useId(panel));
126
- panel?.toggleAttribute("hidden", selectedPanel !== panel);
127
- panel?.setAttribute(
128
- SAFE_LABELLEDBY,
129
- useId(selectedPanel === panel ? selected : tab)
130
- );
116
+ const selected = this.selected || [...queryWithoutNested("u-tab", this.tabList || this)].every(
117
+ (tab) => tab.ariaSelected !== "true"
118
+ );
119
+ this.role = "tab";
120
+ this.tabIndex = selected ? 0 : -1;
121
+ this.ariaSelected = `${selected}`;
122
+ }
123
+ attributeChangedCallback(name, prev) {
124
+ if (!this.selected) return;
125
+ const nextPanel = getPanel(this);
126
+ const nextPanelId = useId(nextPanel);
127
+ if (this.tabList)
128
+ queryWithoutNested("u-tab", this.tabList).forEach((tab) => {
129
+ const isUnselect = tab !== this && tab.ariaSelected === "true";
130
+ const prevPanel = isUnselect && getPanel(tab);
131
+ if (prevPanel && prevPanel !== nextPanel) prevPanel.hidden = true;
132
+ if (isUnselect) {
133
+ tab.ariaSelected = "false";
134
+ tab.tabIndex = -1;
135
+ }
131
136
  });
132
- SKIP_ATTR_CHANGE = false;
133
- }
137
+ if (name === ARIA_CONTROLS) getPanel(this, prev)?.setAttribute("hidden", "");
138
+ if (this.getAttribute(ARIA_CONTROLS) !== nextPanelId)
139
+ this.setAttribute(ARIA_CONTROLS, nextPanelId);
140
+ this.tabIndex = 0;
141
+ nextPanel?.setAttribute(SAFE_LABELLEDBY, useId(this));
142
+ nextPanel?.removeAttribute("hidden");
134
143
  }
135
144
  get tabsElement() {
136
145
  return this.closest("u-tabs");
@@ -142,11 +151,12 @@ var UHTMLTabElement = class extends UHTMLElement {
142
151
  return this.ariaSelected === "true";
143
152
  }
144
153
  set selected(value) {
145
- this.ariaSelected = `${value}`;
154
+ this.ariaSelected = `${!!value}`;
146
155
  }
147
156
  /** Retrieves the ordinal position of an tab in a tablist. */
148
157
  get index() {
149
- return Array.from(this.tabsElement?.tabs || []).indexOf(this);
158
+ const tabList = this.tabList;
159
+ return tabList ? [...queryWithoutNested("u-tab", tabList)].indexOf(this) : 0;
150
160
  }
151
161
  get panel() {
152
162
  return getPanel(this);
@@ -159,33 +169,27 @@ var UHTMLTabPanelElement = class extends UHTMLElement {
159
169
  attachStyle(this, DISPLAY_BLOCK);
160
170
  }
161
171
  connectedCallback() {
172
+ this.hidden = [...this.tabs].every((tab) => tab.ariaSelected !== "true");
162
173
  this.role = "tabpanel";
163
- this.hidden = Array.from(this.tabs).every((tab) => !tab.selected);
164
- }
165
- attributeChangedCallback(_name, prev, next) {
166
- if (SKIP_ATTR_CHANGE || prev === next)
167
- return;
168
- getTabs(this, prev).forEach((tab) => tab.setAttribute(ARIA_CONTROLS, next));
169
174
  }
170
175
  get tabsElement() {
171
176
  return this.closest("u-tabs");
172
177
  }
173
178
  get tabs() {
174
- return getTabs(this, this.id);
179
+ const css = `u-tab[${ARIA_CONTROLS}="${this.id}"]`;
180
+ const root = getRoot(this).querySelectorAll(css);
181
+ return root.length ? root : document.querySelectorAll(css);
175
182
  }
176
183
  };
177
- UHTMLTabPanelElement.observedAttributes = ["id"];
178
184
  var queryWithoutNested = (tag, self) => {
179
- const selector = `${tag}:not(:scope ${self.nodeName}:not(:scope) ${tag})`;
180
- return self.querySelectorAll(selector);
181
- };
182
- var getPanel = (self) => {
183
- const css = `u-tabpanel[id="${self.getAttribute(ARIA_CONTROLS)}"]`;
184
- return getRoot(self).querySelector(css) || document.querySelector(css);
185
+ const css = `${tag}:not(:scope ${self.nodeName}:not(:scope) ${tag})`;
186
+ return self.querySelectorAll(css);
185
187
  };
186
- var getTabs = (self, id2) => {
187
- const css = `u-tab[${ARIA_CONTROLS}="${id2}"]`;
188
- return getRoot(self).querySelectorAll(css);
188
+ var getSelectedIndex = (tabs) => [...tabs].findIndex((tab) => tab.ariaSelected === "true");
189
+ var getPanel = (self, id2) => {
190
+ const css = `u-tabpanel[id="${id2 || self.getAttribute(ARIA_CONTROLS)}"]`;
191
+ const tabsElement = self.closest("u-tabs");
192
+ return getRoot(self).querySelector(css) || document.querySelector(css) || tabsElement && queryWithoutNested("u-tabpanel", tabsElement)[[...queryWithoutNested("u-tab", tabsElement)].indexOf(self)] || null;
189
193
  };
190
194
  customElements.define("u-tabs", UHTMLTabsElement);
191
195
  customElements.define("u-tablist", UHTMLTabListElement);
@@ -74,6 +74,21 @@
74
74
  "text": "UHTMLTabsElement | null"
75
75
  },
76
76
  "readonly": true
77
+ },
78
+ {
79
+ "kind": "field",
80
+ "name": "tabs",
81
+ "type": {
82
+ "text": "NodeListOf<UHTMLTabElement>"
83
+ },
84
+ "readonly": true
85
+ },
86
+ {
87
+ "kind": "field",
88
+ "name": "selectedIndex",
89
+ "type": {
90
+ "text": "number"
91
+ }
77
92
  }
78
93
  ],
79
94
  "superclass": {
@@ -166,11 +181,6 @@
166
181
  "readonly": true
167
182
  }
168
183
  ],
169
- "attributes": [
170
- {
171
- "name": "id"
172
- }
173
- ],
174
184
  "superclass": {
175
185
  "name": "UHTMLElement",
176
186
  "module": "//Users/eirikbacker/Library/Mobile%20Documents/com~apple~CloudDocs/5-capra/u-elements/packages/utils"
@@ -26,7 +26,7 @@
26
26
  {
27
27
  "name": "u-tabpanel",
28
28
  "description": "The `<u-tabpanel>` HTML element is a container for the resources of layered content associated with a `<u-tab>`.\n[MDN Reference](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/tabpanel_role)\n---\n",
29
- "attributes": [{ "name": "id", "values": [] }],
29
+ "attributes": [],
30
30
  "references": []
31
31
  }
32
32
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@u-elements/u-tabs",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "license": "MIT",
5
5
  "description": "HTML tags, just truly accessible",
6
6
  "homepage": "https://u-elements.github.io/u-elements/",