@bemedev/mind-flow 0.3.2 → 1.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 (115) hide show
  1. package/README.md +55 -12
  2. package/lib/helpers/clamp.d.ts +2 -0
  3. package/lib/helpers/clamp.d.ts.map +1 -0
  4. package/lib/helpers/createContext.d.ts +1 -1
  5. package/lib/helpers/createContext.d.ts.map +1 -1
  6. package/lib/helpers/index.d.ts +3 -0
  7. package/lib/helpers/index.d.ts.map +1 -0
  8. package/lib/index.cjs.js +1 -1
  9. package/lib/index.d.ts +9 -1
  10. package/lib/index.d.ts.map +1 -1
  11. package/lib/index.es.js +2464 -373
  12. package/lib/output.css +1 -1
  13. package/lib/server.cjs.js +1002 -262
  14. package/lib/server.es.js +968 -272
  15. package/lib/services/main.machine.d.ts +1564 -667
  16. package/lib/services/main.machine.d.ts.map +1 -1
  17. package/lib/services/main.machine.data.d.ts +14 -1
  18. package/lib/services/main.machine.data.d.ts.map +1 -1
  19. package/lib/services/main.machine.helpers.d.ts +1 -1
  20. package/lib/services/main.machine.helpers.d.ts.map +1 -1
  21. package/lib/services/main.machine.test.d.ts +2 -0
  22. package/lib/services/main.machine.test.d.ts.map +1 -0
  23. package/lib/services/main.machine.typings.d.ts +26 -8
  24. package/lib/services/main.machine.typings.d.ts.map +1 -1
  25. package/lib/ui/Flow.d.ts +8 -4
  26. package/lib/ui/Flow.d.ts.map +1 -1
  27. package/lib/ui/components/Bounds.d.ts.map +1 -1
  28. package/lib/ui/components/EdgeComponent.d.ts.map +1 -1
  29. package/lib/ui/components/EdgesBoard.d.ts.map +1 -1
  30. package/lib/ui/components/EditPanel.d.ts +19 -0
  31. package/lib/ui/components/EditPanel.d.ts.map +1 -0
  32. package/lib/ui/components/EditPanel.hooks.d.ts +29 -0
  33. package/lib/ui/components/EditPanel.hooks.d.ts.map +1 -0
  34. package/lib/ui/components/EditPanel.types.d.ts +47 -0
  35. package/lib/ui/components/EditPanel.types.d.ts.map +1 -0
  36. package/lib/ui/components/FlowChart.context.d.ts +1563 -666
  37. package/lib/ui/components/FlowChart.context.d.ts.map +1 -1
  38. package/lib/ui/components/FlowChart.d.ts +8 -47
  39. package/lib/ui/components/FlowChart.d.ts.map +1 -1
  40. package/lib/ui/components/FlowChart.types.d.ts +87 -0
  41. package/lib/ui/components/FlowChart.types.d.ts.map +1 -0
  42. package/lib/ui/components/NodeComponent.d.ts +10 -12
  43. package/lib/ui/components/NodeComponent.d.ts.map +1 -1
  44. package/lib/ui/components/NodesBoard.d.ts +17 -2
  45. package/lib/ui/components/NodesBoard.d.ts.map +1 -1
  46. package/lib/ui/components/Panels.d.ts +12 -0
  47. package/lib/ui/components/Panels.d.ts.map +1 -0
  48. package/lib/ui/globals/components/atoms/TypingText.d.ts +29 -0
  49. package/lib/ui/globals/components/atoms/TypingText.d.ts.map +1 -0
  50. package/lib/ui/globals/components/molecules/FadingDots.d.ts +21 -0
  51. package/lib/ui/globals/components/molecules/FadingDots.d.ts.map +1 -0
  52. package/lib/ui/globals/components/organisms/LoadingFallback.d.ts +46 -0
  53. package/lib/ui/globals/components/organisms/LoadingFallback.d.ts.map +1 -0
  54. package/lib/ui/globals/constants/index.d.ts +3 -0
  55. package/lib/ui/globals/constants/index.d.ts.map +1 -0
  56. package/lib/ui/globals/constants/links.d.ts +4 -0
  57. package/lib/ui/globals/constants/links.d.ts.map +1 -0
  58. package/lib/ui/globals/constants/strings.d.ts +5 -0
  59. package/lib/ui/globals/constants/strings.d.ts.map +1 -0
  60. package/lib/ui/globals/directives/clickOutside.d.ts +9 -0
  61. package/lib/ui/globals/directives/clickOutside.d.ts.map +1 -0
  62. package/lib/ui/globals/directives/index.d.ts +16 -0
  63. package/lib/ui/globals/directives/index.d.ts.map +1 -0
  64. package/lib/ui/globals/directives/mouseOut.d.ts +33 -0
  65. package/lib/ui/globals/directives/mouseOut.d.ts.map +1 -0
  66. package/lib/ui/globals/directives/mouseOut.test.d.ts +2 -0
  67. package/lib/ui/globals/directives/mouseOut.test.d.ts.map +1 -0
  68. package/lib/ui/globals/directives/resize.d.ts +11 -0
  69. package/lib/ui/globals/directives/resize.d.ts.map +1 -0
  70. package/lib/ui/globals/helpers/espace.d.ts +17 -0
  71. package/lib/ui/globals/helpers/espace.d.ts.map +1 -0
  72. package/lib/ui/globals/signals/createTyping.d.ts +27 -0
  73. package/lib/ui/globals/signals/createTyping.d.ts.map +1 -0
  74. package/lib/ui/globals/types.d.ts +54 -0
  75. package/lib/ui/globals/types.d.ts.map +1 -0
  76. package/lib/ui/utils.d.ts +11 -0
  77. package/lib/ui/utils.d.ts.map +1 -0
  78. package/package.json +2 -3
  79. package/src/helpers/clamp.ts +3 -0
  80. package/src/helpers/createContext.ts +20 -4
  81. package/src/helpers/index.ts +2 -0
  82. package/src/index.ts +9 -1
  83. package/src/services/main.machine.data.ts +13 -2
  84. package/src/services/main.machine.helpers.ts +12 -4
  85. package/src/services/main.machine.test.ts +205 -0
  86. package/src/services/main.machine.ts +136 -65
  87. package/src/services/main.machine.typings.ts +31 -6
  88. package/src/ui/Flow.tsx +47 -8
  89. package/src/ui/components/Bounds.tsx +0 -2
  90. package/src/ui/components/EdgeComponent.tsx +60 -46
  91. package/src/ui/components/EdgesBoard.tsx +1 -2
  92. package/src/ui/components/EditPanel.hooks.ts +69 -0
  93. package/src/ui/components/EditPanel.tsx +89 -0
  94. package/src/ui/components/EditPanel.types.ts +49 -0
  95. package/src/ui/components/FlowChart.context.ts +21 -11
  96. package/src/ui/components/FlowChart.tsx +20 -57
  97. package/src/ui/components/FlowChart.types.ts +92 -0
  98. package/src/ui/components/NodeComponent.tsx +81 -58
  99. package/src/ui/components/NodesBoard.tsx +29 -7
  100. package/src/ui/components/Panels.tsx +31 -0
  101. package/src/ui/globals/components/atoms/TypingText.tsx +63 -0
  102. package/src/ui/globals/components/molecules/FadingDots.tsx +45 -0
  103. package/src/ui/globals/components/organisms/LoadingFallback.tsx +116 -0
  104. package/src/ui/globals/constants/index.ts +2 -0
  105. package/src/ui/globals/constants/links.ts +15 -0
  106. package/src/ui/globals/constants/strings.ts +5 -0
  107. package/src/ui/globals/directives/clickOutside.ts +18 -0
  108. package/src/ui/globals/directives/index.ts +16 -0
  109. package/src/ui/globals/directives/mouseOut.test.ts +115 -0
  110. package/src/ui/globals/directives/mouseOut.ts +98 -0
  111. package/src/ui/globals/directives/resize.ts +33 -0
  112. package/src/ui/globals/helpers/espace.ts +26 -0
  113. package/src/ui/globals/signals/createTyping.ts +82 -0
  114. package/src/ui/globals/types.ts +83 -0
  115. package/src/ui/utils.ts +14 -0
@@ -0,0 +1,115 @@
1
+ import { createRoot } from 'solid-js';
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
+
4
+ import { mouseOut } from './mouseOut';
5
+
6
+ describe('Directive => mouseOut', () => {
7
+ let el: HTMLDivElement;
8
+
9
+ beforeEach(() => {
10
+ vi.useFakeTimers();
11
+ el = document.createElement('div');
12
+ document.body.appendChild(el);
13
+ });
14
+
15
+ afterEach(() => {
16
+ vi.useRealTimers();
17
+ el.remove();
18
+ });
19
+
20
+ it('#01 => should trigger callback immediately when no delay is provided', () => {
21
+ const callback = vi.fn();
22
+
23
+ createRoot(dispose => {
24
+ mouseOut(el, () => callback);
25
+
26
+ el.dispatchEvent(new MouseEvent('mouseleave'));
27
+ expect(callback).toHaveBeenCalledTimes(1);
28
+
29
+ dispose();
30
+ });
31
+ });
32
+
33
+ it('#02 => should wait for the specified delay before executing the callback', () => {
34
+ const callback = vi.fn();
35
+
36
+ createRoot(dispose => {
37
+ mouseOut(el, () => [callback, 300]);
38
+
39
+ el.dispatchEvent(new MouseEvent('mouseleave'));
40
+ expect(callback).not.toHaveBeenCalled();
41
+
42
+ vi.advanceTimersByTime(200);
43
+ expect(callback).not.toHaveBeenCalled();
44
+
45
+ vi.advanceTimersByTime(100);
46
+ expect(callback).toHaveBeenCalledTimes(1);
47
+
48
+ dispose();
49
+ });
50
+ });
51
+
52
+ it('#03 => should reset/cancel the timer when user re-enters before delay finishes', () => {
53
+ const callback = vi.fn();
54
+
55
+ createRoot(dispose => {
56
+ mouseOut(el, () => ({ callback, delay: 500 }));
57
+
58
+ el.dispatchEvent(new MouseEvent('mouseleave'));
59
+ expect(callback).not.toHaveBeenCalled();
60
+
61
+ vi.advanceTimersByTime(300);
62
+ expect(callback).not.toHaveBeenCalled();
63
+
64
+ // User re-enters
65
+ el.dispatchEvent(new MouseEvent('mouseenter'));
66
+
67
+ // Time passes past the original 500ms
68
+ vi.advanceTimersByTime(500);
69
+ expect(callback).not.toHaveBeenCalled();
70
+
71
+ dispose();
72
+ });
73
+ });
74
+
75
+ it('#04 => should re-arm timer when mouse leaves again after re-entering', () => {
76
+ const callback = vi.fn();
77
+
78
+ createRoot(dispose => {
79
+ mouseOut(el, () => ({ onMouseOut: callback, delay: 400 }));
80
+
81
+ el.dispatchEvent(new MouseEvent('mouseleave'));
82
+ vi.advanceTimersByTime(200);
83
+ el.dispatchEvent(new MouseEvent('mouseenter'));
84
+ vi.advanceTimersByTime(300);
85
+ expect(callback).not.toHaveBeenCalled();
86
+
87
+ // Leave again
88
+ el.dispatchEvent(new MouseEvent('mouseleave'));
89
+ vi.advanceTimersByTime(400);
90
+ expect(callback).toHaveBeenCalledTimes(1);
91
+
92
+ dispose();
93
+ });
94
+ });
95
+
96
+ it('#05 => should cancel pending timer and remove event listeners on cleanup', () => {
97
+ const callback = vi.fn();
98
+
99
+ let disposeRoot!: () => void;
100
+ createRoot(dispose => {
101
+ disposeRoot = dispose;
102
+ mouseOut(el, () => [callback, 300]);
103
+ });
104
+
105
+ el.dispatchEvent(new MouseEvent('mouseleave'));
106
+ disposeRoot();
107
+
108
+ vi.advanceTimersByTime(400);
109
+ expect(callback).not.toHaveBeenCalled();
110
+
111
+ // Ensure listeners are removed
112
+ el.dispatchEvent(new MouseEvent('mouseleave'));
113
+ expect(callback).not.toHaveBeenCalled();
114
+ });
115
+ });
@@ -0,0 +1,98 @@
1
+ import { onCleanup } from 'solid-js';
2
+
3
+ /**
4
+ * Callback function type invoked when the mouse leaves the bound element.
5
+ *
6
+ * @param event - Optional mouse event that triggered the leave.
7
+ */
8
+ export type MouseOutCallback = (event?: MouseEvent) => void;
9
+
10
+ /** Configuration options for the {@linkcode mouseOut} directive. */
11
+ export type MouseOutOptions = {
12
+ /** Callback function to trigger after the timer elapses. */
13
+ callback?: MouseOutCallback;
14
+
15
+ /** Alternative callback property name for triggering after the timer elapses. */
16
+ onMouseOut?: MouseOutCallback;
17
+
18
+ /**
19
+ * Delay in milliseconds before triggering the callback after the mouse leaves. If
20
+ * the cursor re-enters the element before this duration, the timer is reset.
21
+ *
22
+ * @defaultValue 0
23
+ */
24
+ delay?: number;
25
+ };
26
+
27
+ /** Accepted parameter shapes for the {@linkcode mouseOut} directive. */
28
+ export type MouseOutParam =
29
+ | MouseOutCallback
30
+ | [callback: MouseOutCallback, delay?: number]
31
+ | MouseOutOptions;
32
+
33
+ /**
34
+ * Solid custom directive that triggers a callback when the mouse leaves the bound
35
+ * element after a specified timer duration. If the user re-enters the component
36
+ * before the timer expires, the timer is reset/cancelled.
37
+ *
38
+ * @param el - The bound DOM element.
39
+ * @param accessor - Accessor returning a callback, tuple `[callback, delay]`, or
40
+ * type {@linkcode MouseOutOptions}.
41
+ */
42
+ export const mouseOut = (
43
+ el: HTMLElement | Element,
44
+ accessor: () => MouseOutParam,
45
+ ) => {
46
+ let timer: ReturnType<typeof setTimeout> | undefined;
47
+
48
+ const clear = () => {
49
+ if (timer !== undefined) {
50
+ clearTimeout(timer);
51
+ timer = undefined;
52
+ }
53
+ };
54
+
55
+ const onMouseEnter = () => {
56
+ clear();
57
+ };
58
+
59
+ const onMouseLeave = (event: MouseEvent) => {
60
+ clear();
61
+
62
+ const param = accessor();
63
+ if (!param) return;
64
+
65
+ let callback: MouseOutCallback | undefined;
66
+ let delay = 0;
67
+
68
+ if (typeof param === 'function') {
69
+ callback = param;
70
+ } else if (Array.isArray(param)) {
71
+ callback = param[0];
72
+ delay = param[1] ?? 0;
73
+ } else if (typeof param === 'object') {
74
+ callback = param.callback ?? param.onMouseOut;
75
+ delay = param.delay ?? 0;
76
+ }
77
+
78
+ if (!callback) return;
79
+
80
+ if (delay > 0) {
81
+ timer = setTimeout(() => {
82
+ callback?.(event);
83
+ timer = undefined;
84
+ }, delay);
85
+ } else {
86
+ callback(event);
87
+ }
88
+ };
89
+
90
+ el.addEventListener('mouseleave', onMouseLeave as EventListener);
91
+ el.addEventListener('mouseenter', onMouseEnter as EventListener);
92
+
93
+ onCleanup(() => {
94
+ clear();
95
+ el.removeEventListener('mouseleave', onMouseLeave as EventListener);
96
+ el.removeEventListener('mouseenter', onMouseEnter as EventListener);
97
+ });
98
+ };
@@ -0,0 +1,33 @@
1
+ import { useFlow } from '../../components/FlowChart.context';
2
+
3
+ /**
4
+ * Creates a ResizeObserver hook attached to the node content container element,
5
+ * dispatching RESIZE events to update node dimensions and edge positioning in
6
+ * real-time.
7
+ *
8
+ * @param id - Unique identifier of the flowchart node.
9
+ *
10
+ * @returns Ref callback attaching and cleaning up the observer on unmount.
11
+ */
12
+ export const resize = (id: string) => (el: HTMLDivElement) => {
13
+ const service = useFlow();
14
+
15
+ const resize = new ResizeObserver(entries => {
16
+ const entry = entries[0].target;
17
+ if (!entry) return;
18
+
19
+ const width = entry.clientWidth;
20
+ const height = entry.clientHeight;
21
+
22
+ if (width > 0 && height > 0) {
23
+ service.send({ type: 'RESIZE', payload: { id, size: { width, height } } });
24
+ }
25
+ });
26
+
27
+ resize.observe(el);
28
+
29
+ return () => {
30
+ resize.unobserve(el);
31
+ resize.disconnect();
32
+ };
33
+ };
@@ -0,0 +1,26 @@
1
+ import { NON_BREAKABLE_SPACE, VISIBLE_ESPACE } from '../constants';
2
+
3
+ /**
4
+ * Returns a zero-width space if string is empty, preserving layout flow.
5
+ *
6
+ * @param newText - The input text string.
7
+ *
8
+ * @returns The input text or zero-width visible space placeholder.
9
+ */
10
+ export const espace = (newText: string) => {
11
+ if (newText === '') {
12
+ return VISIBLE_ESPACE;
13
+ }
14
+ return newText;
15
+ };
16
+
17
+ /**
18
+ * Generates a repeated sequence of non-breakable space characters.
19
+ *
20
+ * @param count - The number of non-breakable spaces to repeat. Defaults to 1.
21
+ *
22
+ * @returns The non-breakable space string sequence.
23
+ */
24
+ export const repeatSpace = (count = 1) => {
25
+ return NON_BREAKABLE_SPACE.repeat(count);
26
+ };
@@ -0,0 +1,82 @@
1
+ import { createSignal, onCleanup } from 'solid-js';
2
+
3
+ import { VISIBLE_ESPACE } from '../constants';
4
+ import { espace } from '../helpers/espace';
5
+
6
+ /** Typing animation options. */
7
+ type Props = {
8
+ /** The text content to type out. */
9
+ content: string;
10
+ /** Minimum delay interval between keystrokes in milliseconds. */
11
+ min: number;
12
+ /** Whether the typing effect rewinds after completion. */
13
+ rewind?: boolean;
14
+ /** Delay in milliseconds before rewinding or restarting. */
15
+ rewindDelay?: number;
16
+ };
17
+
18
+ /**
19
+ * Creates a reactive typewriter effect signal with oscillatory typing delay and
20
+ * optional rewind.
21
+ *
22
+ * @param props - Configuration properties of type {@linkcode Props}.
23
+ *
24
+ * @returns An object containing the current text signal, type trigger, and text
25
+ * setter.
26
+ */
27
+ export const createTyping = ({ content, min, ...props }: Props) => {
28
+ const rewind = props.rewind ?? false;
29
+ const rewindDelay = (props as any).rewindDelay ?? 500;
30
+
31
+ const OSCILLATION = Math.min(min / 10 + content.length / 10, 10);
32
+
33
+ const [text, setText] = createSignal(VISIBLE_ESPACE);
34
+ let timeoutId: number | NodeJS.Timeout;
35
+ let index = 0;
36
+ let isTyping = true;
37
+ const max = min + OSCILLATION;
38
+ let interval = min;
39
+ let intervalDirection = 1; // 1 for incrementing, -1 for decrementing
40
+
41
+ const type = () => {
42
+ if (isTyping) {
43
+ if (index < content.length) {
44
+ setText(prev => prev + content[index]);
45
+ index++;
46
+ timeoutId = setTimeout(type, interval);
47
+ } else if (rewind) {
48
+ isTyping = false;
49
+ // interval = min;
50
+ intervalDirection = 1;
51
+ timeoutId = setTimeout(type, rewindDelay);
52
+ }
53
+ } else {
54
+ // Rewind mode
55
+ if (index > 0) {
56
+ index--;
57
+ setText(espace(content.substring(0, index)));
58
+ timeoutId = setTimeout(type, interval);
59
+ } else {
60
+ isTyping = true;
61
+ // interval = min;
62
+ intervalDirection = 1;
63
+ timeoutId = setTimeout(type, rewindDelay);
64
+ }
65
+ }
66
+
67
+ // Update interval with oscillation
68
+ interval += intervalDirection;
69
+
70
+ if (interval >= max) {
71
+ intervalDirection = -1;
72
+ } else if (interval <= min) {
73
+ intervalDirection = 1;
74
+ }
75
+ };
76
+
77
+ onCleanup(() => {
78
+ if (timeoutId !== undefined) clearTimeout(timeoutId);
79
+ });
80
+
81
+ return { text, type, setText };
82
+ };
@@ -0,0 +1,83 @@
1
+ import type { Component, ComponentProps, JSX, ValidComponent } from 'solid-js';
2
+
3
+ /**
4
+ * Extracts a subset of component props from a valid component.
5
+ *
6
+ * @template | {@linkcode ValidComponent} `T` - Target component type.
7
+ * @template | `keyof ComponentProps<T>` `K` - Prop keys to pick.
8
+ */
9
+ export type PropsOf<
10
+ T extends ValidComponent,
11
+ K extends keyof ComponentProps<T> = never,
12
+ > = Pick<ComponentProps<T>, K>;
13
+
14
+ /**
15
+ * Omits a subset of component props from a valid component.
16
+ *
17
+ * @template | {@linkcode ValidComponent} `T` - Target component type.
18
+ * @template | `keyof ComponentProps<T>` `K` - Prop keys to omit.
19
+ */
20
+ export type OmitPropsOf<
21
+ T extends ValidComponent,
22
+ K extends keyof ComponentProps<T> = never,
23
+ > = Omit<ComponentProps<T>, K>;
24
+
25
+ /**
26
+ * Creates a component type where specified props are optional.
27
+ *
28
+ * @template | {@linkcode ValidComponent} `T` - Target component type.
29
+ * @template | `keyof ComponentProps<T>` `K` - Prop keys to make optional.
30
+ */
31
+ export type ReduceComponent<
32
+ T extends ValidComponent,
33
+ K extends keyof ComponentProps<T> = never,
34
+ > = Component<OmitPropsOf<T, K> & Partial<PropsOf<T, K>>>;
35
+
36
+ /**
37
+ * Short alias for type {@linkcode ReduceComponent}.
38
+ *
39
+ * @template | {@linkcode ValidComponent} `T` - Target component type.
40
+ * @template | `keyof ComponentProps<T>` `K` - Prop keys to make optional.
41
+ */
42
+ export type RC<
43
+ T extends ValidComponent,
44
+ K extends keyof ComponentProps<T> = never,
45
+ > = ReduceComponent<T, K>;
46
+
47
+ /**
48
+ * Creates a component type requiring only a picked subset of props.
49
+ *
50
+ * @template | {@linkcode ValidComponent} `T` - Target component type.
51
+ * @template | `keyof ComponentProps<T>` `K` - Picked prop keys.
52
+ */
53
+ export type PickComponent<
54
+ T extends ValidComponent,
55
+ K extends keyof ComponentProps<T> = never,
56
+ > = Component<PropsOf<T, K>>;
57
+
58
+ /**
59
+ * Short alias for type {@linkcode PickComponent}.
60
+ *
61
+ * @template | {@linkcode ValidComponent} `T` - Target component type.
62
+ * @template | `keyof ComponentProps<T>` `K` - Picked prop keys.
63
+ */
64
+ export type PiC<
65
+ T extends ValidComponent,
66
+ K extends keyof ComponentProps<T> = never,
67
+ > = PickComponent<T, K>;
68
+
69
+ /** Type representing an HTML `<link>` tag properties. */
70
+ export type RootLink = JSX.HTMLElementTags['link'];
71
+
72
+ /**
73
+ * Type representing an HTML element ref or ref callback.
74
+ *
75
+ * @template | {@linkcode HTMLElement} `T` - Target HTML element type.
76
+ */
77
+ export type Ref<T extends HTMLElement> =
78
+ | T
79
+ | ((el: T | undefined) => void)
80
+ | undefined;
81
+
82
+ /** Class map dictionary for conditional styling in Solid.js components. */
83
+ export type ClassList = Record<string, boolean | undefined>;
@@ -0,0 +1,14 @@
1
+ import { clsx, type ClassValue } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ /**
5
+ * Merges conditional CSS class names using `clsx` and resolves Tailwind CSS class
6
+ * conflicts with `twMerge`.
7
+ *
8
+ * @param inputs - Variable list of class values, objects, or arrays.
9
+ *
10
+ * @returns The resolved single class name string.
11
+ */
12
+ export function cn(...inputs: ClassValue[]) {
13
+ return twMerge(clsx(inputs));
14
+ }