@u-elements/u-tabs 0.0.4 → 0.0.6

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
@@ -3,17 +3,21 @@
3
3
  // ../utils.ts
4
4
  var IS_BROWSER = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.navigator !== "undefined";
5
5
  var IS_ANDROID = IS_BROWSER && /android/i.test(navigator.userAgent);
6
+ IS_BROWSER && // @ts-expect-error Typescript has not implemented userAgentData yet https://stackoverflow.com/a/71392474
7
+ /^Mac/i.test(navigator.userAgentData?.platform || navigator.platform);
6
8
  var SAFE_LABELLEDBY = `${IS_ANDROID ? "data" : "aria"}-labelledby`;
7
9
  var DISPLAY_BLOCK = ":host(:not([hidden])) { display: block }";
8
10
  var UHTMLElement = typeof HTMLElement === "undefined" ? class {
9
11
  } : HTMLElement;
10
- var events = (action, element, rest) => rest[0].split(",").forEach((type) => {
11
- rest[0] = type;
12
- Element.prototype[`${action}EventListener`].apply(element, rest);
13
- });
12
+ var events = (action, element, rest) => {
13
+ for (const type of rest[0].split(",")) {
14
+ rest[0] = type;
15
+ Element.prototype[`${action}EventListener`].apply(element, rest);
16
+ }
17
+ };
14
18
  var on = (element, ...rest) => events("add", element, rest);
15
19
  var off = (element, ...rest) => events("remove", element, rest);
16
- var attachStyle = (element, css) => element.attachShadow({ mode: "closed" }).append(
20
+ var attachStyle = (element, css) => element.attachShadow({ mode: "open" }).append(
17
21
  createElement("slot"),
18
22
  // Unnamed slot does automatically render all top element nodes
19
23
  createElement("style", { textContent: css })
@@ -26,7 +30,11 @@ var asButton = (event) => {
26
30
  };
27
31
  var getRoot = (node) => node.getRootNode();
28
32
  var id = 0;
29
- var useId = (el) => el ? el.id || (el.id = `:${el.nodeName.toLowerCase()}${(++id).toString(32)}`) : "";
33
+ var useId = (el) => {
34
+ if (!el) return "";
35
+ if (!el.id) el.id = `:${el.nodeName.toLowerCase()}${(++id).toString(32)}`;
36
+ return el.id;
37
+ };
30
38
  var createElement = (tagName, props) => Object.assign(document.createElement(tagName), props);
31
39
  var customElements = {
32
40
  define: (name, instance) => !IS_BROWSER || window.customElements.get(name) || window.customElements.define(name, instance)
@@ -111,33 +119,32 @@ var UHTMLTabElement = class extends UHTMLElement {
111
119
  super();
112
120
  attachStyle(
113
121
  this,
114
- `:host(:not([hidden])) { cursor: pointer; display: inline-block }`
122
+ ":host(:not([hidden])) { cursor: pointer; display: inline-block }"
115
123
  );
116
124
  }
117
125
  connectedCallback() {
118
- const selected = this.selected || [...queryWithoutNested("u-tab", this.tabList || this)].every(
119
- (tab) => tab.ariaSelected !== "true"
120
- );
126
+ const selected = this.selected || ![...queryWithoutNested("u-tab", this.tabList || this)].some(isSelected);
121
127
  this.role = "tab";
122
128
  this.tabIndex = selected ? 0 : -1;
123
129
  this.ariaSelected = `${selected}`;
130
+ if (!this.hasAttribute(ARIA_CONTROLS))
131
+ this.setAttribute(ARIA_CONTROLS, useId(getPanel(this)));
124
132
  }
125
133
  attributeChangedCallback(name, prev) {
126
134
  if (!this.selected) return;
127
135
  const nextPanel = getPanel(this);
128
136
  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) {
137
+ if (name === "aria-selected" && this.tabList)
138
+ for (const tab of queryWithoutNested("u-tab", this.tabList)) {
139
+ if (tab !== this && isSelected(tab)) {
140
+ getPanel(tab)?.setAttribute("hidden", "");
135
141
  tab.ariaSelected = "false";
136
142
  tab.tabIndex = -1;
137
143
  }
138
- });
139
- if (name === ARIA_CONTROLS) getPanel(this, prev)?.setAttribute("hidden", "");
140
- if (this.getAttribute(ARIA_CONTROLS) !== nextPanelId)
144
+ }
145
+ if (name === ARIA_CONTROLS && prev)
146
+ getPanel(this, prev)?.setAttribute("hidden", "");
147
+ if (nextPanel && this.getAttribute(ARIA_CONTROLS) !== nextPanelId)
141
148
  this.setAttribute(ARIA_CONTROLS, nextPanelId);
142
149
  this.tabIndex = 0;
143
150
  nextPanel?.setAttribute(SAFE_LABELLEDBY, useId(this));
@@ -150,7 +157,7 @@ var UHTMLTabElement = class extends UHTMLElement {
150
157
  return this.closest("u-tablist");
151
158
  }
152
159
  get selected() {
153
- return this.ariaSelected === "true";
160
+ return isSelected(this);
154
161
  }
155
162
  set selected(value) {
156
163
  this.ariaSelected = `${!!value}`;
@@ -171,8 +178,14 @@ var UHTMLTabPanelElement = class extends UHTMLElement {
171
178
  attachStyle(this, DISPLAY_BLOCK);
172
179
  }
173
180
  connectedCallback() {
174
- this.hidden = [...this.tabs].every((tab) => tab.ariaSelected !== "true");
181
+ this.hidden = getSelectedIndex(this.tabs) === -1;
175
182
  this.role = "tabpanel";
183
+ this.attributeChangedCallback();
184
+ }
185
+ attributeChangedCallback() {
186
+ if (this.hidden || isFocusable(this.firstElementChild))
187
+ this.removeAttribute("tabindex");
188
+ else this.tabIndex = 0;
176
189
  }
177
190
  get tabsElement() {
178
191
  return this.closest("u-tabs");
@@ -183,15 +196,20 @@ var UHTMLTabPanelElement = class extends UHTMLElement {
183
196
  return root.length ? root : document.querySelectorAll(css);
184
197
  }
185
198
  };
186
- var queryWithoutNested = (tag, self) => {
187
- const css = `${tag}:not(:scope ${self.nodeName}:not(:scope) ${tag})`;
188
- return self.querySelectorAll(css);
189
- };
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;
199
+ UHTMLTabPanelElement.observedAttributes = ["hidden"];
200
+ var queryWithoutNested = (tag, self) => self.querySelectorAll(
201
+ `${tag}:not(:scope ${self.nodeName}:not(:scope) ${tag})`
202
+ );
203
+ var isSelected = (tab) => tab.ariaSelected === "true";
204
+ var getSelectedIndex = (tabs) => [...tabs].findIndex(isSelected);
205
+ var isFocusable = (el) => el instanceof Element && el.matches(
206
+ `:is([contenteditable],[controls],[href],[tabindex],input:not([type="hidden"]),select,textarea,button,summary,iframe):not(:disabled,[tabindex^="-"])`
207
+ );
208
+ var getPanel = (tab, id2) => {
209
+ const panelId = id2 || tab.getAttribute(ARIA_CONTROLS);
210
+ const panelSelector = `u-tabpanel[id="${panelId}"]`;
211
+ const tabsElement = tab.closest("u-tabs");
212
+ return panelId && getRoot(tab).querySelector(panelSelector) || panelId && getRoot(tab).querySelector(panelSelector) || tabsElement && queryWithoutNested("u-tabpanel", tabsElement)[[...queryWithoutNested("u-tab", tabsElement)].indexOf(tab)] || null;
195
213
  };
196
214
  customElements.define("u-tabs", UHTMLTabsElement);
197
215
  customElements.define("u-tablist", UHTMLTabListElement);
package/dist/u-tabs.d.cts CHANGED
@@ -5,10 +5,10 @@ declare const UHTMLElement: {
5
5
 
6
6
  declare global {
7
7
  interface HTMLElementTagNameMap {
8
- 'u-tabs': UHTMLTabsElement;
9
- 'u-tablist': UHTMLTabListElement;
10
- 'u-tab': UHTMLTabElement;
11
- 'u-tabpanel': UHTMLTabPanelElement;
8
+ "u-tabs": UHTMLTabsElement;
9
+ "u-tablist": UHTMLTabListElement;
10
+ "u-tab": UHTMLTabElement;
11
+ "u-tabpanel": UHTMLTabPanelElement;
12
12
  }
13
13
  }
14
14
  /**
@@ -59,8 +59,10 @@ declare class UHTMLTabElement extends UHTMLElement {
59
59
  * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/tabpanel_role)
60
60
  */
61
61
  declare class UHTMLTabPanelElement extends UHTMLElement {
62
+ static observedAttributes: string[];
62
63
  constructor();
63
64
  connectedCallback(): void;
65
+ attributeChangedCallback(): void;
64
66
  get tabsElement(): UHTMLTabsElement | null;
65
67
  get tabs(): NodeListOf<UHTMLTabElement>;
66
68
  }
package/dist/u-tabs.d.ts CHANGED
@@ -5,10 +5,10 @@ declare const UHTMLElement: {
5
5
 
6
6
  declare global {
7
7
  interface HTMLElementTagNameMap {
8
- 'u-tabs': UHTMLTabsElement;
9
- 'u-tablist': UHTMLTabListElement;
10
- 'u-tab': UHTMLTabElement;
11
- 'u-tabpanel': UHTMLTabPanelElement;
8
+ "u-tabs": UHTMLTabsElement;
9
+ "u-tablist": UHTMLTabListElement;
10
+ "u-tab": UHTMLTabElement;
11
+ "u-tabpanel": UHTMLTabPanelElement;
12
12
  }
13
13
  }
14
14
  /**
@@ -59,8 +59,10 @@ declare class UHTMLTabElement extends UHTMLElement {
59
59
  * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/tabpanel_role)
60
60
  */
61
61
  declare class UHTMLTabPanelElement extends UHTMLElement {
62
+ static observedAttributes: string[];
62
63
  constructor();
63
64
  connectedCallback(): void;
65
+ attributeChangedCallback(): void;
64
66
  get tabsElement(): UHTMLTabsElement | null;
65
67
  get tabs(): NodeListOf<UHTMLTabElement>;
66
68
  }
package/dist/u-tabs.js CHANGED
@@ -1,17 +1,21 @@
1
1
  // ../utils.ts
2
2
  var IS_BROWSER = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.navigator !== "undefined";
3
3
  var IS_ANDROID = IS_BROWSER && /android/i.test(navigator.userAgent);
4
+ IS_BROWSER && // @ts-expect-error Typescript has not implemented userAgentData yet https://stackoverflow.com/a/71392474
5
+ /^Mac/i.test(navigator.userAgentData?.platform || navigator.platform);
4
6
  var SAFE_LABELLEDBY = `${IS_ANDROID ? "data" : "aria"}-labelledby`;
5
7
  var DISPLAY_BLOCK = ":host(:not([hidden])) { display: block }";
6
8
  var UHTMLElement = typeof HTMLElement === "undefined" ? class {
7
9
  } : HTMLElement;
8
- var events = (action, element, rest) => rest[0].split(",").forEach((type) => {
9
- rest[0] = type;
10
- Element.prototype[`${action}EventListener`].apply(element, rest);
11
- });
10
+ var events = (action, element, rest) => {
11
+ for (const type of rest[0].split(",")) {
12
+ rest[0] = type;
13
+ Element.prototype[`${action}EventListener`].apply(element, rest);
14
+ }
15
+ };
12
16
  var on = (element, ...rest) => events("add", element, rest);
13
17
  var off = (element, ...rest) => events("remove", element, rest);
14
- var attachStyle = (element, css) => element.attachShadow({ mode: "closed" }).append(
18
+ var attachStyle = (element, css) => element.attachShadow({ mode: "open" }).append(
15
19
  createElement("slot"),
16
20
  // Unnamed slot does automatically render all top element nodes
17
21
  createElement("style", { textContent: css })
@@ -24,7 +28,11 @@ var asButton = (event) => {
24
28
  };
25
29
  var getRoot = (node) => node.getRootNode();
26
30
  var id = 0;
27
- var useId = (el) => el ? el.id || (el.id = `:${el.nodeName.toLowerCase()}${(++id).toString(32)}`) : "";
31
+ var useId = (el) => {
32
+ if (!el) return "";
33
+ if (!el.id) el.id = `:${el.nodeName.toLowerCase()}${(++id).toString(32)}`;
34
+ return el.id;
35
+ };
28
36
  var createElement = (tagName, props) => Object.assign(document.createElement(tagName), props);
29
37
  var customElements = {
30
38
  define: (name, instance) => !IS_BROWSER || window.customElements.get(name) || window.customElements.define(name, instance)
@@ -109,33 +117,32 @@ var UHTMLTabElement = class extends UHTMLElement {
109
117
  super();
110
118
  attachStyle(
111
119
  this,
112
- `:host(:not([hidden])) { cursor: pointer; display: inline-block }`
120
+ ":host(:not([hidden])) { cursor: pointer; display: inline-block }"
113
121
  );
114
122
  }
115
123
  connectedCallback() {
116
- const selected = this.selected || [...queryWithoutNested("u-tab", this.tabList || this)].every(
117
- (tab) => tab.ariaSelected !== "true"
118
- );
124
+ const selected = this.selected || ![...queryWithoutNested("u-tab", this.tabList || this)].some(isSelected);
119
125
  this.role = "tab";
120
126
  this.tabIndex = selected ? 0 : -1;
121
127
  this.ariaSelected = `${selected}`;
128
+ if (!this.hasAttribute(ARIA_CONTROLS))
129
+ this.setAttribute(ARIA_CONTROLS, useId(getPanel(this)));
122
130
  }
123
131
  attributeChangedCallback(name, prev) {
124
132
  if (!this.selected) return;
125
133
  const nextPanel = getPanel(this);
126
134
  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) {
135
+ if (name === "aria-selected" && this.tabList)
136
+ for (const tab of queryWithoutNested("u-tab", this.tabList)) {
137
+ if (tab !== this && isSelected(tab)) {
138
+ getPanel(tab)?.setAttribute("hidden", "");
133
139
  tab.ariaSelected = "false";
134
140
  tab.tabIndex = -1;
135
141
  }
136
- });
137
- if (name === ARIA_CONTROLS) getPanel(this, prev)?.setAttribute("hidden", "");
138
- if (this.getAttribute(ARIA_CONTROLS) !== nextPanelId)
142
+ }
143
+ if (name === ARIA_CONTROLS && prev)
144
+ getPanel(this, prev)?.setAttribute("hidden", "");
145
+ if (nextPanel && this.getAttribute(ARIA_CONTROLS) !== nextPanelId)
139
146
  this.setAttribute(ARIA_CONTROLS, nextPanelId);
140
147
  this.tabIndex = 0;
141
148
  nextPanel?.setAttribute(SAFE_LABELLEDBY, useId(this));
@@ -148,7 +155,7 @@ var UHTMLTabElement = class extends UHTMLElement {
148
155
  return this.closest("u-tablist");
149
156
  }
150
157
  get selected() {
151
- return this.ariaSelected === "true";
158
+ return isSelected(this);
152
159
  }
153
160
  set selected(value) {
154
161
  this.ariaSelected = `${!!value}`;
@@ -169,8 +176,14 @@ var UHTMLTabPanelElement = class extends UHTMLElement {
169
176
  attachStyle(this, DISPLAY_BLOCK);
170
177
  }
171
178
  connectedCallback() {
172
- this.hidden = [...this.tabs].every((tab) => tab.ariaSelected !== "true");
179
+ this.hidden = getSelectedIndex(this.tabs) === -1;
173
180
  this.role = "tabpanel";
181
+ this.attributeChangedCallback();
182
+ }
183
+ attributeChangedCallback() {
184
+ if (this.hidden || isFocusable(this.firstElementChild))
185
+ this.removeAttribute("tabindex");
186
+ else this.tabIndex = 0;
174
187
  }
175
188
  get tabsElement() {
176
189
  return this.closest("u-tabs");
@@ -181,15 +194,20 @@ var UHTMLTabPanelElement = class extends UHTMLElement {
181
194
  return root.length ? root : document.querySelectorAll(css);
182
195
  }
183
196
  };
184
- var queryWithoutNested = (tag, self) => {
185
- const css = `${tag}:not(:scope ${self.nodeName}:not(:scope) ${tag})`;
186
- return self.querySelectorAll(css);
187
- };
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;
197
+ UHTMLTabPanelElement.observedAttributes = ["hidden"];
198
+ var queryWithoutNested = (tag, self) => self.querySelectorAll(
199
+ `${tag}:not(:scope ${self.nodeName}:not(:scope) ${tag})`
200
+ );
201
+ var isSelected = (tab) => tab.ariaSelected === "true";
202
+ var getSelectedIndex = (tabs) => [...tabs].findIndex(isSelected);
203
+ var isFocusable = (el) => el instanceof Element && el.matches(
204
+ `:is([contenteditable],[controls],[href],[tabindex],input:not([type="hidden"]),select,textarea,button,summary,iframe):not(:disabled,[tabindex^="-"])`
205
+ );
206
+ var getPanel = (tab, id2) => {
207
+ const panelId = id2 || tab.getAttribute(ARIA_CONTROLS);
208
+ const panelSelector = `u-tabpanel[id="${panelId}"]`;
209
+ const tabsElement = tab.closest("u-tabs");
210
+ return panelId && getRoot(tab).querySelector(panelSelector) || panelId && getRoot(tab).querySelector(panelSelector) || tabsElement && queryWithoutNested("u-tabpanel", tabsElement)[[...queryWithoutNested("u-tab", tabsElement)].indexOf(tab)] || null;
193
211
  };
194
212
  customElements.define("u-tabs", UHTMLTabsElement);
195
213
  customElements.define("u-tablist", UHTMLTabListElement);
@@ -181,6 +181,11 @@
181
181
  "readonly": true
182
182
  }
183
183
  ],
184
+ "attributes": [
185
+ {
186
+ "name": "hidden"
187
+ }
188
+ ],
184
189
  "superclass": {
185
190
  "name": "UHTMLElement",
186
191
  "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": [],
29
+ "attributes": [{ "name": "hidden", "values": [] }],
30
30
  "references": []
31
31
  }
32
32
  ]
package/package.json CHANGED
@@ -1,25 +1,25 @@
1
1
  {
2
- "name": "@u-elements/u-tabs",
3
- "version": "0.0.4",
4
- "license": "MIT",
5
- "description": "HTML tags, just truly accessible",
6
- "homepage": "https://u-elements.github.io/u-elements/",
7
- "type": "module",
8
- "main": "dist/u-tabs.cjs",
9
- "module": "dist/u-tabs.js",
10
- "types": "dist/u-tabs.d.ts",
11
- "customElements": "dist/u-tabs.manifest.json",
12
- "files": [
13
- "dist"
14
- ],
15
- "repository": {
16
- "type": "git",
17
- "url": "https://github.com/u-elements/u-elements.git"
18
- },
19
- "bugs": {
20
- "url": "https://github.com/u-elements/u-elements/issues"
21
- },
22
- "scripts": {
23
- "build": "tsup --config ../../tsup.config.ts"
24
- }
2
+ "name": "@u-elements/u-tabs",
3
+ "version": "0.0.6",
4
+ "license": "MIT",
5
+ "description": "HTML tags, just truly accessible",
6
+ "homepage": "https://u-elements.github.io/u-elements/",
7
+ "type": "module",
8
+ "main": "dist/u-tabs.cjs",
9
+ "module": "dist/u-tabs.js",
10
+ "types": "dist/u-tabs.d.ts",
11
+ "customElements": "dist/u-tabs.manifest.json",
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/u-elements/u-elements.git"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/u-elements/u-elements/issues"
21
+ },
22
+ "scripts": {
23
+ "build": "tsup --config ../../tsup.config.ts"
24
+ }
25
25
  }