@newthink/ui 0.0.1 → 0.1.0

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.
@@ -0,0 +1,2 @@
1
+ export declare const tabs: import("./headless").HeadlessComponent;
2
+ //# sourceMappingURL=tabs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../src/headless/tabs.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,IAAI,wCA+ChB,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { headless } from './headless';
2
+ export const tabs = headless('tabs', (_el, _directive, { Alpine, evaluate }) => {
3
+ const tabGroup = Alpine.reactive({
4
+ activeTab: 0,
5
+ tabs: [],
6
+ panels: [],
7
+ $focus: evaluate('$focus'),
8
+ });
9
+ return tabGroup;
10
+ }, {
11
+ list: (tabGroup, el, _directive, { Alpine }) => {
12
+ const focusNext = () => tabGroup.$focus.wrap().next();
13
+ const focusPrevious = () => tabGroup.$focus.wrap().previous();
14
+ Alpine.bind(el, {
15
+ ':role': () => 'tablist',
16
+ '@keydown.arrow-left.stop.prevent': focusPrevious,
17
+ '@keydown.arrow-right.stop.prevent': focusNext,
18
+ '@keydown.arrow-up.stop.prevent': focusPrevious,
19
+ '@keydown.arrow-down.stop.prevent': focusNext,
20
+ });
21
+ },
22
+ tab: (tabGroup, el, _directive, { Alpine }) => {
23
+ const tabIndex = tabGroup.tabs.push(el) - 1;
24
+ Alpine.bind(el, {
25
+ ':tabindex': () => Number(tabIndex === tabGroup.activeTab) - 1,
26
+ ':aria-selected': () => tabIndex === tabGroup.activeTab,
27
+ '@click': () => tabGroup.$focus.focus(el),
28
+ '@focus': () => (tabGroup.activeTab = tabIndex),
29
+ });
30
+ },
31
+ panel: (tabGroup, el, _directive, { Alpine }) => {
32
+ const panelIndex = tabGroup.panels.push(el) - 1;
33
+ Alpine.bind(el, {
34
+ 'x-show': () => panelIndex === tabGroup.activeTab,
35
+ ':hidden': () => panelIndex !== tabGroup.activeTab,
36
+ ':aria-hidden': () => panelIndex !== tabGroup.activeTab,
37
+ });
38
+ },
39
+ });
40
+ //# sourceMappingURL=tabs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs.js","sourceRoot":"","sources":["../../src/headless/tabs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,MAAM,CAAC,MAAM,IAAI,GAAG,QAAQ,CAC1B,MAAM,EACN,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE;IACxC,MAAM,QAAQ,GAKV,MAAM,CAAC,QAAQ,CAAC;QAClB,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,EAAmB;QACzB,MAAM,EAAE,EAAmB;QAC3B,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAe;KACzC,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AAClB,CAAC,EACD;IACE,IAAI,EAAE,CAAC,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;QAC7C,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;QACtD,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC;QAE9D,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE;YACd,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS;YACxB,kCAAkC,EAAE,aAAa;YACjD,mCAAmC,EAAE,SAAS;YAC9C,gCAAgC,EAAE,aAAa;YAC/C,kCAAkC,EAAE,SAAS;SAC9C,CAAC,CAAC;IACL,CAAC;IACD,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;QAC5C,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE;YACd,WAAW,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC;YAC9D,gBAAgB,EAAE,GAAG,EAAE,CAAC,QAAQ,KAAK,QAAQ,CAAC,SAAS;YACvD,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACzC,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,SAAS,GAAG,QAAQ,CAAC;SAChD,CAAC,CAAC;IACL,CAAC;IACD,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;QAC9C,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;QAChD,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE;YACd,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU,KAAK,QAAQ,CAAC,SAAS;YACjD,SAAS,EAAE,GAAG,EAAE,CAAC,UAAU,KAAK,QAAQ,CAAC,SAAS;YAClD,cAAc,EAAE,GAAG,EAAE,CAAC,UAAU,KAAK,QAAQ,CAAC,SAAS;SACxD,CAAC,CAAC;IACL,CAAC;CACF,CACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGxD,eAAO,MAAM,EAAE,EAAE,iBAAsD,CAAC;AAExE,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAIxD,eAAO,MAAM,EAAE,EAAE,iBAA4D,CAAC;AAE9E,eAAe,EAAE,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { panel } from './headless/panel';
2
- export const UI = (Alpine) => Alpine.plugin([panel]);
2
+ import { tabs } from './headless/tabs';
3
+ export const UI = (Alpine) => Alpine.plugin([panel, tabs]);
3
4
  export default UI;
4
5
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC,MAAM,CAAC,MAAM,EAAE,GAAsB,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AAExE,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC,MAAM,CAAC,MAAM,EAAE,GAAsB,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;AAE9E,eAAe,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newthink/ui",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "Simple Alpine Headless UI Components",
5
5
  "exports": {
6
6
  ".": {
@@ -19,7 +19,7 @@
19
19
  "license": "ISC",
20
20
  "devDependencies": {
21
21
  "@trivago/prettier-plugin-sort-imports": "4.1.1",
22
- "@types/node": "20.2.3",
22
+ "@types/node": "20.2.4",
23
23
  "@typescript-eslint/eslint-plugin": "5.59.7",
24
24
  "@typescript-eslint/parser": "5.59.7",
25
25
  "@vitest/coverage-c8": "0.31.1",
@@ -37,7 +37,8 @@
37
37
  "vitest": "0.31.1"
38
38
  },
39
39
  "peerDependencies": {
40
- "alpinejs": "^3.10.0"
40
+ "alpinejs": "^3.10.0",
41
+ "@alpinejs/focus": "^3.10.0"
41
42
  },
42
43
  "prettier": {
43
44
  "singleQuote": true,
@@ -0,0 +1,76 @@
1
+ import { headless } from './headless';
2
+
3
+ export const tabs = headless(
4
+ 'tabs',
5
+ (_el, _directive, { Alpine, evaluate }) => {
6
+ const tabGroup: {
7
+ activeTab: number;
8
+ tabs: HTMLElement[];
9
+ panels: HTMLElement[];
10
+ $focus: FocusMagic;
11
+ } = Alpine.reactive({
12
+ activeTab: 0,
13
+ tabs: [] as HTMLElement[],
14
+ panels: [] as HTMLElement[],
15
+ $focus: evaluate('$focus') as FocusMagic,
16
+ });
17
+ return tabGroup;
18
+ },
19
+ {
20
+ list: (tabGroup, el, _directive, { Alpine }) => {
21
+ const focusNext = () => tabGroup.$focus.wrap().next();
22
+ const focusPrevious = () => tabGroup.$focus.wrap().previous();
23
+
24
+ Alpine.bind(el, {
25
+ ':role': () => 'tablist',
26
+ '@keydown.arrow-left.stop.prevent': focusPrevious,
27
+ '@keydown.arrow-right.stop.prevent': focusNext,
28
+ '@keydown.arrow-up.stop.prevent': focusPrevious,
29
+ '@keydown.arrow-down.stop.prevent': focusNext,
30
+ });
31
+ },
32
+ tab: (tabGroup, el, _directive, { Alpine }) => {
33
+ const tabIndex = tabGroup.tabs.push(el) - 1;
34
+ Alpine.bind(el, {
35
+ ':tabindex': () => Number(tabIndex === tabGroup.activeTab) - 1,
36
+ ':aria-selected': () => tabIndex === tabGroup.activeTab,
37
+ '@click': () => tabGroup.$focus.focus(el),
38
+ '@focus': () => (tabGroup.activeTab = tabIndex),
39
+ });
40
+ },
41
+ panel: (tabGroup, el, _directive, { Alpine }) => {
42
+ const panelIndex = tabGroup.panels.push(el) - 1;
43
+ Alpine.bind(el, {
44
+ 'x-show': () => panelIndex === tabGroup.activeTab,
45
+ ':hidden': () => panelIndex !== tabGroup.activeTab,
46
+ ':aria-hidden': () => panelIndex !== tabGroup.activeTab,
47
+ });
48
+ },
49
+ }
50
+ );
51
+
52
+ type FocusMagic = {
53
+ within(el: HTMLElement): FocusMagic;
54
+ withoutScrolling(): FocusMagic;
55
+ noscroll(): FocusMagic;
56
+ withWrapAround(): FocusMagic;
57
+ wrap(): FocusMagic;
58
+ focusable(el: HTMLElement): boolean;
59
+ previouslyFocused(): HTMLElement;
60
+ lastFocused(): HTMLElement;
61
+ focused(): HTMLElement;
62
+ focusables(): HTMLElement[];
63
+ all(): HTMLElement[];
64
+ isFirst(el: HTMLElement): boolean;
65
+ isLast(el: HTMLElement): boolean;
66
+ getFirst(): HTMLElement;
67
+ getLast(): HTMLElement;
68
+ getNext(): HTMLElement;
69
+ getPrevious(): HTMLElement;
70
+ first(): HTMLElement;
71
+ last(): HTMLElement;
72
+ next(): HTMLElement;
73
+ previous(): HTMLElement;
74
+ prev(): HTMLElement;
75
+ focus(el: HTMLElement): void;
76
+ };
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { HeadlessComponent } from './headless/headless';
2
2
  import { panel } from './headless/panel';
3
+ import { tabs } from './headless/tabs';
3
4
 
4
- export const UI: HeadlessComponent = (Alpine) => Alpine.plugin([panel]);
5
+ export const UI: HeadlessComponent = (Alpine) => Alpine.plugin([panel, tabs]);
5
6
 
6
7
  export default UI;