@u-elements/u-tabs 0.0.3 → 0.0.5

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
@@ -43,7 +43,7 @@ var UHTMLTabsElement = class extends UHTMLElement {
43
43
  return queryWithoutNested("u-tablist", this)[0] || null;
44
44
  }
45
45
  get selectedIndex() {
46
- return [...this.tabs].findIndex((tab) => tab.ariaSelected === "true");
46
+ return getSelectedIndex(this.tabs);
47
47
  }
48
48
  set selectedIndex(index) {
49
49
  if (this.tabs[index]) this.tabs[index].ariaSelected = "true";
@@ -70,26 +70,42 @@ var UHTMLTabListElement = class extends UHTMLElement {
70
70
  handleEvent(event) {
71
71
  const { key } = event;
72
72
  const tabs = [...this.getElementsByTagName("u-tab")];
73
- let index = tabs.findIndex((tab) => tab.contains(event.target));
74
- if (event.defaultPrevented || index === -1) return;
75
- if (event.type === "click") 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;
76
77
  if (event.type === "keydown" && !asButton(event)) {
77
78
  if (key === "ArrowDown" || key === "ArrowRight")
78
- index = ++index % tabs.length;
79
+ next = (prev + 1) % tabs.length;
79
80
  else if (key === "ArrowUp" || key === "ArrowLeft")
80
- index = (index || tabs.length) - 1;
81
- else if (key === "End") index = tabs.length - 1;
82
- else if (key === "Home") index = 0;
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);
83
85
  else return;
84
- event.preventDefault();
85
- tabs[index].focus();
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
+ }
86
94
  }
87
95
  }
88
96
  get tabsElement() {
89
97
  return this.closest("u-tabs");
90
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
+ }
91
108
  };
92
- var SKIP_ATTR_CHANGE = false;
93
109
  var UHTMLTabElement = class extends UHTMLElement {
94
110
  constructor() {
95
111
  super();
@@ -99,32 +115,32 @@ var UHTMLTabElement = class extends UHTMLElement {
99
115
  );
100
116
  }
101
117
  connectedCallback() {
102
- this.selected = !!this.selected;
103
- }
104
- attributeChangedCallback(_name, prev, next) {
105
- if (!SKIP_ATTR_CHANGE && prev !== next && (SKIP_ATTR_CHANGE = true)) {
106
- const { tabs = [], panels = [], selectedIndex } = this.tabsElement || {};
107
- const selected = this.selected ? this : tabs[selectedIndex || 0] || this;
108
- let selectedPanel;
109
- panels.forEach((panel) => {
110
- panel.removeAttribute(SAFE_LABELLEDBY);
111
- panel.hidden = true;
112
- });
113
- tabs.forEach((tab, index) => {
114
- const panel = getPanel(tab) || panels[index] || null;
115
- if (selected === tab && panel) selectedPanel = panel;
116
- tab.role = "tab";
117
- tab.tabIndex = selected === tab ? 0 : -1;
118
- tab.ariaSelected = `${selected === tab}`;
119
- tab.setAttribute(ARIA_CONTROLS, useId(panel));
120
- panel?.toggleAttribute("hidden", selectedPanel !== panel);
121
- panel?.setAttribute(
122
- SAFE_LABELLEDBY,
123
- useId(selectedPanel === panel ? selected : tab)
124
- );
118
+ const selected = this.selected || ![...queryWithoutNested("u-tab", this.tabList || this)].some(isSelected);
119
+ this.role = "tab";
120
+ this.tabIndex = selected ? 0 : -1;
121
+ this.ariaSelected = `${selected}`;
122
+ if (!this.hasAttribute(ARIA_CONTROLS))
123
+ this.setAttribute(ARIA_CONTROLS, useId(getPanel(this)));
124
+ }
125
+ attributeChangedCallback(name, prev) {
126
+ if (!this.selected) return;
127
+ const nextPanel = getPanel(this);
128
+ const nextPanelId = useId(nextPanel);
129
+ if (name === "aria-selected" && this.tabList)
130
+ queryWithoutNested("u-tab", this.tabList).forEach((tab) => {
131
+ if (tab !== this && isSelected(tab)) {
132
+ getPanel(tab)?.setAttribute("hidden", "");
133
+ tab.ariaSelected = "false";
134
+ tab.tabIndex = -1;
135
+ }
125
136
  });
126
- SKIP_ATTR_CHANGE = false;
127
- }
137
+ if (name === ARIA_CONTROLS && prev)
138
+ getPanel(this, prev)?.setAttribute("hidden", "");
139
+ if (this.getAttribute(ARIA_CONTROLS) !== nextPanelId)
140
+ this.setAttribute(ARIA_CONTROLS, nextPanelId);
141
+ this.tabIndex = 0;
142
+ nextPanel?.setAttribute(SAFE_LABELLEDBY, useId(this));
143
+ nextPanel?.removeAttribute("hidden");
128
144
  }
129
145
  get tabsElement() {
130
146
  return this.closest("u-tabs");
@@ -133,14 +149,15 @@ var UHTMLTabElement = class extends UHTMLElement {
133
149
  return this.closest("u-tablist");
134
150
  }
135
151
  get selected() {
136
- return this.ariaSelected === "true";
152
+ return isSelected(this);
137
153
  }
138
154
  set selected(value) {
139
- this.ariaSelected = `${value}`;
155
+ this.ariaSelected = `${!!value}`;
140
156
  }
141
157
  /** Retrieves the ordinal position of an tab in a tablist. */
142
158
  get index() {
143
- return Array.from(this.tabsElement?.tabs || []).indexOf(this);
159
+ const tabList = this.tabList;
160
+ return tabList ? [...queryWithoutNested("u-tab", tabList)].indexOf(this) : 0;
144
161
  }
145
162
  get panel() {
146
163
  return getPanel(this);
@@ -153,32 +170,28 @@ var UHTMLTabPanelElement = class extends UHTMLElement {
153
170
  attachStyle(this, DISPLAY_BLOCK);
154
171
  }
155
172
  connectedCallback() {
173
+ this.hidden = getSelectedIndex(this.tabs) === -1;
156
174
  this.role = "tabpanel";
157
- this.hidden = Array.from(this.tabs).every((tab) => !tab.selected);
158
- }
159
- attributeChangedCallback(_name, prev, next) {
160
- if (SKIP_ATTR_CHANGE || prev === next) return;
161
- getTabs(this, prev).forEach((tab) => tab.setAttribute(ARIA_CONTROLS, next));
162
175
  }
163
176
  get tabsElement() {
164
177
  return this.closest("u-tabs");
165
178
  }
166
179
  get tabs() {
167
- return getTabs(this, this.id);
180
+ const css = `u-tab[${ARIA_CONTROLS}="${this.id}"]`;
181
+ const root = getRoot(this).querySelectorAll(css);
182
+ return root.length ? root : document.querySelectorAll(css);
168
183
  }
169
184
  };
170
- UHTMLTabPanelElement.observedAttributes = ["id"];
171
185
  var queryWithoutNested = (tag, self) => {
172
- const selector = `${tag}:not(:scope ${self.nodeName}:not(:scope) ${tag})`;
173
- return self.querySelectorAll(selector);
174
- };
175
- var getPanel = (self) => {
176
- const css = `u-tabpanel[id="${self.getAttribute(ARIA_CONTROLS)}"]`;
177
- return getRoot(self).querySelector(css) || document.querySelector(css);
186
+ const css = `${tag}:not(:scope ${self.nodeName}:not(:scope) ${tag})`;
187
+ return self.querySelectorAll(css);
178
188
  };
179
- var getTabs = (self, id2) => {
180
- const css = `u-tab[${ARIA_CONTROLS}="${id2}"]`;
181
- return getRoot(self).querySelectorAll(css);
189
+ var isSelected = (tab) => tab.ariaSelected === "true";
190
+ var getSelectedIndex = (tabs) => [...tabs].findIndex(isSelected);
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;
182
195
  };
183
196
  customElements.define("u-tabs", UHTMLTabsElement);
184
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
@@ -41,7 +41,7 @@ var UHTMLTabsElement = class extends UHTMLElement {
41
41
  return queryWithoutNested("u-tablist", this)[0] || null;
42
42
  }
43
43
  get selectedIndex() {
44
- return [...this.tabs].findIndex((tab) => tab.ariaSelected === "true");
44
+ return getSelectedIndex(this.tabs);
45
45
  }
46
46
  set selectedIndex(index) {
47
47
  if (this.tabs[index]) this.tabs[index].ariaSelected = "true";
@@ -68,26 +68,42 @@ var UHTMLTabListElement = class extends UHTMLElement {
68
68
  handleEvent(event) {
69
69
  const { key } = event;
70
70
  const tabs = [...this.getElementsByTagName("u-tab")];
71
- let index = tabs.findIndex((tab) => tab.contains(event.target));
72
- if (event.defaultPrevented || index === -1) return;
73
- if (event.type === "click") 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;
74
75
  if (event.type === "keydown" && !asButton(event)) {
75
76
  if (key === "ArrowDown" || key === "ArrowRight")
76
- index = ++index % tabs.length;
77
+ next = (prev + 1) % tabs.length;
77
78
  else if (key === "ArrowUp" || key === "ArrowLeft")
78
- index = (index || tabs.length) - 1;
79
- else if (key === "End") index = tabs.length - 1;
80
- else if (key === "Home") index = 0;
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);
81
83
  else return;
82
- event.preventDefault();
83
- tabs[index].focus();
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
+ }
84
92
  }
85
93
  }
86
94
  get tabsElement() {
87
95
  return this.closest("u-tabs");
88
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
+ }
89
106
  };
90
- var SKIP_ATTR_CHANGE = false;
91
107
  var UHTMLTabElement = class extends UHTMLElement {
92
108
  constructor() {
93
109
  super();
@@ -97,32 +113,32 @@ var UHTMLTabElement = class extends UHTMLElement {
97
113
  );
98
114
  }
99
115
  connectedCallback() {
100
- this.selected = !!this.selected;
101
- }
102
- attributeChangedCallback(_name, prev, next) {
103
- if (!SKIP_ATTR_CHANGE && prev !== next && (SKIP_ATTR_CHANGE = true)) {
104
- const { tabs = [], panels = [], selectedIndex } = this.tabsElement || {};
105
- const selected = this.selected ? this : tabs[selectedIndex || 0] || this;
106
- let selectedPanel;
107
- panels.forEach((panel) => {
108
- panel.removeAttribute(SAFE_LABELLEDBY);
109
- panel.hidden = true;
110
- });
111
- tabs.forEach((tab, index) => {
112
- const panel = getPanel(tab) || panels[index] || null;
113
- if (selected === tab && panel) selectedPanel = panel;
114
- tab.role = "tab";
115
- tab.tabIndex = selected === tab ? 0 : -1;
116
- tab.ariaSelected = `${selected === tab}`;
117
- tab.setAttribute(ARIA_CONTROLS, useId(panel));
118
- panel?.toggleAttribute("hidden", selectedPanel !== panel);
119
- panel?.setAttribute(
120
- SAFE_LABELLEDBY,
121
- useId(selectedPanel === panel ? selected : tab)
122
- );
116
+ const selected = this.selected || ![...queryWithoutNested("u-tab", this.tabList || this)].some(isSelected);
117
+ this.role = "tab";
118
+ this.tabIndex = selected ? 0 : -1;
119
+ this.ariaSelected = `${selected}`;
120
+ if (!this.hasAttribute(ARIA_CONTROLS))
121
+ this.setAttribute(ARIA_CONTROLS, useId(getPanel(this)));
122
+ }
123
+ attributeChangedCallback(name, prev) {
124
+ if (!this.selected) return;
125
+ const nextPanel = getPanel(this);
126
+ const nextPanelId = useId(nextPanel);
127
+ if (name === "aria-selected" && this.tabList)
128
+ queryWithoutNested("u-tab", this.tabList).forEach((tab) => {
129
+ if (tab !== this && isSelected(tab)) {
130
+ getPanel(tab)?.setAttribute("hidden", "");
131
+ tab.ariaSelected = "false";
132
+ tab.tabIndex = -1;
133
+ }
123
134
  });
124
- SKIP_ATTR_CHANGE = false;
125
- }
135
+ if (name === ARIA_CONTROLS && prev)
136
+ getPanel(this, prev)?.setAttribute("hidden", "");
137
+ if (this.getAttribute(ARIA_CONTROLS) !== nextPanelId)
138
+ this.setAttribute(ARIA_CONTROLS, nextPanelId);
139
+ this.tabIndex = 0;
140
+ nextPanel?.setAttribute(SAFE_LABELLEDBY, useId(this));
141
+ nextPanel?.removeAttribute("hidden");
126
142
  }
127
143
  get tabsElement() {
128
144
  return this.closest("u-tabs");
@@ -131,14 +147,15 @@ var UHTMLTabElement = class extends UHTMLElement {
131
147
  return this.closest("u-tablist");
132
148
  }
133
149
  get selected() {
134
- return this.ariaSelected === "true";
150
+ return isSelected(this);
135
151
  }
136
152
  set selected(value) {
137
- this.ariaSelected = `${value}`;
153
+ this.ariaSelected = `${!!value}`;
138
154
  }
139
155
  /** Retrieves the ordinal position of an tab in a tablist. */
140
156
  get index() {
141
- return Array.from(this.tabsElement?.tabs || []).indexOf(this);
157
+ const tabList = this.tabList;
158
+ return tabList ? [...queryWithoutNested("u-tab", tabList)].indexOf(this) : 0;
142
159
  }
143
160
  get panel() {
144
161
  return getPanel(this);
@@ -151,32 +168,28 @@ var UHTMLTabPanelElement = class extends UHTMLElement {
151
168
  attachStyle(this, DISPLAY_BLOCK);
152
169
  }
153
170
  connectedCallback() {
171
+ this.hidden = getSelectedIndex(this.tabs) === -1;
154
172
  this.role = "tabpanel";
155
- this.hidden = Array.from(this.tabs).every((tab) => !tab.selected);
156
- }
157
- attributeChangedCallback(_name, prev, next) {
158
- if (SKIP_ATTR_CHANGE || prev === next) return;
159
- getTabs(this, prev).forEach((tab) => tab.setAttribute(ARIA_CONTROLS, next));
160
173
  }
161
174
  get tabsElement() {
162
175
  return this.closest("u-tabs");
163
176
  }
164
177
  get tabs() {
165
- return getTabs(this, this.id);
178
+ const css = `u-tab[${ARIA_CONTROLS}="${this.id}"]`;
179
+ const root = getRoot(this).querySelectorAll(css);
180
+ return root.length ? root : document.querySelectorAll(css);
166
181
  }
167
182
  };
168
- UHTMLTabPanelElement.observedAttributes = ["id"];
169
183
  var queryWithoutNested = (tag, self) => {
170
- const selector = `${tag}:not(:scope ${self.nodeName}:not(:scope) ${tag})`;
171
- return self.querySelectorAll(selector);
172
- };
173
- var getPanel = (self) => {
174
- const css = `u-tabpanel[id="${self.getAttribute(ARIA_CONTROLS)}"]`;
175
- return getRoot(self).querySelector(css) || document.querySelector(css);
184
+ const css = `${tag}:not(:scope ${self.nodeName}:not(:scope) ${tag})`;
185
+ return self.querySelectorAll(css);
176
186
  };
177
- var getTabs = (self, id2) => {
178
- const css = `u-tab[${ARIA_CONTROLS}="${id2}"]`;
179
- return getRoot(self).querySelectorAll(css);
187
+ var isSelected = (tab) => tab.ariaSelected === "true";
188
+ var getSelectedIndex = (tabs) => [...tabs].findIndex(isSelected);
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;
180
193
  };
181
194
  customElements.define("u-tabs", UHTMLTabsElement);
182
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.3",
3
+ "version": "0.0.5",
4
4
  "license": "MIT",
5
5
  "description": "HTML tags, just truly accessible",
6
6
  "homepage": "https://u-elements.github.io/u-elements/",