@u-elements/u-tabs 0.0.5 → 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,7 +119,7 @@ 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() {
@@ -127,16 +135,16 @@ var UHTMLTabElement = class extends UHTMLElement {
127
135
  const nextPanel = getPanel(this);
128
136
  const nextPanelId = useId(nextPanel);
129
137
  if (name === "aria-selected" && this.tabList)
130
- queryWithoutNested("u-tab", this.tabList).forEach((tab) => {
138
+ for (const tab of queryWithoutNested("u-tab", this.tabList)) {
131
139
  if (tab !== this && isSelected(tab)) {
132
140
  getPanel(tab)?.setAttribute("hidden", "");
133
141
  tab.ariaSelected = "false";
134
142
  tab.tabIndex = -1;
135
143
  }
136
- });
144
+ }
137
145
  if (name === ARIA_CONTROLS && prev)
138
146
  getPanel(this, prev)?.setAttribute("hidden", "");
139
- if (this.getAttribute(ARIA_CONTROLS) !== nextPanelId)
147
+ if (nextPanel && this.getAttribute(ARIA_CONTROLS) !== nextPanelId)
140
148
  this.setAttribute(ARIA_CONTROLS, nextPanelId);
141
149
  this.tabIndex = 0;
142
150
  nextPanel?.setAttribute(SAFE_LABELLEDBY, useId(this));
@@ -172,6 +180,12 @@ var UHTMLTabPanelElement = class extends UHTMLElement {
172
180
  connectedCallback() {
173
181
  this.hidden = getSelectedIndex(this.tabs) === -1;
174
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;
175
189
  }
176
190
  get tabsElement() {
177
191
  return this.closest("u-tabs");
@@ -182,16 +196,20 @@ var UHTMLTabPanelElement = class extends UHTMLElement {
182
196
  return root.length ? root : document.querySelectorAll(css);
183
197
  }
184
198
  };
185
- var queryWithoutNested = (tag, self) => {
186
- const css = `${tag}:not(:scope ${self.nodeName}:not(:scope) ${tag})`;
187
- return self.querySelectorAll(css);
188
- };
199
+ UHTMLTabPanelElement.observedAttributes = ["hidden"];
200
+ var queryWithoutNested = (tag, self) => self.querySelectorAll(
201
+ `${tag}:not(:scope ${self.nodeName}:not(:scope) ${tag})`
202
+ );
189
203
  var isSelected = (tab) => tab.ariaSelected === "true";
190
204
  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;
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,7 +117,7 @@ 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() {
@@ -125,16 +133,16 @@ var UHTMLTabElement = class extends UHTMLElement {
125
133
  const nextPanel = getPanel(this);
126
134
  const nextPanelId = useId(nextPanel);
127
135
  if (name === "aria-selected" && this.tabList)
128
- queryWithoutNested("u-tab", this.tabList).forEach((tab) => {
136
+ for (const tab of queryWithoutNested("u-tab", this.tabList)) {
129
137
  if (tab !== this && isSelected(tab)) {
130
138
  getPanel(tab)?.setAttribute("hidden", "");
131
139
  tab.ariaSelected = "false";
132
140
  tab.tabIndex = -1;
133
141
  }
134
- });
142
+ }
135
143
  if (name === ARIA_CONTROLS && prev)
136
144
  getPanel(this, prev)?.setAttribute("hidden", "");
137
- if (this.getAttribute(ARIA_CONTROLS) !== nextPanelId)
145
+ if (nextPanel && this.getAttribute(ARIA_CONTROLS) !== nextPanelId)
138
146
  this.setAttribute(ARIA_CONTROLS, nextPanelId);
139
147
  this.tabIndex = 0;
140
148
  nextPanel?.setAttribute(SAFE_LABELLEDBY, useId(this));
@@ -170,6 +178,12 @@ var UHTMLTabPanelElement = class extends UHTMLElement {
170
178
  connectedCallback() {
171
179
  this.hidden = getSelectedIndex(this.tabs) === -1;
172
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;
173
187
  }
174
188
  get tabsElement() {
175
189
  return this.closest("u-tabs");
@@ -180,16 +194,20 @@ var UHTMLTabPanelElement = class extends UHTMLElement {
180
194
  return root.length ? root : document.querySelectorAll(css);
181
195
  }
182
196
  };
183
- var queryWithoutNested = (tag, self) => {
184
- const css = `${tag}:not(:scope ${self.nodeName}:not(:scope) ${tag})`;
185
- return self.querySelectorAll(css);
186
- };
197
+ UHTMLTabPanelElement.observedAttributes = ["hidden"];
198
+ var queryWithoutNested = (tag, self) => self.querySelectorAll(
199
+ `${tag}:not(:scope ${self.nodeName}:not(:scope) ${tag})`
200
+ );
187
201
  var isSelected = (tab) => tab.ariaSelected === "true";
188
202
  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;
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.5",
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
  }