@newthink/ui 0.0.1

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,9 @@
1
+ import type { Alpine } from 'alpinejs';
2
+ import type { DirectiveData, ElementWithXAttributes, Utilities } from 'alpinejs/dist/types';
3
+ export declare const headless: <T extends Record<string, unknown>>(name: string, rootHandler: RootHandler<T>, handlers?: HeadlessHandlers<T> | undefined) => HeadlessComponent;
4
+ type HeadlessHandlers<T extends Record<string, unknown>> = Record<string, HeadlessHandler<T>>;
5
+ export type RootHandler<T> = (el: ElementWithXAttributes, directive: DirectiveData, utilities: Utilities) => T;
6
+ export type HeadlessHandler<T> = (state: T, el: ElementWithXAttributes, directive: DirectiveData, utilities: Utilities) => void;
7
+ export type HeadlessComponent = (Alpine: Alpine) => void;
8
+ export {};
9
+ //# sourceMappingURL=headless.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headless.d.ts","sourceRoot":"","sources":["../../src/headless/headless.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EACV,aAAa,EACb,sBAAsB,EACtB,SAAS,EACV,MAAM,qBAAqB,CAAC;AAE7B,eAAO,MAAM,QAAQ,4CACb,MAAM,8EAGX,iBA2BF,CAAC;AAeF,KAAK,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,MAAM,CAC/D,MAAM,EACN,eAAe,CAAC,CAAC,CAAC,CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAC3B,EAAE,EAAE,sBAAsB,EAC1B,SAAS,EAAE,aAAa,EACxB,SAAS,EAAE,SAAS,KACjB,CAAC,CAAC;AAEP,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,CAC/B,KAAK,EAAE,CAAC,EACR,EAAE,EAAE,sBAAsB,EAC1B,SAAS,EAAE,aAAa,EACxB,SAAS,EAAE,SAAS,KACjB,IAAI,CAAC;AAEV,MAAM,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC"}
@@ -0,0 +1,25 @@
1
+ export const headless = (name, rootHandler, handlers) => {
2
+ const headlessMap = new WeakMap();
3
+ return (Alpine) => {
4
+ const getHeadlessComponentState = (el) => {
5
+ const root = Alpine.findClosest(el, headlessMap.has.bind(headlessMap));
6
+ if (!root)
7
+ return console.error(`Could not find root for ${name} component`, el);
8
+ return headlessMap.get(root);
9
+ };
10
+ Alpine.magic(name, (el) => getHeadlessComponentState(el));
11
+ Alpine.directive(name, (el, directive, utils) => {
12
+ if (!directive.value)
13
+ return headlessMap.set(el, rootHandler(el, directive, utils));
14
+ const headlessState = getHeadlessComponentState(el);
15
+ if (!headlessState)
16
+ return missingState(name, el);
17
+ (handlers?.[directive.value] || failback(name))(headlessState, el, directive, utils);
18
+ });
19
+ };
20
+ };
21
+ const failback = (name) => (_, el, directive) => {
22
+ console.error(`Alpine ${name}: Invalid directive value`, el, directive.original);
23
+ };
24
+ const missingState = (name, el) => console.error(`Could not find root for ${name} component`, el);
25
+ //# sourceMappingURL=headless.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headless.js","sourceRoot":"","sources":["../../src/headless/headless.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,IAAY,EACZ,WAA2B,EAC3B,QAA8B,EACX,EAAE;IACrB,MAAM,WAAW,GAAG,IAAI,OAAO,EAA6B,CAAC;IAC7D,OAAO,CAAC,MAAc,EAAE,EAAE;QACxB,MAAM,yBAAyB,GAAG,CAChC,EAA0B,EAChB,EAAE;YACZ,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,EAAE,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;YACvE,IAAI,CAAC,IAAI;gBACP,OAAO,OAAO,CAAC,KAAK,CAAC,2BAA2B,IAAI,YAAY,EAAE,EAAE,CAAC,CAAC;YACxE,OAAO,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC,CAAC;QAEF,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,yBAAyB,CAAC,EAAE,CAAC,CAAC,CAAC;QAE1D,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE;YAC9C,IAAI,CAAC,SAAS,CAAC,KAAK;gBAClB,OAAO,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;YAChE,MAAM,aAAa,GAAG,yBAAyB,CAAC,EAAE,CAAC,CAAC;YACpD,IAAI,CAAC,aAAa;gBAAE,OAAO,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAClD,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAC7C,aAAa,EACb,EAAE,EACF,SAAS,EACT,KAAK,CACN,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,QAAQ,GACZ,CAAC,IAAY,EAAE,EAAE,CACjB,CAAC,CAAU,EAAE,EAA0B,EAAE,SAAwB,EAAE,EAAE;IACnE,OAAO,CAAC,KAAK,CACX,UAAU,IAAI,2BAA2B,EACzC,EAAE,EACF,SAAS,CAAC,QAAQ,CACnB,CAAC;AACJ,CAAC,CAAC;AAEJ,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,EAA0B,EAAE,EAAE,CAChE,OAAO,CAAC,KAAK,CAAC,2BAA2B,IAAI,YAAY,EAAE,EAAE,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const panel: import("./headless").HeadlessComponent;
2
+ //# sourceMappingURL=panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../../src/headless/panel.ts"],"names":[],"mappings":"AA8BA,eAAO,MAAM,KAAK,wCAgBhB,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { headless } from './headless';
2
+ const handleRoot = (el, _, { Alpine }) => {
3
+ const panelState = Alpine.reactive({
4
+ isOpen: false,
5
+ open() {
6
+ this.isOpen = true;
7
+ },
8
+ close() {
9
+ this.isOpen = false;
10
+ },
11
+ toggle() {
12
+ return this.isOpen ? this.close() : this.open();
13
+ },
14
+ });
15
+ Alpine.bind(el, {
16
+ 'x-id': () => ['x-panel'],
17
+ });
18
+ return panelState;
19
+ };
20
+ export const panel = headless('panel', handleRoot, {
21
+ summary: (panelState, el, _, { Alpine }) => {
22
+ Alpine.bind(el, {
23
+ ':aria-expanded': () => panelState.isOpen,
24
+ ':aria-controls': '$id("x-panel")',
25
+ });
26
+ },
27
+ content: (panelState, el, _, { Alpine }) => {
28
+ Alpine.bind(el, {
29
+ 'x-show': () => panelState.isOpen,
30
+ 'x-transition:enter': 'transition-transform',
31
+ 'x-transition:enter-start': 'translate-x-full',
32
+ 'x-transition:leave': 'transition-transform',
33
+ 'x-transition:leave-end': 'translate-x-full',
34
+ });
35
+ },
36
+ });
37
+ //# sourceMappingURL=panel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panel.js","sourceRoot":"","sources":["../../src/headless/panel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,QAAQ,EAAE,MAAM,YAAY,CAAC;AASnD,MAAM,UAAU,GAA4B,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;IAChE,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC;QACjC,MAAM,EAAE,KAAK;QACb,IAAI;YACF,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,CAAC;QACD,KAAK;YACH,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACtB,CAAC;QACD,MAAM;YACJ,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAClD,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE;QACd,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,SAAS,CAAC;KAC1B,CAAC,CAAC;IAEH,OAAO,UAAU,CAAC;AACpB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,EAAE,UAAU,EAAE;IACjD,OAAO,EAAE,CAAC,UAAU,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;QACzC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE;YACd,gBAAgB,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM;YACzC,gBAAgB,EAAE,gBAAgB;SACnC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,CAAC,UAAU,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;QACzC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE;YACd,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM;YACjC,oBAAoB,EAAE,sBAAsB;YAC5C,0BAA0B,EAAE,kBAAkB;YAC9C,oBAAoB,EAAE,sBAAsB;YAC5C,wBAAwB,EAAE,kBAAkB;SAC7C,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { HeadlessComponent } from './headless/headless';
2
+ export declare const UI: HeadlessComponent;
3
+ export default UI;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import { panel } from './headless/panel';
2
+ export const UI = (Alpine) => Alpine.plugin([panel]);
3
+ export default UI;
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"}
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@newthink/ui",
3
+ "version": "0.0.1",
4
+ "description": "Simple Alpine Headless UI Components",
5
+ "exports": {
6
+ ".": {
7
+ "import": "./dist/index.js",
8
+ "types": "./dist/index.d.ts"
9
+ },
10
+ "./package.json": "./package.json"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "src"
15
+ ],
16
+ "type": "module",
17
+ "keywords": [],
18
+ "author": "",
19
+ "license": "ISC",
20
+ "devDependencies": {
21
+ "@trivago/prettier-plugin-sort-imports": "4.1.1",
22
+ "@types/node": "20.2.3",
23
+ "@typescript-eslint/eslint-plugin": "5.59.7",
24
+ "@typescript-eslint/parser": "5.59.7",
25
+ "@vitest/coverage-c8": "0.31.1",
26
+ "alpinejs": "npm:@ekwoka/alpinets@0.0.1-alpha",
27
+ "esbuild": "0.17.19",
28
+ "eslint": "8.41.0",
29
+ "eslint-plugin-filename-export": "1.0.3",
30
+ "husky": "8.0.3",
31
+ "lint-staged": "13.2.2",
32
+ "prettier": "2.8.8",
33
+ "pretty-bytes": "^6.1.0",
34
+ "typescript": "5.0.4",
35
+ "vite": "4.3.8",
36
+ "vite-tsconfig-paths": "4.2.0",
37
+ "vitest": "0.31.1"
38
+ },
39
+ "peerDependencies": {
40
+ "alpinejs": "^3.10.0"
41
+ },
42
+ "prettier": {
43
+ "singleQuote": true,
44
+ "bracketSameLine": true,
45
+ "plugins": [
46
+ "@trivago/prettier-plugin-sort-imports"
47
+ ],
48
+ "importOrder": [
49
+ "node:.*",
50
+ "@/lib(.*)$",
51
+ "@/utils(.*)$",
52
+ "^[./]"
53
+ ],
54
+ "importOrderSeparation": true,
55
+ "importOrderSortSpecifiers": true
56
+ },
57
+ "lint-staged": {
58
+ "*.{js,ts,mjs}": [
59
+ "eslint --fix",
60
+ "prettier --write"
61
+ ],
62
+ "*.json": [
63
+ "prettier --write"
64
+ ]
65
+ },
66
+ "scripts": {
67
+ "build": "tsc",
68
+ "coverage": "vitest run --coverage",
69
+ "lint": "eslint --fix ./src; prettier --write ./src --loglevel error",
70
+ "lint:check": "eslint --max-warnings 10 ./src && prettier --check ./src",
71
+ "prebuild": "rm -rf dist",
72
+ "size": "node scripts/esbuild.js",
73
+ "test": "vitest"
74
+ }
75
+ }
@@ -0,0 +1,72 @@
1
+ import type { Alpine } from 'alpinejs';
2
+ import type {
3
+ DirectiveData,
4
+ ElementWithXAttributes,
5
+ Utilities,
6
+ } from 'alpinejs/dist/types';
7
+
8
+ export const headless = <T extends Record<string, unknown>>(
9
+ name: string,
10
+ rootHandler: RootHandler<T>,
11
+ handlers?: HeadlessHandlers<T>
12
+ ): HeadlessComponent => {
13
+ const headlessMap = new WeakMap<ElementWithXAttributes, T>();
14
+ return (Alpine: Alpine) => {
15
+ const getHeadlessComponentState = (
16
+ el: ElementWithXAttributes
17
+ ): T | void => {
18
+ const root = Alpine.findClosest(el, headlessMap.has.bind(headlessMap));
19
+ if (!root)
20
+ return console.error(`Could not find root for ${name} component`, el);
21
+ return headlessMap.get(root);
22
+ };
23
+
24
+ Alpine.magic(name, (el) => getHeadlessComponentState(el));
25
+
26
+ Alpine.directive(name, (el, directive, utils) => {
27
+ if (!directive.value)
28
+ return headlessMap.set(el, rootHandler(el, directive, utils));
29
+ const headlessState = getHeadlessComponentState(el);
30
+ if (!headlessState) return missingState(name, el);
31
+ (handlers?.[directive.value] || failback(name))(
32
+ headlessState,
33
+ el,
34
+ directive,
35
+ utils
36
+ );
37
+ });
38
+ };
39
+ };
40
+
41
+ const failback =
42
+ (name: string) =>
43
+ (_: unknown, el: ElementWithXAttributes, directive: DirectiveData) => {
44
+ console.error(
45
+ `Alpine ${name}: Invalid directive value`,
46
+ el,
47
+ directive.original
48
+ );
49
+ };
50
+
51
+ const missingState = (name: string, el: ElementWithXAttributes) =>
52
+ console.error(`Could not find root for ${name} component`, el);
53
+
54
+ type HeadlessHandlers<T extends Record<string, unknown>> = Record<
55
+ string,
56
+ HeadlessHandler<T>
57
+ >;
58
+
59
+ export type RootHandler<T> = (
60
+ el: ElementWithXAttributes,
61
+ directive: DirectiveData,
62
+ utilities: Utilities
63
+ ) => T;
64
+
65
+ export type HeadlessHandler<T> = (
66
+ state: T,
67
+ el: ElementWithXAttributes,
68
+ directive: DirectiveData,
69
+ utilities: Utilities
70
+ ) => void;
71
+
72
+ export type HeadlessComponent = (Alpine: Alpine) => void;
@@ -0,0 +1,47 @@
1
+ import { RootHandler, headless } from './headless';
2
+
3
+ type PanelState = {
4
+ isOpen: boolean;
5
+ open: () => void;
6
+ close: () => void;
7
+ toggle: () => void;
8
+ };
9
+
10
+ const handleRoot: RootHandler<PanelState> = (el, _, { Alpine }) => {
11
+ const panelState = Alpine.reactive({
12
+ isOpen: false,
13
+ open() {
14
+ this.isOpen = true;
15
+ },
16
+ close() {
17
+ this.isOpen = false;
18
+ },
19
+ toggle() {
20
+ return this.isOpen ? this.close() : this.open();
21
+ },
22
+ });
23
+
24
+ Alpine.bind(el, {
25
+ 'x-id': () => ['x-panel'],
26
+ });
27
+
28
+ return panelState;
29
+ };
30
+
31
+ export const panel = headless('panel', handleRoot, {
32
+ summary: (panelState, el, _, { Alpine }) => {
33
+ Alpine.bind(el, {
34
+ ':aria-expanded': () => panelState.isOpen,
35
+ ':aria-controls': '$id("x-panel")',
36
+ });
37
+ },
38
+ content: (panelState, el, _, { Alpine }) => {
39
+ Alpine.bind(el, {
40
+ 'x-show': () => panelState.isOpen,
41
+ 'x-transition:enter': 'transition-transform',
42
+ 'x-transition:enter-start': 'translate-x-full',
43
+ 'x-transition:leave': 'transition-transform',
44
+ 'x-transition:leave-end': 'translate-x-full',
45
+ });
46
+ },
47
+ });
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { HeadlessComponent } from './headless/headless';
2
+ import { panel } from './headless/panel';
3
+
4
+ export const UI: HeadlessComponent = (Alpine) => Alpine.plugin([panel]);
5
+
6
+ export default UI;