@scalar/use-hooks 0.1.46 → 0.1.48
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/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +7 -0
- package/dist/useBindCx/cva.js +11 -9
- package/dist/useBindCx/cva.js.map +7 -0
- package/dist/useBindCx/cva.test.js +92 -0
- package/dist/useBindCx/cva.test.js.map +7 -0
- package/dist/useBindCx/index.js +8 -7
- package/dist/useBindCx/index.js.map +7 -0
- package/dist/useBindCx/useBindCx.js +17 -15
- package/dist/useBindCx/useBindCx.js.map +7 -0
- package/dist/useBindCx/useBindCx.test.js +173 -0
- package/dist/useBindCx/useBindCx.test.js.map +7 -0
- package/dist/useBreakpoints/index.js +2 -4
- package/dist/useBreakpoints/index.js.map +7 -0
- package/dist/useBreakpoints/useBreakpoints.js +17 -14
- package/dist/useBreakpoints/useBreakpoints.js.map +7 -0
- package/dist/useBreakpoints/useBreakpoints.test.js +49 -0
- package/dist/useBreakpoints/useBreakpoints.test.js.map +7 -0
- package/dist/useClipboard/index.js +2 -4
- package/dist/useClipboard/index.js.map +7 -0
- package/dist/useClipboard/types.js +1 -0
- package/dist/useClipboard/types.js.map +7 -0
- package/dist/useClipboard/useClipboard.d.ts +1 -1
- package/dist/useClipboard/useClipboard.js +14 -9
- package/dist/useClipboard/useClipboard.js.map +7 -0
- package/dist/useClipboard/useClipboard.test.js +70 -0
- package/dist/useClipboard/useClipboard.test.js.map +7 -0
- package/dist/useColorMode/index.d.ts +1 -1
- package/dist/useColorMode/index.js +3 -4
- package/dist/useColorMode/index.js.map +7 -0
- package/dist/useColorMode/types.js +1 -0
- package/dist/useColorMode/types.js.map +7 -0
- package/dist/useColorMode/useColorMode.d.ts +1 -1
- package/dist/useColorMode/useColorMode.js +67 -41
- package/dist/useColorMode/useColorMode.js.map +7 -0
- package/dist/useColorMode/useColorMode.test.js +171 -0
- package/dist/useColorMode/useColorMode.test.js.map +7 -0
- package/package.json +11 -8
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=index.js.map
|
package/dist/useBindCx/cva.js
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
import { defineConfig
|
|
2
|
-
import { extendTailwindMerge
|
|
3
|
-
const
|
|
1
|
+
import { defineConfig } from "cva";
|
|
2
|
+
import { extendTailwindMerge } from "tailwind-merge";
|
|
3
|
+
const tw = extendTailwindMerge({
|
|
4
4
|
extend: {
|
|
5
5
|
classGroups: {
|
|
6
6
|
"font-size": ["text-3xs", "text-xxs"]
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
|
-
})
|
|
9
|
+
});
|
|
10
|
+
const { cva, cx, compose } = defineConfig({
|
|
10
11
|
hooks: {
|
|
11
|
-
onComplete: (
|
|
12
|
+
onComplete: (className) => tw(className)
|
|
12
13
|
}
|
|
13
14
|
});
|
|
14
15
|
export {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
compose,
|
|
17
|
+
cva,
|
|
18
|
+
cx,
|
|
19
|
+
tw
|
|
19
20
|
};
|
|
21
|
+
//# sourceMappingURL=cva.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/useBindCx/cva.ts"],
|
|
4
|
+
"sourcesContent": ["import { defineConfig } from 'cva'\nimport { extendTailwindMerge } from 'tailwind-merge'\n\n/**\n * Tailwind Merge Config\n *\n * By default tailwind merge only knows about the default tailwind classes\n * this is because it does not load in the tailwind config at runtime (perf reasons)\n * we must specify any custom classes if they are getting overwritten\n *\n * https://github.com/dcastil/tailwind-merge/blob/v2.0.0/docs/configuration.md#class-groups\n */\nconst tw = extendTailwindMerge({\n extend: {\n classGroups: {\n 'font-size': ['text-3xs', 'text-xxs'],\n },\n },\n})\n\n/**\n * CVA Config\n *\n * https://beta.cva.style/api-reference/#defineconfig\n */\nconst { cva, cx, compose } = defineConfig({\n hooks: {\n onComplete: (className) => tw(className),\n },\n})\n\nexport { cva, cx, compose, tw }\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,2BAA2B;AAWpC,MAAM,KAAK,oBAAoB;AAAA,EAC7B,QAAQ;AAAA,IACN,aAAa;AAAA,MACX,aAAa,CAAC,YAAY,UAAU;AAAA,IACtC;AAAA,EACF;AACF,CAAC;AAOD,MAAM,EAAE,KAAK,IAAI,QAAQ,IAAI,aAAa;AAAA,EACxC,OAAO;AAAA,IACL,YAAY,CAAC,cAAc,GAAG,SAAS;AAAA,EACzC;AACF,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { cva, cx } from "./cva.js";
|
|
3
|
+
describe("cx utility", () => {
|
|
4
|
+
it("should merge classnames correctly", () => {
|
|
5
|
+
expect(cx("foo", "bar")).toBe("foo bar");
|
|
6
|
+
expect(cx("foo", void 0, "bar")).toBe("foo bar");
|
|
7
|
+
expect(cx("foo", null, "bar")).toBe("foo bar");
|
|
8
|
+
expect(cx("foo", false, "bar")).toBe("foo bar");
|
|
9
|
+
expect(cx("foo", "bar")).toBe("foo bar");
|
|
10
|
+
expect(cx("foo", false)).toBe("foo");
|
|
11
|
+
});
|
|
12
|
+
it("should de-dupe classses", () => {
|
|
13
|
+
expect(cx("mt-1", "mt-2")).toBe("mt-2");
|
|
14
|
+
expect(cx("bg-b-1 bg-b-1 bg-b-2 bg-b-1")).toBe("bg-b-1");
|
|
15
|
+
expect(cx("text-xxs text-3xs")).toBe("text-3xs");
|
|
16
|
+
});
|
|
17
|
+
it("should handle conditional classes", () => {
|
|
18
|
+
const isActive = true;
|
|
19
|
+
const isDisabled = false;
|
|
20
|
+
expect(cx("base", isActive && "active", isDisabled && "disabled")).toBe("base active");
|
|
21
|
+
});
|
|
22
|
+
it("should handle tailwind conflicts", () => {
|
|
23
|
+
expect(cx("p-4 px-6", "px-2")).toBe("p-4 px-2");
|
|
24
|
+
expect(cx("text-blue text-xl", "text-red")).toBe("text-xl text-red");
|
|
25
|
+
});
|
|
26
|
+
it("should handle arrays of classes", () => {
|
|
27
|
+
expect(cx(["foo", "bar"], "baz")).toBe("foo bar baz");
|
|
28
|
+
expect(cx(["foo", null, void 0], ["bar", false])).toBe("foo bar");
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
describe("cva utility", () => {
|
|
32
|
+
it("should create variant classes correctly", () => {
|
|
33
|
+
const button = cva({
|
|
34
|
+
base: "px-4 py-2 rounded",
|
|
35
|
+
variants: {
|
|
36
|
+
intent: {
|
|
37
|
+
primary: "bg-blue text-white",
|
|
38
|
+
secondary: "bg-gray text-black"
|
|
39
|
+
},
|
|
40
|
+
size: {
|
|
41
|
+
sm: "text-sm",
|
|
42
|
+
lg: "text-lg"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
defaultVariants: {
|
|
46
|
+
intent: "primary",
|
|
47
|
+
size: "sm"
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
expect(button()).toBe("px-4 py-2 rounded bg-blue text-white text-sm");
|
|
51
|
+
expect(button({ intent: "secondary" })).toBe("px-4 py-2 rounded bg-gray text-black text-sm");
|
|
52
|
+
expect(button({ size: "lg" })).toBe("px-4 py-2 rounded bg-blue text-white text-lg");
|
|
53
|
+
});
|
|
54
|
+
it("should handle compound variants", () => {
|
|
55
|
+
const button = cva({
|
|
56
|
+
base: "base-style",
|
|
57
|
+
variants: {
|
|
58
|
+
intent: {
|
|
59
|
+
primary: "primary-style",
|
|
60
|
+
secondary: "secondary-style"
|
|
61
|
+
},
|
|
62
|
+
size: {
|
|
63
|
+
small: "small-style",
|
|
64
|
+
large: "large-style"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
compoundVariants: [
|
|
68
|
+
{
|
|
69
|
+
intent: "primary",
|
|
70
|
+
size: "small",
|
|
71
|
+
class: "primary-small-style"
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
});
|
|
75
|
+
expect(button({ intent: "primary", size: "small" })).toBe(
|
|
76
|
+
"base-style primary-style small-style primary-small-style"
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
it("should handle undefined variant values", () => {
|
|
80
|
+
const button = cva({
|
|
81
|
+
base: "base-style",
|
|
82
|
+
variants: {
|
|
83
|
+
intent: {
|
|
84
|
+
primary: "primary-style",
|
|
85
|
+
secondary: "secondary-style"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
expect(button({ intent: void 0 })).toBe("base-style");
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=cva.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/useBindCx/cva.test.ts"],
|
|
4
|
+
"sourcesContent": ["import { describe, expect, it } from 'vitest'\n\nimport { cva, cx } from './cva'\n\ndescribe('cx utility', () => {\n it('should merge classnames correctly', () => {\n expect(cx('foo', 'bar')).toBe('foo bar')\n expect(cx('foo', undefined, 'bar')).toBe('foo bar')\n expect(cx('foo', null, 'bar')).toBe('foo bar')\n expect(cx('foo', false, 'bar')).toBe('foo bar')\n expect(cx('foo', true && 'bar')).toBe('foo bar')\n expect(cx('foo', false && 'bar')).toBe('foo')\n })\n\n it('should de-dupe classses', () => {\n expect(cx('mt-1', 'mt-2')).toBe('mt-2')\n expect(cx('bg-b-1 bg-b-1 bg-b-2 bg-b-1')).toBe('bg-b-1')\n expect(cx('text-xxs text-3xs')).toBe('text-3xs')\n })\n\n it('should handle conditional classes', () => {\n const isActive = true\n const isDisabled = false\n expect(cx('base', isActive && 'active', isDisabled && 'disabled')).toBe('base active')\n })\n\n it('should handle tailwind conflicts', () => {\n expect(cx('p-4 px-6', 'px-2')).toBe('p-4 px-2')\n expect(cx('text-blue text-xl', 'text-red')).toBe('text-xl text-red')\n })\n\n it('should handle arrays of classes', () => {\n expect(cx(['foo', 'bar'], 'baz')).toBe('foo bar baz')\n expect(cx(['foo', null, undefined], ['bar', false])).toBe('foo bar')\n })\n})\n\ndescribe('cva utility', () => {\n it('should create variant classes correctly', () => {\n const button = cva({\n base: 'px-4 py-2 rounded',\n variants: {\n intent: {\n primary: 'bg-blue text-white',\n secondary: 'bg-gray text-black',\n },\n size: {\n sm: 'text-sm',\n lg: 'text-lg',\n },\n },\n defaultVariants: {\n intent: 'primary',\n size: 'sm',\n },\n })\n\n expect(button()).toBe('px-4 py-2 rounded bg-blue text-white text-sm')\n expect(button({ intent: 'secondary' })).toBe('px-4 py-2 rounded bg-gray text-black text-sm')\n expect(button({ size: 'lg' })).toBe('px-4 py-2 rounded bg-blue text-white text-lg')\n })\n\n it('should handle compound variants', () => {\n const button = cva({\n base: 'base-style',\n variants: {\n intent: {\n primary: 'primary-style',\n secondary: 'secondary-style',\n },\n size: {\n small: 'small-style',\n large: 'large-style',\n },\n },\n compoundVariants: [\n {\n intent: 'primary',\n size: 'small',\n class: 'primary-small-style',\n },\n ],\n })\n\n expect(button({ intent: 'primary', size: 'small' })).toBe(\n 'base-style primary-style small-style primary-small-style',\n )\n })\n\n it('should handle undefined variant values', () => {\n const button = cva({\n base: 'base-style',\n variants: {\n intent: {\n primary: 'primary-style',\n secondary: 'secondary-style',\n },\n },\n })\n\n expect(button({ intent: undefined })).toBe('base-style')\n })\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/useBindCx/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { useBindCx
|
|
2
|
-
import {
|
|
1
|
+
import { useBindCx } from "./useBindCx.js";
|
|
2
|
+
import { cva, cx, compose, tw } from "./cva.js";
|
|
3
3
|
export {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
compose,
|
|
5
|
+
cva,
|
|
6
|
+
cx,
|
|
7
|
+
tw,
|
|
8
|
+
useBindCx
|
|
9
9
|
};
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { cx
|
|
3
|
-
function
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { computed, useAttrs } from "vue";
|
|
2
|
+
import { cx } from "./cva.js";
|
|
3
|
+
function useBindCx() {
|
|
4
|
+
const attrs = useAttrs();
|
|
5
|
+
const destructured = computed(() => {
|
|
6
|
+
const { class: className, ...rest } = attrs;
|
|
7
|
+
return { class: className || "", rest };
|
|
7
8
|
});
|
|
8
|
-
function
|
|
9
|
+
function bindCx(...args) {
|
|
9
10
|
return {
|
|
10
|
-
class:
|
|
11
|
-
...
|
|
11
|
+
class: cx(...args, destructured.value.class),
|
|
12
|
+
...destructured.value.rest
|
|
12
13
|
};
|
|
13
14
|
}
|
|
14
|
-
function
|
|
15
|
-
return { class:
|
|
15
|
+
function bindClass(...args) {
|
|
16
|
+
return { class: cx(...args, destructured.value.class) };
|
|
16
17
|
}
|
|
17
18
|
return {
|
|
18
19
|
/**
|
|
@@ -29,17 +30,18 @@ function d() {
|
|
|
29
30
|
* <div v-bind="cx(...)">...</div>
|
|
30
31
|
* </template>
|
|
31
32
|
*/
|
|
32
|
-
cx:
|
|
33
|
+
cx: bindCx,
|
|
33
34
|
/**
|
|
34
35
|
* Provides a wrapper around the `cx` function that merges the
|
|
35
36
|
* component's class attribute with the provided classes and but **does
|
|
36
37
|
* not** bind the remaining attributes
|
|
37
38
|
*/
|
|
38
|
-
classCx:
|
|
39
|
+
classCx: bindClass,
|
|
39
40
|
/** The remaining attributes that are not class attributes */
|
|
40
|
-
otherAttrs:
|
|
41
|
+
otherAttrs: computed(() => destructured.value.rest)
|
|
41
42
|
};
|
|
42
43
|
}
|
|
43
44
|
export {
|
|
44
|
-
|
|
45
|
+
useBindCx
|
|
45
46
|
};
|
|
47
|
+
//# sourceMappingURL=useBindCx.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/useBindCx/useBindCx.ts"],
|
|
4
|
+
"sourcesContent": ["import type { CXOptions } from 'cva'\nimport { computed, useAttrs } from 'vue'\n\nimport { cx } from './cva'\n\n/**\n * Provides a wrapper around the `cx` function that merges the\n * component's class attribute with the provided classes and binds the\n * remaining attributes\n *\n * @see https://beta.cva.style/api-reference#cx\n *\n * @example\n * <script setup>\n * import { useBindCx, cva } from '@scalar/components'\n *\n * defineProps<{ active?: boolean }>()\n *\n * // Important: disable inheritance of attributes\n * defineOptions({ inheritAttrs: false })\n *\n * const { cx } = useBindCx()\n *\n * const variants = cva({\n * base: 'border rounded p-2 bg-b-1',\n * variants: { active: { true: 'bg-b-2' } },\n * })\n * </script>\n * <template>\n * <div v-bind=\"cx(variants({ active }))\">MockComponent</div>\n * </template>\n */\nexport function useBindCx() {\n const attrs = useAttrs()\n\n const destructured = computed(() => {\n const { class: className, ...rest } = attrs\n return { class: className || '', rest }\n })\n\n function bindCx(...args: CXOptions): {\n /** The merged class attribute */\n class: string\n /** The remaining attributes */\n [key: string]: any\n } {\n return {\n class: cx(...args, destructured.value.class),\n ...destructured.value.rest,\n }\n }\n\n function bindClass(...args: CXOptions): {\n /** The merged class attribute */\n class: string\n } {\n return { class: cx(...args, destructured.value.class) }\n }\n\n return {\n /**\n * Provides a wrapper around the `cx` function that merges the\n * component's class attribute with the provided classes and binds the\n * remaining attributes\n *\n * @example\n * <script setup>\n * ...\n * const { cx } = useBindCx()\n * </script>\n * <template>\n * <div v-bind=\"cx(...)\">...</div>\n * </template>\n */\n cx: bindCx,\n /**\n * Provides a wrapper around the `cx` function that merges the\n * component's class attribute with the provided classes and but **does\n * not** bind the remaining attributes\n */\n classCx: bindClass,\n /** The remaining attributes that are not class attributes */\n otherAttrs: computed(() => destructured.value.rest),\n }\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,UAAU,gBAAgB;AAEnC,SAAS,UAAU;AA6BZ,SAAS,YAAY;AAC1B,QAAM,QAAQ,SAAS;AAEvB,QAAM,eAAe,SAAS,MAAM;AAClC,UAAM,EAAE,OAAO,WAAW,GAAG,KAAK,IAAI;AACtC,WAAO,EAAE,OAAO,aAAa,IAAI,KAAK;AAAA,EACxC,CAAC;AAED,WAAS,UAAU,MAKjB;AACA,WAAO;AAAA,MACL,OAAO,GAAG,GAAG,MAAM,aAAa,MAAM,KAAK;AAAA,MAC3C,GAAG,aAAa,MAAM;AAAA,IACxB;AAAA,EACF;AAEA,WAAS,aAAa,MAGpB;AACA,WAAO,EAAE,OAAO,GAAG,GAAG,MAAM,aAAa,MAAM,KAAK,EAAE;AAAA,EACxD;AAEA,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAeL,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMJ,SAAS;AAAA;AAAA,IAET,YAAY,SAAS,MAAM,aAAa,MAAM,IAAI;AAAA,EACpD;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { mount } from "@vue/test-utils";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { defineComponent } from "vue";
|
|
4
|
+
import { cva } from "./cva.js";
|
|
5
|
+
import { useBindCx } from "./useBindCx.js";
|
|
6
|
+
describe("useBindCx", () => {
|
|
7
|
+
const variants = cva({
|
|
8
|
+
base: "bg-base",
|
|
9
|
+
variants: {
|
|
10
|
+
active: { true: "bg-active" }
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
const TestComponent = defineComponent({
|
|
14
|
+
props: {
|
|
15
|
+
active: Boolean
|
|
16
|
+
},
|
|
17
|
+
inheritAttrs: false,
|
|
18
|
+
setup() {
|
|
19
|
+
const { cx } = useBindCx();
|
|
20
|
+
return { cx, variants };
|
|
21
|
+
},
|
|
22
|
+
template: '<div v-bind="cx(variants({ active }))">Test</div>'
|
|
23
|
+
});
|
|
24
|
+
it("should merge base classes correctly", () => {
|
|
25
|
+
const wrapper = mount(TestComponent);
|
|
26
|
+
expect(wrapper.attributes("class")).toBe("bg-base");
|
|
27
|
+
});
|
|
28
|
+
it("should apply variant tailwind classes", () => {
|
|
29
|
+
const wrapper = mount(TestComponent, {
|
|
30
|
+
props: { active: true }
|
|
31
|
+
});
|
|
32
|
+
expect(wrapper.attributes("class")).toBe("bg-active");
|
|
33
|
+
});
|
|
34
|
+
it("should merge external classes with internal classes", () => {
|
|
35
|
+
const wrapper = mount(TestComponent, {
|
|
36
|
+
attrs: { class: "external-class" }
|
|
37
|
+
});
|
|
38
|
+
expect(wrapper.attributes("class")).toBe("bg-base external-class");
|
|
39
|
+
});
|
|
40
|
+
it("should be reactive to prop changes", async () => {
|
|
41
|
+
const wrapper = mount(TestComponent, {
|
|
42
|
+
props: { active: false },
|
|
43
|
+
attrs: { class: "external-class" }
|
|
44
|
+
});
|
|
45
|
+
expect(wrapper.attributes("class")).toBe("bg-base external-class");
|
|
46
|
+
await wrapper.setProps({ active: true });
|
|
47
|
+
expect(wrapper.attributes("class")).toBe("bg-active external-class");
|
|
48
|
+
});
|
|
49
|
+
it("should be reactive to attribute changes", async () => {
|
|
50
|
+
const WrapperComponent = defineComponent({
|
|
51
|
+
props: { c: { type: String, default: "" } },
|
|
52
|
+
components: { TestComponent },
|
|
53
|
+
template: '<TestComponent :class="c" />'
|
|
54
|
+
});
|
|
55
|
+
const wrapper = mount(WrapperComponent, {
|
|
56
|
+
props: { c: "external-class" }
|
|
57
|
+
});
|
|
58
|
+
expect(wrapper.attributes("class")).toBe("bg-base external-class");
|
|
59
|
+
await wrapper.setProps({ c: "updated-class" });
|
|
60
|
+
expect(wrapper.attributes("class")).toBe("bg-base updated-class");
|
|
61
|
+
});
|
|
62
|
+
it("should handle multiple class combinations", () => {
|
|
63
|
+
const wrapper = mount(TestComponent, {
|
|
64
|
+
props: { active: true },
|
|
65
|
+
attrs: { class: "external-class another-class" }
|
|
66
|
+
});
|
|
67
|
+
expect(wrapper.attributes("class")).toBe("bg-active external-class another-class");
|
|
68
|
+
});
|
|
69
|
+
it("should apply external tailwind classes", () => {
|
|
70
|
+
const wrapper = mount(TestComponent, {
|
|
71
|
+
attrs: { class: "bg-external" }
|
|
72
|
+
});
|
|
73
|
+
expect(wrapper.attributes("class")).toBe("bg-external");
|
|
74
|
+
});
|
|
75
|
+
it("should apply classes in arrays", () => {
|
|
76
|
+
const wrapper = mount(TestComponent, {
|
|
77
|
+
attrs: { class: ["external-class", "another-class"] }
|
|
78
|
+
});
|
|
79
|
+
expect(wrapper.attributes("class")).toBe("bg-base external-class another-class");
|
|
80
|
+
});
|
|
81
|
+
it("should apply classes in objects", () => {
|
|
82
|
+
const wrapper = mount(TestComponent, {
|
|
83
|
+
attrs: { class: { "truthy-class": true, "falsy-class": false } }
|
|
84
|
+
});
|
|
85
|
+
expect(wrapper.attributes("class")).toBe("bg-base truthy-class");
|
|
86
|
+
});
|
|
87
|
+
it("should pass through other attributes", () => {
|
|
88
|
+
const wrapper = mount(TestComponent, {
|
|
89
|
+
attrs: { "data-testid": "test", "aria-label": "test label" }
|
|
90
|
+
});
|
|
91
|
+
expect(wrapper.attributes("class")).toBe("bg-base");
|
|
92
|
+
expect(wrapper.attributes("data-testid")).toBe("test");
|
|
93
|
+
expect(wrapper.attributes("aria-label")).toBe("test label");
|
|
94
|
+
});
|
|
95
|
+
it("should merge classes with classCx without other attributes", () => {
|
|
96
|
+
const ClassCxComponent = defineComponent({
|
|
97
|
+
props: { active: Boolean },
|
|
98
|
+
inheritAttrs: false,
|
|
99
|
+
setup() {
|
|
100
|
+
const { classCx } = useBindCx();
|
|
101
|
+
return { classCx };
|
|
102
|
+
},
|
|
103
|
+
template: `<div v-bind="classCx('internal-class')">Test</div>`
|
|
104
|
+
});
|
|
105
|
+
const wrapper = mount(ClassCxComponent, {
|
|
106
|
+
props: { active: true },
|
|
107
|
+
attrs: { "data-testid": "test", class: "external-class" }
|
|
108
|
+
});
|
|
109
|
+
expect(wrapper.attributes("class")).toBe("internal-class external-class");
|
|
110
|
+
expect(wrapper.attributes("data-testid")).toBeUndefined();
|
|
111
|
+
});
|
|
112
|
+
it("should provide other attributes via otherAttrs", () => {
|
|
113
|
+
const OtherAttrsComponent = defineComponent({
|
|
114
|
+
inheritAttrs: false,
|
|
115
|
+
setup() {
|
|
116
|
+
const { otherAttrs } = useBindCx();
|
|
117
|
+
return { otherAttrs };
|
|
118
|
+
},
|
|
119
|
+
template: '<div v-bind="otherAttrs">Test</div>'
|
|
120
|
+
});
|
|
121
|
+
const wrapper = mount(OtherAttrsComponent, {
|
|
122
|
+
attrs: {
|
|
123
|
+
class: "should-not-appear",
|
|
124
|
+
"data-testid": "test",
|
|
125
|
+
"aria-label": "test label"
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
expect(wrapper.attributes("class")).toBeUndefined();
|
|
129
|
+
expect(wrapper.attributes("data-testid")).toBe("test");
|
|
130
|
+
expect(wrapper.attributes("aria-label")).toBe("test label");
|
|
131
|
+
});
|
|
132
|
+
it("should handle reactive attribute changes with classCx and otherAttrs", async () => {
|
|
133
|
+
const WrapperComponent = defineComponent({
|
|
134
|
+
props: {
|
|
135
|
+
className: { type: String, default: "" },
|
|
136
|
+
testId: { type: String, default: "" }
|
|
137
|
+
},
|
|
138
|
+
components: {
|
|
139
|
+
InnerComponent: defineComponent({
|
|
140
|
+
inheritAttrs: false,
|
|
141
|
+
setup() {
|
|
142
|
+
const { classCx, otherAttrs } = useBindCx();
|
|
143
|
+
return { classCx, otherAttrs, variants };
|
|
144
|
+
},
|
|
145
|
+
template: `
|
|
146
|
+
<div>
|
|
147
|
+
<div data-test="class" v-bind="classCx(variants({}))">Class</div>
|
|
148
|
+
<div data-test="attrs" v-bind="otherAttrs">Attrs</div>
|
|
149
|
+
</div>
|
|
150
|
+
`
|
|
151
|
+
})
|
|
152
|
+
},
|
|
153
|
+
template: '<InnerComponent :class="className" :data-testid="testId" />'
|
|
154
|
+
});
|
|
155
|
+
const wrapper = mount(WrapperComponent, {
|
|
156
|
+
props: {
|
|
157
|
+
className: "initial-class",
|
|
158
|
+
testId: "initial-id"
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
const classDiv = wrapper.find('[data-test="class"]');
|
|
162
|
+
const attrsDiv = wrapper.find('[data-test="attrs"]');
|
|
163
|
+
expect(classDiv.attributes("class")).toBe("bg-base initial-class");
|
|
164
|
+
expect(attrsDiv.attributes("data-testid")).toBe("initial-id");
|
|
165
|
+
await wrapper.setProps({
|
|
166
|
+
className: "updated-class",
|
|
167
|
+
testId: "updated-id"
|
|
168
|
+
});
|
|
169
|
+
expect(classDiv.attributes("class")).toBe("bg-base updated-class");
|
|
170
|
+
expect(attrsDiv.attributes("data-testid")).toBe("updated-id");
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
//# sourceMappingURL=useBindCx.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/useBindCx/useBindCx.test.ts"],
|
|
4
|
+
"sourcesContent": ["import { mount } from '@vue/test-utils'\nimport { describe, expect, it } from 'vitest'\nimport { defineComponent } from 'vue'\n\nimport { cva } from './cva'\nimport { useBindCx } from './useBindCx'\n\ndescribe('useBindCx', () => {\n const variants = cva({\n base: 'bg-base',\n variants: {\n active: { true: 'bg-active' },\n },\n })\n\n const TestComponent = defineComponent({\n props: {\n active: Boolean,\n },\n inheritAttrs: false,\n setup() {\n const { cx } = useBindCx()\n return { cx, variants }\n },\n template: '<div v-bind=\"cx(variants({ active }))\">Test</div>',\n })\n\n it('should merge base classes correctly', () => {\n const wrapper = mount(TestComponent)\n expect(wrapper.attributes('class')).toBe('bg-base')\n })\n\n it('should apply variant tailwind classes', () => {\n const wrapper = mount(TestComponent, {\n props: { active: true },\n })\n expect(wrapper.attributes('class')).toBe('bg-active')\n })\n\n it('should merge external classes with internal classes', () => {\n const wrapper = mount(TestComponent, {\n attrs: { class: 'external-class' },\n })\n expect(wrapper.attributes('class')).toBe('bg-base external-class')\n })\n\n it('should be reactive to prop changes', async () => {\n const wrapper = mount(TestComponent, {\n props: { active: false },\n attrs: { class: 'external-class' },\n })\n expect(wrapper.attributes('class')).toBe('bg-base external-class')\n\n await wrapper.setProps({ active: true })\n expect(wrapper.attributes('class')).toBe('bg-active external-class')\n })\n\n it('should be reactive to attribute changes', async () => {\n const WrapperComponent = defineComponent({\n props: { c: { type: String, default: '' } },\n components: { TestComponent },\n template: '<TestComponent :class=\"c\" />',\n })\n\n const wrapper = mount(WrapperComponent, {\n props: { c: 'external-class' },\n })\n expect(wrapper.attributes('class')).toBe('bg-base external-class')\n\n await wrapper.setProps({ c: 'updated-class' })\n expect(wrapper.attributes('class')).toBe('bg-base updated-class')\n })\n\n it('should handle multiple class combinations', () => {\n const wrapper = mount(TestComponent, {\n props: { active: true },\n attrs: { class: 'external-class another-class' },\n })\n expect(wrapper.attributes('class')).toBe('bg-active external-class another-class')\n })\n\n it('should apply external tailwind classes', () => {\n const wrapper = mount(TestComponent, {\n attrs: { class: 'bg-external' },\n })\n expect(wrapper.attributes('class')).toBe('bg-external')\n })\n\n it('should apply classes in arrays', () => {\n const wrapper = mount(TestComponent, {\n attrs: { class: ['external-class', 'another-class'] },\n })\n expect(wrapper.attributes('class')).toBe('bg-base external-class another-class')\n })\n\n it('should apply classes in objects', () => {\n const wrapper = mount(TestComponent, {\n attrs: { class: { 'truthy-class': true, 'falsy-class': false } },\n })\n expect(wrapper.attributes('class')).toBe('bg-base truthy-class')\n })\n\n it('should pass through other attributes', () => {\n const wrapper = mount(TestComponent, {\n attrs: { 'data-testid': 'test', 'aria-label': 'test label' },\n })\n expect(wrapper.attributes('class')).toBe('bg-base')\n expect(wrapper.attributes('data-testid')).toBe('test')\n expect(wrapper.attributes('aria-label')).toBe('test label')\n })\n\n it('should merge classes with classCx without other attributes', () => {\n const ClassCxComponent = defineComponent({\n props: { active: Boolean },\n inheritAttrs: false,\n setup() {\n const { classCx } = useBindCx()\n return { classCx }\n },\n template: '<div v-bind=\"classCx(\\'internal-class\\')\">Test</div>',\n })\n\n const wrapper = mount(ClassCxComponent, {\n props: { active: true },\n attrs: { 'data-testid': 'test', class: 'external-class' },\n })\n\n expect(wrapper.attributes('class')).toBe('internal-class external-class')\n expect(wrapper.attributes('data-testid')).toBeUndefined()\n })\n\n it('should provide other attributes via otherAttrs', () => {\n const OtherAttrsComponent = defineComponent({\n inheritAttrs: false,\n setup() {\n const { otherAttrs } = useBindCx()\n return { otherAttrs }\n },\n template: '<div v-bind=\"otherAttrs\">Test</div>',\n })\n\n const wrapper = mount(OtherAttrsComponent, {\n attrs: {\n class: 'should-not-appear',\n 'data-testid': 'test',\n 'aria-label': 'test label',\n },\n })\n\n expect(wrapper.attributes('class')).toBeUndefined()\n expect(wrapper.attributes('data-testid')).toBe('test')\n expect(wrapper.attributes('aria-label')).toBe('test label')\n })\n\n it('should handle reactive attribute changes with classCx and otherAttrs', async () => {\n const WrapperComponent = defineComponent({\n props: {\n className: { type: String, default: '' },\n testId: { type: String, default: '' },\n },\n components: {\n InnerComponent: defineComponent({\n inheritAttrs: false,\n setup() {\n const { classCx, otherAttrs } = useBindCx()\n return { classCx, otherAttrs, variants }\n },\n template: `\n <div>\n <div data-test=\"class\" v-bind=\"classCx(variants({}))\">Class</div>\n <div data-test=\"attrs\" v-bind=\"otherAttrs\">Attrs</div>\n </div>\n `,\n }),\n },\n template: '<InnerComponent :class=\"className\" :data-testid=\"testId\" />',\n })\n\n const wrapper = mount(WrapperComponent, {\n props: {\n className: 'initial-class',\n testId: 'initial-id',\n },\n })\n\n const classDiv = wrapper.find('[data-test=\"class\"]')\n const attrsDiv = wrapper.find('[data-test=\"attrs\"]')\n\n expect(classDiv.attributes('class')).toBe('bg-base initial-class')\n expect(attrsDiv.attributes('data-testid')).toBe('initial-id')\n\n await wrapper.setProps({\n className: 'updated-class',\n testId: 'updated-id',\n })\n\n expect(classDiv.attributes('class')).toBe('bg-base updated-class')\n expect(attrsDiv.attributes('data-testid')).toBe('updated-id')\n })\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,26 +1,29 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { useMediaQuery
|
|
3
|
-
import { computed
|
|
4
|
-
function
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import preset from "@scalar/themes/tailwind";
|
|
2
|
+
import { useMediaQuery } from "@vueuse/core";
|
|
3
|
+
import { computed, unref } from "vue";
|
|
4
|
+
function useBreakpoints() {
|
|
5
|
+
const screens = preset.theme.screens;
|
|
6
|
+
const mediaQueries = Object.fromEntries(
|
|
7
|
+
Object.entries(screens).map(([breakpoint, value]) => [
|
|
8
|
+
breakpoint,
|
|
9
|
+
useMediaQuery(typeof value === "string" ? `(min-width: ${value})` : value.raw)
|
|
9
10
|
])
|
|
10
|
-
)
|
|
11
|
+
);
|
|
12
|
+
const breakpoints = computed(
|
|
11
13
|
() => Object.fromEntries(
|
|
12
|
-
Object.entries(
|
|
14
|
+
Object.entries(mediaQueries).map(([breakpoint, queryRef]) => [breakpoint, unref(queryRef)])
|
|
13
15
|
)
|
|
14
16
|
);
|
|
15
17
|
return {
|
|
16
18
|
/** The screen sizes defined in the preset */
|
|
17
|
-
screens
|
|
19
|
+
screens,
|
|
18
20
|
/** Reactive media queries for each of the screen sizes */
|
|
19
|
-
mediaQueries
|
|
21
|
+
mediaQueries,
|
|
20
22
|
/** The breakpoints as reactive media queries */
|
|
21
|
-
breakpoints
|
|
23
|
+
breakpoints
|
|
22
24
|
};
|
|
23
25
|
}
|
|
24
26
|
export {
|
|
25
|
-
|
|
27
|
+
useBreakpoints
|
|
26
28
|
};
|
|
29
|
+
//# sourceMappingURL=useBreakpoints.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/useBreakpoints/useBreakpoints.ts"],
|
|
4
|
+
"sourcesContent": ["import preset from '@scalar/themes/tailwind'\nimport { useMediaQuery } from '@vueuse/core'\nimport { type Ref, computed, unref } from 'vue'\n\ntype Screen = keyof typeof preset.theme.screens\n\n/**\n * Exposes Tailwind CSS breakpoints as reactive media queries\n *\n * **Warning:** This hook is not a replacement for Tailwind CSS breakpoints. Using breakpoints in Javascript can cause issues with Server Side Rendering (SSR) and the Tailwind CSS breakpoints should be used when possible.\n */\nexport function useBreakpoints() {\n const screens = preset.theme.screens\n\n const mediaQueries = Object.fromEntries(\n Object.entries(screens).map(([breakpoint, value]) => [\n breakpoint,\n useMediaQuery(typeof value === 'string' ? `(min-width: ${value})` : value.raw),\n ]),\n ) as Record<Screen, Ref<boolean>>\n\n // We make the breakpoints a computed object so that we can use them in templates as `breakpoints.x` instead of `breakpoints.x.value`\n const breakpoints = computed(\n () =>\n Object.fromEntries(\n Object.entries(mediaQueries).map(([breakpoint, queryRef]) => [breakpoint, unref(queryRef)]),\n ) as Record<Screen, boolean>,\n )\n\n return {\n /** The screen sizes defined in the preset */\n screens,\n /** Reactive media queries for each of the screen sizes */\n mediaQueries,\n /** The breakpoints as reactive media queries */\n breakpoints,\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,YAAY;AACnB,SAAS,qBAAqB;AAC9B,SAAmB,UAAU,aAAa;AASnC,SAAS,iBAAiB;AAC/B,QAAM,UAAU,OAAO,MAAM;AAE7B,QAAM,eAAe,OAAO;AAAA,IAC1B,OAAO,QAAQ,OAAO,EAAE,IAAI,CAAC,CAAC,YAAY,KAAK,MAAM;AAAA,MACnD;AAAA,MACA,cAAc,OAAO,UAAU,WAAW,eAAe,KAAK,MAAM,MAAM,GAAG;AAAA,IAC/E,CAAC;AAAA,EACH;AAGA,QAAM,cAAc;AAAA,IAClB,MACE,OAAO;AAAA,MACL,OAAO,QAAQ,YAAY,EAAE,IAAI,CAAC,CAAC,YAAY,QAAQ,MAAM,CAAC,YAAY,MAAM,QAAQ,CAAC,CAAC;AAAA,IAC5F;AAAA,EACJ;AAEA,SAAO;AAAA;AAAA,IAEL;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import preset from "@scalar/themes/tailwind";
|
|
2
|
+
import { useMediaQuery } from "@vueuse/core";
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { ref } from "vue";
|
|
5
|
+
import { useBreakpoints } from "./useBreakpoints.js";
|
|
6
|
+
const screens = preset.theme.screens;
|
|
7
|
+
vi.mock("@vueuse/core", () => ({
|
|
8
|
+
useMediaQuery: vi.fn()
|
|
9
|
+
}));
|
|
10
|
+
describe("useBreakpoints", () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
vi.resetAllMocks();
|
|
13
|
+
});
|
|
14
|
+
it("should expose the screen sizes", () => {
|
|
15
|
+
const { screens: exposedScreens } = useBreakpoints();
|
|
16
|
+
expect(exposedScreens).toEqual(screens);
|
|
17
|
+
});
|
|
18
|
+
it("should expose media queries for a given screen size", () => {
|
|
19
|
+
vi.mocked(useMediaQuery).mockImplementation((query) => ref(query === `(min-width: ${screens.md})`));
|
|
20
|
+
const { mediaQueries } = useBreakpoints();
|
|
21
|
+
expect(mediaQueries.sm.value).toEqual(false);
|
|
22
|
+
expect(mediaQueries.md.value).toEqual(true);
|
|
23
|
+
});
|
|
24
|
+
it("should update breakpoints when the media query changes", () => {
|
|
25
|
+
const mdQuery = ref(false);
|
|
26
|
+
vi.mocked(useMediaQuery).mockImplementation(
|
|
27
|
+
(query) => query === `(min-width: ${screens.md})` ? mdQuery : ref(false)
|
|
28
|
+
);
|
|
29
|
+
const { breakpoints } = useBreakpoints();
|
|
30
|
+
expect(breakpoints.value.md).toEqual(false);
|
|
31
|
+
mdQuery.value = true;
|
|
32
|
+
expect(breakpoints.value.md).toEqual(true);
|
|
33
|
+
});
|
|
34
|
+
it("works in SSG environment without window", () => {
|
|
35
|
+
const originalWindow = global.window;
|
|
36
|
+
delete global.window;
|
|
37
|
+
vi.mocked(useMediaQuery).mockImplementation(() => ref(false));
|
|
38
|
+
const { screens: exposedScreens, mediaQueries, breakpoints } = useBreakpoints();
|
|
39
|
+
expect(exposedScreens).toEqual(screens);
|
|
40
|
+
Object.values(mediaQueries).forEach((query) => {
|
|
41
|
+
expect(query.value).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
Object.values(breakpoints.value).forEach((value) => {
|
|
44
|
+
expect(value).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
global.window = originalWindow;
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=useBreakpoints.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/useBreakpoints/useBreakpoints.test.ts"],
|
|
4
|
+
"sourcesContent": ["import preset from '@scalar/themes/tailwind'\nimport { useMediaQuery } from '@vueuse/core'\nimport { beforeEach, describe, expect, it, vi } from 'vitest'\nimport { ref } from 'vue'\n\nimport { useBreakpoints } from './useBreakpoints'\n\nconst screens = preset.theme.screens\n\nvi.mock('@vueuse/core', () => ({\n useMediaQuery: vi.fn(),\n}))\n\ndescribe('useBreakpoints', () => {\n beforeEach(() => {\n vi.resetAllMocks()\n })\n\n it('should expose the screen sizes', () => {\n const { screens: exposedScreens } = useBreakpoints()\n expect(exposedScreens).toEqual(screens)\n })\n\n it('should expose media queries for a given screen size', () => {\n vi.mocked(useMediaQuery).mockImplementation((query) => ref(query === `(min-width: ${screens.md})`))\n\n const { mediaQueries } = useBreakpoints()\n expect(mediaQueries.sm.value).toEqual(false)\n expect(mediaQueries.md.value).toEqual(true)\n })\n\n it('should update breakpoints when the media query changes', () => {\n const mdQuery = ref(false)\n vi.mocked(useMediaQuery).mockImplementation((query) =>\n query === `(min-width: ${screens.md})` ? mdQuery : ref(false),\n )\n\n const { breakpoints } = useBreakpoints()\n\n expect(breakpoints.value.md).toEqual(false)\n\n mdQuery.value = true\n\n expect(breakpoints.value.md).toEqual(true)\n })\n\n it('works in SSG environment without window', () => {\n const originalWindow = global.window\n\n // Mock SSG environment by removing window\n // @ts-expect-error\n delete global.window\n\n // Mock useMediaQuery to return false since there\u2019s no window\n vi.mocked(useMediaQuery).mockImplementation(() => ref(false))\n\n const { screens: exposedScreens, mediaQueries, breakpoints } = useBreakpoints()\n\n // Screens should still be exposed since they\u2019re static\n expect(exposedScreens).toEqual(screens)\n\n // Media queries should all be false without window\n Object.values(mediaQueries).forEach((query) => {\n expect(query.value).toBe(false)\n })\n\n // Breakpoints should all be false without window\n Object.values(breakpoints.value).forEach((value) => {\n expect(value).toBe(false)\n })\n\n // Restore window\n global.window = originalWindow\n })\n})\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,YAAY;AACnB,SAAS,qBAAqB;AAC9B,SAAS,YAAY,UAAU,QAAQ,IAAI,UAAU;AACrD,SAAS,WAAW;AAEpB,SAAS,sBAAsB;AAE/B,MAAM,UAAU,OAAO,MAAM;AAE7B,GAAG,KAAK,gBAAgB,OAAO;AAAA,EAC7B,eAAe,GAAG,GAAG;AACvB,EAAE;AAEF,SAAS,kBAAkB,MAAM;AAC/B,aAAW,MAAM;AACf,OAAG,cAAc;AAAA,EACnB,CAAC;AAED,KAAG,kCAAkC,MAAM;AACzC,UAAM,EAAE,SAAS,eAAe,IAAI,eAAe;AACnD,WAAO,cAAc,EAAE,QAAQ,OAAO;AAAA,EACxC,CAAC;AAED,KAAG,uDAAuD,MAAM;AAC9D,OAAG,OAAO,aAAa,EAAE,mBAAmB,CAAC,UAAU,IAAI,UAAU,eAAe,QAAQ,EAAE,GAAG,CAAC;AAElG,UAAM,EAAE,aAAa,IAAI,eAAe;AACxC,WAAO,aAAa,GAAG,KAAK,EAAE,QAAQ,KAAK;AAC3C,WAAO,aAAa,GAAG,KAAK,EAAE,QAAQ,IAAI;AAAA,EAC5C,CAAC;AAED,KAAG,0DAA0D,MAAM;AACjE,UAAM,UAAU,IAAI,KAAK;AACzB,OAAG,OAAO,aAAa,EAAE;AAAA,MAAmB,CAAC,UAC3C,UAAU,eAAe,QAAQ,EAAE,MAAM,UAAU,IAAI,KAAK;AAAA,IAC9D;AAEA,UAAM,EAAE,YAAY,IAAI,eAAe;AAEvC,WAAO,YAAY,MAAM,EAAE,EAAE,QAAQ,KAAK;AAE1C,YAAQ,QAAQ;AAEhB,WAAO,YAAY,MAAM,EAAE,EAAE,QAAQ,IAAI;AAAA,EAC3C,CAAC;AAED,KAAG,2CAA2C,MAAM;AAClD,UAAM,iBAAiB,OAAO;AAI9B,WAAO,OAAO;AAGd,OAAG,OAAO,aAAa,EAAE,mBAAmB,MAAM,IAAI,KAAK,CAAC;AAE5D,UAAM,EAAE,SAAS,gBAAgB,cAAc,YAAY,IAAI,eAAe;AAG9E,WAAO,cAAc,EAAE,QAAQ,OAAO;AAGtC,WAAO,OAAO,YAAY,EAAE,QAAQ,CAAC,UAAU;AAC7C,aAAO,MAAM,KAAK,EAAE,KAAK,KAAK;AAAA,IAChC,CAAC;AAGD,WAAO,OAAO,YAAY,KAAK,EAAE,QAAQ,CAAC,UAAU;AAClD,aAAO,KAAK,EAAE,KAAK,KAAK;AAAA,IAC1B,CAAC;AAGD,WAAO,SAAS;AAAA,EAClB,CAAC;AACH,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.js.map
|