luan-ui 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.
Files changed (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/dist/components/accordion/accordion.d.ts +34 -0
  4. package/dist/components/accordion/accordion.js +15 -0
  5. package/dist/components/accordion/accordion.js.map +1 -0
  6. package/dist/components/button/button.d.ts +60 -0
  7. package/dist/components/button/button.js +80 -0
  8. package/dist/components/button/button.js.map +1 -0
  9. package/dist/components/card/card.d.ts +172 -0
  10. package/dist/components/card/card.js +212 -0
  11. package/dist/components/card/card.js.map +1 -0
  12. package/dist/components/checkbox/checkbox.d.ts +39 -0
  13. package/dist/components/checkbox/checkbox.js +66 -0
  14. package/dist/components/checkbox/checkbox.js.map +1 -0
  15. package/dist/components/dialog/dialog.d.ts +10 -0
  16. package/dist/components/dialog/dialog.js +23 -0
  17. package/dist/components/dialog/dialog.js.map +1 -0
  18. package/dist/components/form-field/form-field-context.d.ts +13 -0
  19. package/dist/components/form-field/form-field-context.js +12 -0
  20. package/dist/components/form-field/form-field-context.js.map +1 -0
  21. package/dist/components/form-field/form-field.d.ts +25 -0
  22. package/dist/components/form-field/form-field.js +46 -0
  23. package/dist/components/form-field/form-field.js.map +1 -0
  24. package/dist/components/form-helper/form-helper.d.ts +21 -0
  25. package/dist/components/form-helper/form-helper.js +42 -0
  26. package/dist/components/form-helper/form-helper.js.map +1 -0
  27. package/dist/components/icon/icon.d.ts +54 -0
  28. package/dist/components/icon/icon.js +67 -0
  29. package/dist/components/icon/icon.js.map +1 -0
  30. package/dist/components/input/input.d.ts +15 -0
  31. package/dist/components/input/input.js +42 -0
  32. package/dist/components/input/input.js.map +1 -0
  33. package/dist/components/label/label.d.ts +16 -0
  34. package/dist/components/label/label.js +23 -0
  35. package/dist/components/label/label.js.map +1 -0
  36. package/dist/components/pagination/pagination.d.ts +86 -0
  37. package/dist/components/pagination/pagination.js +134 -0
  38. package/dist/components/pagination/pagination.js.map +1 -0
  39. package/dist/components/popover/popover.d.ts +64 -0
  40. package/dist/components/popover/popover.js +81 -0
  41. package/dist/components/popover/popover.js.map +1 -0
  42. package/dist/components/progress/progress.d.ts +17 -0
  43. package/dist/components/progress/progress.js +18 -0
  44. package/dist/components/progress/progress.js.map +1 -0
  45. package/dist/components/radio-group/radio-group.d.ts +33 -0
  46. package/dist/components/radio-group/radio-group.js +37 -0
  47. package/dist/components/radio-group/radio-group.js.map +1 -0
  48. package/dist/components/select/select.d.ts +42 -0
  49. package/dist/components/select/select.js +58 -0
  50. package/dist/components/select/select.js.map +1 -0
  51. package/dist/components/skeleton/skeleton.d.ts +5 -0
  52. package/dist/components/skeleton/skeleton.js +10 -0
  53. package/dist/components/skeleton/skeleton.js.map +1 -0
  54. package/dist/components/slider/slider.d.ts +39 -0
  55. package/dist/components/slider/slider.js +36 -0
  56. package/dist/components/slider/slider.js.map +1 -0
  57. package/dist/components/slot/slot.d.ts +13 -0
  58. package/dist/components/slot/slot.js +70 -0
  59. package/dist/components/slot/slot.js.map +1 -0
  60. package/dist/components/switch/switch.d.ts +41 -0
  61. package/dist/components/switch/switch.js +74 -0
  62. package/dist/components/switch/switch.js.map +1 -0
  63. package/dist/components/table/table.d.ts +33 -0
  64. package/dist/components/table/table.js +45 -0
  65. package/dist/components/table/table.js.map +1 -0
  66. package/dist/components/tabs/tabs.d.ts +6 -0
  67. package/dist/components/tabs/tabs.js +13 -0
  68. package/dist/components/tabs/tabs.js.map +1 -0
  69. package/dist/components/toast/toast.d.ts +33 -0
  70. package/dist/components/toast/toast.js +33 -0
  71. package/dist/components/toast/toast.js.map +1 -0
  72. package/dist/components/tooltip/tooltip.d.ts +35 -0
  73. package/dist/components/tooltip/tooltip.js +48 -0
  74. package/dist/components/tooltip/tooltip.js.map +1 -0
  75. package/dist/index.d.ts +34 -0
  76. package/dist/index.js +21 -0
  77. package/dist/index.js.map +1 -0
  78. package/dist/utilities/cn/cn.d.ts +2 -0
  79. package/dist/utilities/cn/cn.js +6 -0
  80. package/dist/utilities/cn/cn.js.map +1 -0
  81. package/dist/utilities/get-variants/get-variants.d.ts +56 -0
  82. package/dist/utilities/get-variants/get-variants.js +79 -0
  83. package/dist/utilities/get-variants/get-variants.js.map +1 -0
  84. package/dist/utilities/merge-refs/merge-refs.d.ts +2 -0
  85. package/dist/utilities/merge-refs/merge-refs.js +13 -0
  86. package/dist/utilities/merge-refs/merge-refs.js.map +1 -0
  87. package/dist/utilities/pagination/get-truncated-elements.d.ts +7 -0
  88. package/dist/utilities/pagination/get-truncated-elements.js +101 -0
  89. package/dist/utilities/pagination/get-truncated-elements.js.map +1 -0
  90. package/dist/utilities/pagination/keyboard-navigation.d.ts +1 -0
  91. package/dist/utilities/pagination/keyboard-navigation.js +70 -0
  92. package/dist/utilities/pagination/keyboard-navigation.js.map +1 -0
  93. package/dist/utilities/responsive/responsive.d.ts +37 -0
  94. package/dist/utilities/responsive/responsive.js +40 -0
  95. package/dist/utilities/responsive/responsive.js.map +1 -0
  96. package/package.json +77 -0
@@ -0,0 +1,56 @@
1
+ import type { ResponsiveValue } from "@utilities/responsive/responsive";
2
+ type VariantValue = Record<string, string>;
3
+ type VariantConfig = Record<string, VariantValue>;
4
+ type StringBoolean = "true" | "false";
5
+ type BooleanVariant = Partial<Record<StringBoolean, string>>;
6
+ type VariantPropValue<T> = T extends BooleanVariant ? ResponsiveValue<boolean> | undefined : T extends Record<string, unknown> ? ResponsiveValue<keyof T> : never;
7
+ type VariantProps<T extends VariantConfig> = {
8
+ [K in keyof T]: VariantPropValue<T[K]>;
9
+ } & {
10
+ className?: string;
11
+ };
12
+ type ResponsiveClassesConfig<T extends VariantConfig> = {
13
+ base: string;
14
+ variants?: T;
15
+ compoundVariants?: Partial<VariantProps<T>>[];
16
+ };
17
+ /**
18
+ * Creates a function that generates classes based on variant configurations and responsive props
19
+ *
20
+ * @template T - Type extending VariantConfig (Record of variant names to their possible values and corresponding classes)
21
+ *
22
+ * @param config - Configuration object for variants
23
+ * @param config.base - Base classes that are always applied
24
+ * @param config.variants - Object containing variant definitions where each key is a variant name
25
+ * and value is either a string of class names, an object mapping variant values to class names,
26
+ * or an object with true/false keys for boolean variants
27
+ * @param config.compoundVariants - Optional array of compound variants that apply additional classes
28
+ * when multiple variants have specific values
29
+ *
30
+ * @returns A function that accepts variant props and returns classes with twMerge
31
+ *
32
+ * @example
33
+ * const getButtonVariants = getVariants({
34
+ * base: "px-4 py-2 rounded",
35
+ * variants: {
36
+ * intent: {
37
+ * primary: "bg-blue-500 text-white",
38
+ * secondary: "bg-gray-200 text-gray-800"
39
+ * },
40
+ * size: {
41
+ * sm: "text-sm",
42
+ * lg: "text-lg"
43
+ * },
44
+ * disabled: {
45
+ * true: "opacity-50 cursor-not-allowed"
46
+ * }
47
+ * }
48
+ * });
49
+ *
50
+ * // Usage:
51
+ * getButtonVariants({ intent: "primary", size: "lg", disabled: true })
52
+ * // Or with responsive values:
53
+ * getButtonVariants({ intent: { initial: "primary", md: "secondary" } })
54
+ */
55
+ export declare const getVariants: <T extends VariantConfig>({ base, variants, compoundVariants, }: ResponsiveClassesConfig<T>) => ({ className, ...props }: VariantProps<T>) => string;
56
+ export {};
@@ -0,0 +1,79 @@
1
+ import { cn } from "@utilities/cn/cn";
2
+ /**
3
+ * Creates a function that generates classes based on variant configurations and responsive props
4
+ *
5
+ * @template T - Type extending VariantConfig (Record of variant names to their possible values and corresponding classes)
6
+ *
7
+ * @param config - Configuration object for variants
8
+ * @param config.base - Base classes that are always applied
9
+ * @param config.variants - Object containing variant definitions where each key is a variant name
10
+ * and value is either a string of class names, an object mapping variant values to class names,
11
+ * or an object with true/false keys for boolean variants
12
+ * @param config.compoundVariants - Optional array of compound variants that apply additional classes
13
+ * when multiple variants have specific values
14
+ *
15
+ * @returns A function that accepts variant props and returns classes with twMerge
16
+ *
17
+ * @example
18
+ * const getButtonVariants = getVariants({
19
+ * base: "px-4 py-2 rounded",
20
+ * variants: {
21
+ * intent: {
22
+ * primary: "bg-blue-500 text-white",
23
+ * secondary: "bg-gray-200 text-gray-800"
24
+ * },
25
+ * size: {
26
+ * sm: "text-sm",
27
+ * lg: "text-lg"
28
+ * },
29
+ * disabled: {
30
+ * true: "opacity-50 cursor-not-allowed"
31
+ * }
32
+ * }
33
+ * });
34
+ *
35
+ * // Usage:
36
+ * getButtonVariants({ intent: "primary", size: "lg", disabled: true })
37
+ * // Or with responsive values:
38
+ * getButtonVariants({ intent: { initial: "primary", md: "secondary" } })
39
+ */
40
+ export const getVariants = ({ base, variants, compoundVariants, }) => ({ className, ...props }) => {
41
+ const responsiveClasses = Object.entries(props)
42
+ .flatMap(([key, propValue]) => {
43
+ const variant = variants?.[key];
44
+ const value = typeof propValue === "boolean" ? String(propValue) : propValue;
45
+ // Handle undefined values
46
+ if (!value)
47
+ return;
48
+ const variantValue = variant?.[value];
49
+ // Handle string values
50
+ if (typeof variantValue === "string") {
51
+ return variantValue;
52
+ }
53
+ // Handle responsive values
54
+ return Object.entries(value)
55
+ .map(([breakpoint, value]) => {
56
+ // If the breakpoint is initial, return the variant value without breakpoint prefix
57
+ if (breakpoint === "initial") {
58
+ return variants?.[key]?.[value];
59
+ }
60
+ // Otherwise, return the variant value with the breakpoint prefix
61
+ return variants?.[key]?.[value]
62
+ ?.split(" ")
63
+ .map((className) => `${breakpoint}:${className}`)
64
+ .join(" ");
65
+ })
66
+ .join(" ");
67
+ })
68
+ .join(" ");
69
+ const compoundClasses = compoundVariants
70
+ ?.map(({ className, ...compound }) => {
71
+ if (Object.entries(compound).every(([key, value]) => props[key] === String(value) || props[key] === value)) {
72
+ return className;
73
+ }
74
+ return undefined;
75
+ })
76
+ .filter(Boolean);
77
+ return cn(base, responsiveClasses, compoundClasses, className);
78
+ };
79
+ //# sourceMappingURL=get-variants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-variants.js","sourceRoot":"","sources":["../../../src/utilities/get-variants/get-variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AA8BtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,CAAC,MAAM,WAAW,GACvB,CAA0B,EACzB,IAAI,EACJ,QAAQ,EACR,gBAAgB,GACY,EAAE,EAAE,CACjC,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAmB,EAAE,EAAE;IAC5C,MAAM,iBAAiB,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;SAC7C,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,EAAE;QAC7B,MAAM,OAAO,GAAG,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,KAAK,GACV,OAAO,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAEhE,0BAA0B;QAC1B,IAAI,CAAC,KAAK;YAAE,OAAO;QAEnB,MAAM,YAAY,GAAG,OAAO,EAAE,CAAC,KAA6B,CAAC,CAAC;QAE9D,uBAAuB;QACvB,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;YACtC,OAAO,YAAY,CAAC;QACrB,CAAC;QAED,2BAA2B;QAC3B,OAAO,MAAM,CAAC,OAAO,CAAC,KAAmC,CAAC;aACxD,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,EAAE;YAC5B,mFAAmF;YACnF,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;gBAC9B,OAAO,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,KAA6B,CAAC,CAAC;YACzD,CAAC;YACD,iEAAiE;YACjE,OAAO,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,KAA6B,CAAC;gBACtD,EAAE,KAAK,CAAC,GAAG,CAAC;iBACX,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,GAAG,UAAU,IAAI,SAAS,EAAE,CAAC;iBAChD,IAAI,CAAC,GAAG,CAAC,CAAC;QACb,CAAC,CAAC;aACD,IAAI,CAAC,GAAG,CAAC,CAAC;IACb,CAAC,CAAC;SACD,IAAI,CAAC,GAAG,CAAC,CAAC;IAEZ,MAAM,eAAe,GAAG,gBAAgB;QACvC,EAAE,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,QAAQ,EAAE,EAAE,EAAE;QACpC,IACC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,KAAK,CAC7B,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAChB,KAAK,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,KAAK,CACrD,EACA,CAAC;YACF,OAAO,SAAS,CAAC;QAClB,CAAC;QACD,OAAO,SAAS,CAAC;IAClB,CAAC,CAAC;SACD,MAAM,CAAC,OAAO,CAAC,CAAC;IAElB,OAAO,EAAE,CAAC,IAAI,EAAE,iBAAiB,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC;AAChE,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { RefObject } from "react";
2
+ export declare const mergeRefs: <T>(...refs: (RefObject<T> | ((node: T) => void) | null)[]) => (node: T) => void;
@@ -0,0 +1,13 @@
1
+ export const mergeRefs = (...refs) => {
2
+ return (node) => {
3
+ for (const ref of refs) {
4
+ if (!ref)
5
+ continue;
6
+ if (typeof ref === "function")
7
+ ref(node);
8
+ else if (ref)
9
+ ref.current = node;
10
+ }
11
+ };
12
+ };
13
+ //# sourceMappingURL=merge-refs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-refs.js","sourceRoot":"","sources":["../../../src/utilities/merge-refs/merge-refs.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,SAAS,GAAG,CACxB,GAAG,IAAmD,EACrD,EAAE;IACH,OAAO,CAAC,IAAO,EAAE,EAAE;QAClB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACxB,IAAI,CAAC,GAAG;gBAAE,SAAS;YACnB,IAAI,OAAO,GAAG,KAAK,UAAU;gBAAE,GAAG,CAAC,IAAI,CAAC,CAAC;iBACpC,IAAI,GAAG;gBAAE,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC;QAClC,CAAC;IACF,CAAC,CAAC;AACH,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { type ReactElement, type ReactNode } from "react";
2
+ type TruncatedElementsOptions = {
3
+ page: number;
4
+ children: ReactNode;
5
+ };
6
+ export declare const getTruncatedElements: ({ page, children, }: TruncatedElementsOptions) => ReactNode[] | (string | number | bigint | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element)[];
7
+ export {};
@@ -0,0 +1,101 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { PaginationItem, } from "@components/pagination/pagination";
3
+ import { Children, isValidElement, } from "react";
4
+ export const getTruncatedElements = ({ page, children, }) => {
5
+ const elements = Children.toArray(children);
6
+ const paginationItems = elements.filter((el) => isValidElement(el) && el.type === PaginationItem);
7
+ if (paginationItems.length <= 7) {
8
+ return elements.map((element, index) => {
9
+ if (isValidElement(element) && element.type === PaginationItem) {
10
+ const pageNumber = elements
11
+ .slice(0, index)
12
+ .filter((el) => isValidElement(el) && el.type === PaginationItem).length + 1;
13
+ const elementProps = element;
14
+ return (_jsx(PaginationItem, { index: pageNumber, children: elementProps.props.children }, `pagination-item-${pageNumber}`));
15
+ }
16
+ return element;
17
+ });
18
+ }
19
+ const result = [];
20
+ let currentIndex = 0;
21
+ // Add non-PaginationItem elements at the start
22
+ while (currentIndex < elements.length) {
23
+ const element = elements[currentIndex];
24
+ if (!isValidElement(element) ||
25
+ (isValidElement(element) && element.type !== PaginationItem)) {
26
+ result.push(element);
27
+ currentIndex++;
28
+ }
29
+ else {
30
+ break;
31
+ }
32
+ }
33
+ // Always show first 4 pages when in that range
34
+ if (page <= 4) {
35
+ // Show first 4 pages
36
+ for (let i = 0; i < 4; i++) {
37
+ const item = paginationItems[i];
38
+ if (item) {
39
+ result.push(_jsx(PaginationItem, { index: i + 1, children: item.props.children }, `pagination-item-${i + 1}`));
40
+ }
41
+ }
42
+ if (paginationItems.length > 4) {
43
+ result.push(_jsx("span", { className: "flex h-10 w-10 items-center justify-center text-gray-500", children: "..." }, "end-ellipsis"));
44
+ // Show last page
45
+ const lastItem = paginationItems[paginationItems.length - 1];
46
+ if (lastItem) {
47
+ result.push(_jsx(PaginationItem, { index: paginationItems.length, children: lastItem.props.children }, `pagination-item-${paginationItems.length}`));
48
+ }
49
+ }
50
+ }
51
+ // Always show last 4 pages when in that range
52
+ else if (page > paginationItems.length - 4) {
53
+ // Show first page
54
+ const firstItem = paginationItems[0];
55
+ if (firstItem) {
56
+ result.push(_jsx(PaginationItem, { index: 1, children: firstItem.props.children }, "pagination-item-1"));
57
+ }
58
+ result.push(_jsx("span", { className: "flex h-10 w-10 items-center justify-center text-gray-500", children: "..." }, "start-ellipsis"));
59
+ // Show last 4 pages
60
+ for (let i = paginationItems.length - 4; i < paginationItems.length; i++) {
61
+ const item = paginationItems[i];
62
+ if (item) {
63
+ result.push(_jsx(PaginationItem, { index: i + 1, children: item.props.children }, `pagination-item-${i + 1}`));
64
+ }
65
+ }
66
+ }
67
+ // Show truncated view for middle pages
68
+ else {
69
+ // Show first page
70
+ const firstItem = paginationItems[0];
71
+ if (firstItem) {
72
+ result.push(_jsx(PaginationItem, { index: 1, children: firstItem.props.children }, "pagination-item-1"));
73
+ }
74
+ result.push(_jsx("span", { className: "flex h-10 w-10 items-center justify-center text-gray-500", children: "..." }, "start-ellipsis"));
75
+ // Show 5 pages with current page in middle
76
+ for (let i = page - 2; i <= page + 2; i++) {
77
+ if (i > 1 && i < paginationItems.length) {
78
+ const item = paginationItems[i - 1];
79
+ if (item) {
80
+ result.push(_jsx(PaginationItem, { index: i, children: item.props.children }, `pagination-item-${i}`));
81
+ }
82
+ }
83
+ }
84
+ result.push(_jsx("span", { className: "flex h-10 w-10 items-center justify-center text-gray-500", children: "..." }, "end-ellipsis"));
85
+ const lastItem = paginationItems[paginationItems.length - 1];
86
+ if (lastItem) {
87
+ result.push(_jsx(PaginationItem, { index: paginationItems.length, children: lastItem.props.children }, `pagination-item-${paginationItems.length}`));
88
+ }
89
+ }
90
+ // Add remaining non-PaginationItem elements
91
+ while (currentIndex < elements.length) {
92
+ const element = elements[currentIndex];
93
+ if (!isValidElement(element) ||
94
+ (isValidElement(element) && element.type !== PaginationItem)) {
95
+ result.push(element);
96
+ }
97
+ currentIndex++;
98
+ }
99
+ return result;
100
+ };
101
+ //# sourceMappingURL=get-truncated-elements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-truncated-elements.js","sourceRoot":"","sources":["../../../src/utilities/pagination/get-truncated-elements.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare const handleKeyboardNavigation: (e: KeyboardEvent, refs: React.RefObject<Map<number, HTMLButtonElement>>, totalPages: number) => void;
@@ -0,0 +1,70 @@
1
+ export const handleKeyboardNavigation = (e, refs, totalPages) => {
2
+ if (!refs.current.size)
3
+ return;
4
+ const currentFocusedIndex = Array.from(refs.current.entries()).find(([_, el]) => el === document.activeElement)?.[0];
5
+ if (currentFocusedIndex === undefined)
6
+ return;
7
+ let nextIndex;
8
+ switch (e.key) {
9
+ case "ArrowLeft":
10
+ case "ArrowUp": {
11
+ if (currentFocusedIndex === 0) {
12
+ nextIndex = totalPages;
13
+ }
14
+ else if (currentFocusedIndex === totalPages + 1) {
15
+ nextIndex = totalPages;
16
+ }
17
+ else if (currentFocusedIndex === 1) {
18
+ nextIndex = 0;
19
+ }
20
+ else {
21
+ // Find the next available index to the left
22
+ for (let i = currentFocusedIndex - 1; i >= 0; i--) {
23
+ if (refs.current.has(i)) {
24
+ nextIndex = i;
25
+ break;
26
+ }
27
+ }
28
+ // If no index found to the left, try wrapping to the end
29
+ if (nextIndex === undefined) {
30
+ nextIndex = 0;
31
+ }
32
+ }
33
+ break;
34
+ }
35
+ case "ArrowRight":
36
+ case "ArrowDown": {
37
+ if (currentFocusedIndex === 0) {
38
+ nextIndex = 1;
39
+ }
40
+ else if (currentFocusedIndex === totalPages) {
41
+ nextIndex = totalPages + 1;
42
+ }
43
+ else {
44
+ // Find the next available index to the right
45
+ for (let i = currentFocusedIndex + 1; i <= totalPages + 1; i++) {
46
+ if (refs.current.has(i)) {
47
+ nextIndex = i;
48
+ break;
49
+ }
50
+ }
51
+ // If no index found to the right, try wrapping to the start
52
+ if (nextIndex === undefined) {
53
+ nextIndex = totalPages + 1;
54
+ }
55
+ }
56
+ break;
57
+ }
58
+ case "Home":
59
+ nextIndex = 1;
60
+ break;
61
+ case "End":
62
+ nextIndex = totalPages;
63
+ break;
64
+ }
65
+ if (nextIndex !== undefined && nextIndex !== currentFocusedIndex) {
66
+ e.preventDefault();
67
+ refs.current.get(nextIndex)?.focus();
68
+ }
69
+ };
70
+ //# sourceMappingURL=keyboard-navigation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboard-navigation.js","sourceRoot":"","sources":["../../../src/utilities/pagination/keyboard-navigation.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACvC,CAAgB,EAChB,IAAqD,EACrD,UAAkB,EACjB,EAAE;IACH,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI;QAAE,OAAO;IAE/B,MAAM,mBAAmB,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAClE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,QAAQ,CAAC,aAAa,CAC1C,EAAE,CAAC,CAAC,CAAC,CAAC;IAEP,IAAI,mBAAmB,KAAK,SAAS;QAAE,OAAO;IAE9C,IAAI,SAA6B,CAAC;IAElC,QAAQ,CAAC,CAAC,GAAG,EAAE,CAAC;QACf,KAAK,WAAW,CAAC;QACjB,KAAK,SAAS,CAAC,CAAC,CAAC;YAChB,IAAI,mBAAmB,KAAK,CAAC,EAAE,CAAC;gBAC/B,SAAS,GAAG,UAAU,CAAC;YACxB,CAAC;iBAAM,IAAI,mBAAmB,KAAK,UAAU,GAAG,CAAC,EAAE,CAAC;gBACnD,SAAS,GAAG,UAAU,CAAC;YACxB,CAAC;iBAAM,IAAI,mBAAmB,KAAK,CAAC,EAAE,CAAC;gBACtC,SAAS,GAAG,CAAC,CAAC;YACf,CAAC;iBAAM,CAAC;gBACP,4CAA4C;gBAC5C,KAAK,IAAI,CAAC,GAAG,mBAAmB,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;oBACnD,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;wBACzB,SAAS,GAAG,CAAC,CAAC;wBACd,MAAM;oBACP,CAAC;gBACF,CAAC;gBACD,yDAAyD;gBACzD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;oBAC7B,SAAS,GAAG,CAAC,CAAC;gBACf,CAAC;YACF,CAAC;YACD,MAAM;QACP,CAAC;QACD,KAAK,YAAY,CAAC;QAClB,KAAK,WAAW,CAAC,CAAC,CAAC;YAClB,IAAI,mBAAmB,KAAK,CAAC,EAAE,CAAC;gBAC/B,SAAS,GAAG,CAAC,CAAC;YACf,CAAC;iBAAM,IAAI,mBAAmB,KAAK,UAAU,EAAE,CAAC;gBAC/C,SAAS,GAAG,UAAU,GAAG,CAAC,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACP,6CAA6C;gBAC7C,KAAK,IAAI,CAAC,GAAG,mBAAmB,GAAG,CAAC,EAAE,CAAC,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;oBAChE,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;wBACzB,SAAS,GAAG,CAAC,CAAC;wBACd,MAAM;oBACP,CAAC;gBACF,CAAC;gBACD,4DAA4D;gBAC5D,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;oBAC7B,SAAS,GAAG,UAAU,GAAG,CAAC,CAAC;gBAC5B,CAAC;YACF,CAAC;YACD,MAAM;QACP,CAAC;QACD,KAAK,MAAM;YACV,SAAS,GAAG,CAAC,CAAC;YACd,MAAM;QACP,KAAK,KAAK;YACT,SAAS,GAAG,UAAU,CAAC;YACvB,MAAM;IACR,CAAC;IAED,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,mBAAmB,EAAE,CAAC;QAClE,CAAC,CAAC,cAAc,EAAE,CAAC;QACnB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC;IACtC,CAAC;AACF,CAAC,CAAC"}
@@ -0,0 +1,37 @@
1
+ export type Breakpoint = "initial" | "sm" | "md" | "lg" | "xl";
2
+ export type BreakpointsMap<V> = Partial<{
3
+ [breakpoint in Breakpoint]: V;
4
+ }>;
5
+ export type ResponsiveValue<T> = T | BreakpointsMap<T>;
6
+ /**
7
+ * Maps a ResponsiveValue to a new ResponsiveValue using the provided mapper function. Singular values are passed through as is.
8
+ *
9
+ * @template V The type of the original value
10
+ * @template T The type of the mapped value
11
+ * @param {ResponsiveValue<V>} value - The original ResponsiveValue to be mapped
12
+ * @param {function(V): T} mapper - A function that maps a ResponsiveValue to a new ResponsiveValue
13
+ * @returns {ResponsiveValue<T>} A new ResponsiveValue with the mapped values
14
+ *
15
+ *
16
+ * @example
17
+ * const sizes = {
18
+ * initial: 'md',
19
+ * sm: 'lg',
20
+ * }
21
+ *
22
+ * const output = mapResponsiveValue(sizes, size => {
23
+ * switch (size) {
24
+ * case 'initial':
25
+ * return 'sm';
26
+ * case 'sm':
27
+ * return 'md';
28
+ * }
29
+ * });
30
+ *
31
+ * // console.log(output)
32
+ * {
33
+ * initial: 'sm',
34
+ * sm: 'md',
35
+ * }
36
+ */
37
+ export declare const mapResponsiveValue: <V, T>(value: ResponsiveValue<V>, mapper: (value: V) => T) => ResponsiveValue<T>;
@@ -0,0 +1,40 @@
1
+ const isSingularValue = (value) => !isBreakpointsMap(value);
2
+ const isBreakpointsMap = (value) => typeof value === "object" && value != null && !Array.isArray(value);
3
+ /**
4
+ * Maps a ResponsiveValue to a new ResponsiveValue using the provided mapper function. Singular values are passed through as is.
5
+ *
6
+ * @template V The type of the original value
7
+ * @template T The type of the mapped value
8
+ * @param {ResponsiveValue<V>} value - The original ResponsiveValue to be mapped
9
+ * @param {function(V): T} mapper - A function that maps a ResponsiveValue to a new ResponsiveValue
10
+ * @returns {ResponsiveValue<T>} A new ResponsiveValue with the mapped values
11
+ *
12
+ *
13
+ * @example
14
+ * const sizes = {
15
+ * initial: 'md',
16
+ * sm: 'lg',
17
+ * }
18
+ *
19
+ * const output = mapResponsiveValue(sizes, size => {
20
+ * switch (size) {
21
+ * case 'initial':
22
+ * return 'sm';
23
+ * case 'sm':
24
+ * return 'md';
25
+ * }
26
+ * });
27
+ *
28
+ * // console.log(output)
29
+ * {
30
+ * initial: 'sm',
31
+ * sm: 'md',
32
+ * }
33
+ */
34
+ export const mapResponsiveValue = (value, mapper) => isSingularValue(value)
35
+ ? mapper(value)
36
+ : Object.fromEntries(Object.entries(value).map(([breakpoint, value]) => [
37
+ breakpoint,
38
+ mapper(value),
39
+ ]));
40
+ //# sourceMappingURL=responsive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"responsive.js","sourceRoot":"","sources":["../../../src/utilities/responsive/responsive.ts"],"names":[],"mappings":"AAQA,MAAM,eAAe,GAAG,CAAI,KAAyB,EAAc,EAAE,CACpE,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;AAE1B,MAAM,gBAAgB,GAAG,CACxB,KAAyB,EACI,EAAE,CAC/B,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAErE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CACjC,KAAyB,EACzB,MAAuB,EACF,EAAE,CACvB,eAAe,CAAC,KAAK,CAAC;IACrB,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;IACf,CAAC,CAAE,MAAM,CAAC,WAAW,CACnB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC;QAClD,UAAU;QACV,MAAM,CAAC,KAAK,CAAC;KACb,CAAC,CACoB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "luan-ui",
3
+ "version": "0.1.0",
4
+ "description": "A UI library for React",
5
+ "author": "benebene84 <benedikt.sperl@gmail.com> (https://github.com/benebene84)",
6
+ "main": "/dist/index.js",
7
+ "type": "module",
8
+ "files": [
9
+ "dist",
10
+ "README.md",
11
+ "LICENSE"
12
+ ],
13
+ "scripts": {
14
+ "build": "rm -rf dist && tsc -p tsconfig.build.json",
15
+ "dev": "storybook dev -p 6006",
16
+ "build-storybook": "storybook build",
17
+ "check": "biome check",
18
+ "fix": "biome check --write",
19
+ "fix:unsafe": "biome check --write --unsafe",
20
+ "lint": "biome lint",
21
+ "format": "biome format --write",
22
+ "test": "vitest",
23
+ "test:ci": "vitest --run"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/benebene84/luan-ui.git"
28
+ },
29
+ "license": "MIT",
30
+ "bugs": {
31
+ "url": "https://github.com/benebene84/luan-ui/issues"
32
+ },
33
+ "homepage": "https://github.com/benebene84/luan-ui#readme",
34
+ "devDependencies": {
35
+ "@biomejs/biome": "^1.9.4",
36
+ "@chromatic-com/storybook": "^3",
37
+ "@radix-ui/react-icons": "^1.3.2",
38
+ "@storybook/addon-essentials": "^8.6.14",
39
+ "@storybook/addon-interactions": "^8.6.14",
40
+ "@storybook/addon-onboarding": "^8.6.14",
41
+ "@storybook/addon-webpack5-compiler-swc": "^3.0.0",
42
+ "@storybook/blocks": "^8.6.14",
43
+ "@storybook/react": "^8.6.14",
44
+ "@storybook/react-webpack5": "^8.6.14",
45
+ "@storybook/test": "^8.6.14",
46
+ "@tailwindcss/postcss": "^4.1.7",
47
+ "@testing-library/dom": "^10.4.0",
48
+ "@testing-library/jest-dom": "^6.6.3",
49
+ "@testing-library/react": "^16.3.0",
50
+ "@testing-library/user-event": "^14.6.1",
51
+ "@types/react": "^19.1.4",
52
+ "@types/react-dom": "^19.1.5",
53
+ "@vitejs/plugin-react": "^4.4.1",
54
+ "clsx": "^2.1.1",
55
+ "css-loader": "^7.1.2",
56
+ "jsdom": "^26.1.0",
57
+ "lefthook": "^1.11.13",
58
+ "postcss": "^8.5.3",
59
+ "postcss-loader": "^8.1.1",
60
+ "storybook": "^8.6.14",
61
+ "style-loader": "^4.0.0",
62
+ "tailwindcss": "^4.1.7",
63
+ "typescript": "^5.8.3",
64
+ "vitest": "^3.1.3"
65
+ },
66
+ "peerDependencies": {
67
+ "react": "^18.0.0 || ^19.0.0",
68
+ "react-dom": "^18.0.0 || ^19.0.0"
69
+ },
70
+ "dependencies": {
71
+ "@radix-ui/react-compose-refs": "^1.1.2",
72
+ "radix-ui": "^1.4.1",
73
+ "sonner": "^2.0.3",
74
+ "tailwind-merge": "^3.3.0"
75
+ },
76
+ "packageManager": "pnpm@9.14.2"
77
+ }