@mks2508/mks-ui 0.2.1 → 0.3.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.
Files changed (155) hide show
  1. package/dist/react-ui/hooks/Animation/UseAutoHeight.js +7 -7
  2. package/dist/react-ui/hooks/DOM/UseIsInView.js +3 -3
  3. package/dist/react-ui/hooks/Formatting/UseListFormat.d.ts +49 -0
  4. package/dist/react-ui/hooks/Formatting/UseListFormat.d.ts.map +1 -0
  5. package/dist/react-ui/hooks/Formatting/UseListFormat.js +105 -0
  6. package/dist/react-ui/hooks/State/UseControlledState.js +4 -4
  7. package/dist/react-ui/hooks/State/UseDataState.js +5 -5
  8. package/dist/react-ui/hooks/index.d.ts +2 -0
  9. package/dist/react-ui/hooks/index.d.ts.map +1 -1
  10. package/dist/react-ui/hooks/index.js +1 -0
  11. package/dist/react-ui/index.js +22 -2
  12. package/dist/react-ui/lib/get-strict-context.js +3 -3
  13. package/dist/react-ui/primitives/CountingNumber/index.js +3 -3
  14. package/dist/react-ui/primitives/Highlight/index.js +26 -26
  15. package/dist/react-ui/primitives/Slot/index.js +3 -3
  16. package/dist/react-ui/primitives/index.d.ts +1 -0
  17. package/dist/react-ui/primitives/index.d.ts.map +1 -1
  18. package/dist/react-ui/primitives/index.js +18 -0
  19. package/dist/react-ui/primitives/waapi/Morph/Morph.types.d.ts +76 -0
  20. package/dist/react-ui/primitives/waapi/Morph/Morph.types.d.ts.map +1 -0
  21. package/dist/react-ui/primitives/waapi/Morph/MorphContext.d.ts +11 -0
  22. package/dist/react-ui/primitives/waapi/Morph/MorphContext.d.ts.map +1 -0
  23. package/dist/react-ui/primitives/waapi/Morph/MorphContext.js +19 -0
  24. package/dist/react-ui/primitives/waapi/Morph/index.d.ts +23 -0
  25. package/dist/react-ui/primitives/waapi/Morph/index.d.ts.map +1 -0
  26. package/dist/react-ui/primitives/waapi/Morph/index.js +45 -0
  27. package/dist/react-ui/primitives/waapi/Morph/techniques/index.d.ts +12 -0
  28. package/dist/react-ui/primitives/waapi/Morph/techniques/index.d.ts.map +1 -0
  29. package/dist/react-ui/primitives/waapi/Morph/techniques/useCSSGridMorph.d.ts +38 -0
  30. package/dist/react-ui/primitives/waapi/Morph/techniques/useCSSGridMorph.d.ts.map +1 -0
  31. package/dist/react-ui/primitives/waapi/Morph/techniques/useCSSGridMorph.js +78 -0
  32. package/dist/react-ui/primitives/waapi/Morph/techniques/useFLIPClipPath.d.ts +23 -0
  33. package/dist/react-ui/primitives/waapi/Morph/techniques/useFLIPClipPath.d.ts.map +1 -0
  34. package/dist/react-ui/primitives/waapi/Morph/techniques/useFLIPClipPath.js +140 -0
  35. package/dist/react-ui/primitives/waapi/Morph/techniques/useViewTransitions.d.ts +28 -0
  36. package/dist/react-ui/primitives/waapi/Morph/techniques/useViewTransitions.d.ts.map +1 -0
  37. package/dist/react-ui/primitives/waapi/Morph/techniques/useViewTransitions.js +77 -0
  38. package/dist/react-ui/primitives/waapi/Morph/useMorph.d.ts +27 -0
  39. package/dist/react-ui/primitives/waapi/Morph/useMorph.d.ts.map +1 -0
  40. package/dist/react-ui/primitives/waapi/Morph/useMorph.js +86 -0
  41. package/dist/react-ui/primitives/waapi/Reorder/Reorder.types.d.ts +168 -0
  42. package/dist/react-ui/primitives/waapi/Reorder/Reorder.types.d.ts.map +1 -0
  43. package/dist/react-ui/primitives/waapi/Reorder/index.d.ts +25 -0
  44. package/dist/react-ui/primitives/waapi/Reorder/index.d.ts.map +1 -0
  45. package/dist/react-ui/primitives/waapi/Reorder/index.js +186 -0
  46. package/dist/react-ui/primitives/waapi/Reorder/useReorder.d.ts +26 -0
  47. package/dist/react-ui/primitives/waapi/Reorder/useReorder.d.ts.map +1 -0
  48. package/dist/react-ui/primitives/waapi/Reorder/useReorder.js +48 -0
  49. package/dist/react-ui/primitives/waapi/Reorder/useReorderPresence.d.ts +33 -0
  50. package/dist/react-ui/primitives/waapi/Reorder/useReorderPresence.d.ts.map +1 -0
  51. package/dist/react-ui/primitives/waapi/Reorder/useReorderPresence.js +137 -0
  52. package/dist/react-ui/primitives/waapi/Reorder/utils/separatorCoordination.d.ts +47 -0
  53. package/dist/react-ui/primitives/waapi/Reorder/utils/separatorCoordination.d.ts.map +1 -0
  54. package/dist/react-ui/primitives/waapi/Reorder/utils/separatorCoordination.js +72 -0
  55. package/dist/react-ui/primitives/waapi/SlidingNumber/SlidingNumber.styles.d.ts +10 -0
  56. package/dist/react-ui/primitives/waapi/SlidingNumber/SlidingNumber.styles.d.ts.map +1 -0
  57. package/dist/react-ui/primitives/waapi/SlidingNumber/SlidingNumber.types.d.ts +74 -0
  58. package/dist/react-ui/primitives/waapi/SlidingNumber/SlidingNumber.types.d.ts.map +1 -0
  59. package/dist/react-ui/primitives/waapi/SlidingNumber/index.d.ts +33 -0
  60. package/dist/react-ui/primitives/waapi/SlidingNumber/index.d.ts.map +1 -0
  61. package/dist/react-ui/primitives/waapi/SlidingNumber/index.js +354 -0
  62. package/dist/react-ui/primitives/waapi/SlidingText/SlidingText.styles.d.ts +25 -0
  63. package/dist/react-ui/primitives/waapi/SlidingText/SlidingText.styles.d.ts.map +1 -0
  64. package/dist/react-ui/primitives/waapi/SlidingText/SlidingText.types.d.ts +57 -0
  65. package/dist/react-ui/primitives/waapi/SlidingText/SlidingText.types.d.ts.map +1 -0
  66. package/dist/react-ui/primitives/waapi/SlidingText/index.d.ts +26 -0
  67. package/dist/react-ui/primitives/waapi/SlidingText/index.d.ts.map +1 -0
  68. package/dist/react-ui/primitives/waapi/SlidingText/index.js +105 -0
  69. package/dist/react-ui/primitives/waapi/core/animationConstants.d.ts +156 -0
  70. package/dist/react-ui/primitives/waapi/core/animationConstants.d.ts.map +1 -0
  71. package/dist/react-ui/primitives/waapi/core/animationConstants.js +180 -0
  72. package/dist/react-ui/primitives/waapi/core/index.d.ts +16 -0
  73. package/dist/react-ui/primitives/waapi/core/index.d.ts.map +1 -0
  74. package/dist/react-ui/primitives/waapi/core/index.js +5 -0
  75. package/dist/react-ui/primitives/waapi/core/types.d.ts +143 -0
  76. package/dist/react-ui/primitives/waapi/core/types.d.ts.map +1 -0
  77. package/dist/react-ui/primitives/waapi/core/useAnimationOrchestrator.d.ts +32 -0
  78. package/dist/react-ui/primitives/waapi/core/useAnimationOrchestrator.d.ts.map +1 -0
  79. package/dist/react-ui/primitives/waapi/core/useAnimationOrchestrator.js +322 -0
  80. package/dist/react-ui/primitives/waapi/core/useElementRegistry.d.ts +21 -0
  81. package/dist/react-ui/primitives/waapi/core/useElementRegistry.d.ts.map +1 -0
  82. package/dist/react-ui/primitives/waapi/core/useElementRegistry.js +65 -0
  83. package/dist/react-ui/primitives/waapi/core/useFLIPAnimation.d.ts +20 -0
  84. package/dist/react-ui/primitives/waapi/core/useFLIPAnimation.d.ts.map +1 -0
  85. package/dist/react-ui/primitives/waapi/core/useFLIPAnimation.js +99 -0
  86. package/dist/react-ui/primitives/waapi/core/usePositionCapture.d.ts +24 -0
  87. package/dist/react-ui/primitives/waapi/core/usePositionCapture.d.ts.map +1 -0
  88. package/dist/react-ui/primitives/waapi/core/usePositionCapture.js +75 -0
  89. package/dist/react-ui/primitives/waapi/index.d.ts +33 -0
  90. package/dist/react-ui/primitives/waapi/index.d.ts.map +1 -0
  91. package/dist/react-ui/primitives/waapi/index.js +18 -0
  92. package/dist/react-ui/ui/Accordion/index.js +3 -3
  93. package/dist/react-ui/ui/Button/index.js +8 -8
  94. package/dist/react-ui/ui/Combobox/index.js +2 -2
  95. package/dist/react-ui/ui/DataCard/DataCard.styles.d.ts +35 -0
  96. package/dist/react-ui/ui/DataCard/DataCard.styles.d.ts.map +1 -0
  97. package/dist/react-ui/ui/DataCard/DataCard.styles.js +114 -0
  98. package/dist/react-ui/ui/DataCard/DataCard.types.d.ts +135 -0
  99. package/dist/react-ui/ui/DataCard/DataCard.types.d.ts.map +1 -0
  100. package/dist/react-ui/ui/DataCard/index.d.ts +129 -0
  101. package/dist/react-ui/ui/DataCard/index.d.ts.map +1 -0
  102. package/dist/react-ui/ui/DataCard/index.js +276 -0
  103. package/dist/react-ui/ui/Menu/index.js +2 -2
  104. package/dist/react-ui/ui/Switch/index.js +3 -3
  105. package/dist/react-ui/ui/Tabs/index.js +3 -3
  106. package/dist/react-ui/ui/TextFlow/TextFlow.styles.d.ts +16 -0
  107. package/dist/react-ui/ui/TextFlow/TextFlow.styles.d.ts.map +1 -0
  108. package/dist/react-ui/ui/TextFlow/TextFlow.types.d.ts +101 -0
  109. package/dist/react-ui/ui/TextFlow/TextFlow.types.d.ts.map +1 -0
  110. package/dist/react-ui/ui/TextFlow/index.d.ts +26 -0
  111. package/dist/react-ui/ui/TextFlow/index.d.ts.map +1 -0
  112. package/dist/react-ui/ui/TextFlow/index.js +187 -0
  113. package/dist/react-ui/ui/index.d.ts +2 -1
  114. package/dist/react-ui/ui/index.d.ts.map +1 -1
  115. package/dist/react-ui/ui/index.js +3 -1
  116. package/package.json +6 -2
  117. package/src/react-ui/hooks/Formatting/UseListFormat.ts +134 -0
  118. package/src/react-ui/hooks/index.ts +3 -0
  119. package/src/react-ui/primitives/index.ts +3 -0
  120. package/src/react-ui/primitives/waapi/Morph/Morph.types.ts +106 -0
  121. package/src/react-ui/primitives/waapi/Morph/MorphContext.tsx +21 -0
  122. package/src/react-ui/primitives/waapi/Morph/index.tsx +56 -0
  123. package/src/react-ui/primitives/waapi/Morph/techniques/index.ts +12 -0
  124. package/src/react-ui/primitives/waapi/Morph/techniques/useCSSGridMorph.ts +88 -0
  125. package/src/react-ui/primitives/waapi/Morph/techniques/useFLIPClipPath.ts +175 -0
  126. package/src/react-ui/primitives/waapi/Morph/techniques/useViewTransitions.ts +86 -0
  127. package/src/react-ui/primitives/waapi/Morph/useMorph.ts +100 -0
  128. package/src/react-ui/primitives/waapi/Reorder/Reorder.types.ts +177 -0
  129. package/src/react-ui/primitives/waapi/Reorder/index.tsx +260 -0
  130. package/src/react-ui/primitives/waapi/Reorder/useReorder.ts +46 -0
  131. package/src/react-ui/primitives/waapi/Reorder/useReorderPresence.ts +208 -0
  132. package/src/react-ui/primitives/waapi/Reorder/utils/separatorCoordination.ts +104 -0
  133. package/src/react-ui/primitives/waapi/SlidingNumber/SlidingNumber.styles.ts +14 -0
  134. package/src/react-ui/primitives/waapi/SlidingNumber/SlidingNumber.types.ts +84 -0
  135. package/src/react-ui/primitives/waapi/SlidingNumber/index.tsx +474 -0
  136. package/src/react-ui/primitives/waapi/SlidingText/SlidingText.styles.ts +32 -0
  137. package/src/react-ui/primitives/waapi/SlidingText/SlidingText.types.ts +69 -0
  138. package/src/react-ui/primitives/waapi/SlidingText/index.tsx +140 -0
  139. package/src/react-ui/primitives/waapi/core/animationConstants.ts +215 -0
  140. package/src/react-ui/primitives/waapi/core/index.ts +53 -0
  141. package/src/react-ui/primitives/waapi/core/types.ts +200 -0
  142. package/src/react-ui/primitives/waapi/core/useAnimationOrchestrator.ts +429 -0
  143. package/src/react-ui/primitives/waapi/core/useElementRegistry.ts +80 -0
  144. package/src/react-ui/primitives/waapi/core/useFLIPAnimation.ts +137 -0
  145. package/src/react-ui/primitives/waapi/core/usePositionCapture.ts +105 -0
  146. package/src/react-ui/primitives/waapi/index.ts +116 -0
  147. package/src/react-ui/styles/animations.css +369 -0
  148. package/src/react-ui/ui/DataCard/DataCard.styles.ts +150 -0
  149. package/src/react-ui/ui/DataCard/DataCard.types.ts +146 -0
  150. package/src/react-ui/ui/DataCard/index.tsx +406 -0
  151. package/src/react-ui/ui/TextFlow/TextFlow.styles.ts +36 -0
  152. package/src/react-ui/ui/TextFlow/TextFlow.types.ts +118 -0
  153. package/src/react-ui/ui/TextFlow/index.tsx +276 -0
  154. package/src/react-ui/ui/index.ts +4 -1
  155. /package/dist/react-ui/components/MorphingPopover/{morphing-popover.module-CgbYV_HS.css → morphing-popover.module-BycNI8nU.css} +0 -0
@@ -0,0 +1,187 @@
1
+ "use client";
2
+
3
+ import { cn } from "../../lib/utils.js";
4
+ import { SlidingText } from "../../primitives/waapi/SlidingText/index.js";
5
+ import { ReorderRoot } from "../../primitives/waapi/Reorder/index.js";
6
+ import { useListFormat } from "../../hooks/Formatting/UseListFormat.js";
7
+ import React, { useCallback, useMemo } from "react";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+
10
+ //#region src/react-ui/ui/TextFlow/index.tsx
11
+ const DEFAULT_SEPARATOR = {
12
+ listType: "conjunction",
13
+ listStyle: "long",
14
+ transition: "none",
15
+ duration: 200
16
+ };
17
+ const DEFAULT_OVERFLOW = {
18
+ prefix: "+",
19
+ label: " more",
20
+ showSeparator: true
21
+ };
22
+ const DEFAULT_ANIMATION = {
23
+ mode: "character",
24
+ direction: "vertical",
25
+ stagger: 15,
26
+ duration: 200,
27
+ blur: true,
28
+ widthAnimation: false
29
+ };
30
+ /**
31
+ * TextFlow - Locale-aware animated token list component
32
+ *
33
+ * Built on the Reorder primitive, provides smooth FLIP animations for token lists
34
+ * with proper locale-aware separators using Intl.ListFormat.
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * // Basic usage
39
+ * <TextFlow tokens={tokens} />
40
+ *
41
+ * // Spanish multiselect with transition
42
+ * <TextFlow
43
+ * tokens={tokens}
44
+ * maxVisible={2}
45
+ * inline
46
+ * separator={{ locale: 'es', transition: 'fade' }}
47
+ * overflow={{ prefix: '+', label: ' más' }}
48
+ * />
49
+ * ```
50
+ */
51
+ const TextFlow = ({ tokens, placeholder = "No tokens", maxVisible, separator: separatorConfig, overflow: overflowConfig, animation: animationConfig, inline = false, className = "", tokenClassName = "", placeholderClassName = "" }) => {
52
+ const separator = {
53
+ ...DEFAULT_SEPARATOR,
54
+ ...separatorConfig
55
+ };
56
+ const overflow = {
57
+ ...DEFAULT_OVERFLOW,
58
+ ...overflowConfig
59
+ };
60
+ const animation = {
61
+ ...DEFAULT_ANIMATION,
62
+ ...animationConfig
63
+ };
64
+ const exitingIdsRef = React.useRef(/* @__PURE__ */ new Set());
65
+ const visibleTokens = useMemo(() => maxVisible ? tokens.slice(0, maxVisible) : tokens, [tokens, maxVisible]);
66
+ const overflowCount = useMemo(() => maxVisible ? Math.max(0, tokens.length - maxVisible) : 0, [tokens.length, maxVisible]);
67
+ const listParts = useListFormat(visibleTokens.map((t) => t.text), {
68
+ locale: separator.locale,
69
+ type: separator.listType,
70
+ style: separator.listStyle,
71
+ separator: separator.value,
72
+ hasOverflow: overflowCount > 0
73
+ });
74
+ const shouldShowSeparator = useCallback((tokenIndex) => {
75
+ return !(tokenIndex >= visibleTokens.length - 1);
76
+ }, [visibleTokens.length]);
77
+ const handleItemExit = useCallback((id) => {
78
+ exitingIdsRef.current.add(id);
79
+ }, []);
80
+ const handleItemEnter = useCallback((id) => {}, []);
81
+ const showPlaceholder = tokens.length === 0 && !!placeholder;
82
+ const tokenElements = useMemo(() => {
83
+ let elementIndex = 0;
84
+ return listParts.map((part, partIndex) => {
85
+ if (part.type === "element") {
86
+ const token = visibleTokens[part.index];
87
+ if (!token) return null;
88
+ const isExiting = exitingIdsRef.current.has(token.id);
89
+ elementIndex++;
90
+ return /* @__PURE__ */ jsx("span", {
91
+ className: cn("waapi-token-wrapper", tokenClassName),
92
+ "data-reorder-id": token.id,
93
+ children: /* @__PURE__ */ jsx(SlidingText, {
94
+ text: token.text,
95
+ mode: isExiting ? "none" : animation.mode,
96
+ direction: animation.direction,
97
+ staggerDelay: animation.stagger,
98
+ duration: animation.duration,
99
+ blur: animation.blur,
100
+ widthAnimation: !isExiting && animation.widthAnimation,
101
+ initial: isExiting ? false : "initial",
102
+ animate: "animate"
103
+ })
104
+ }, token.id);
105
+ }
106
+ const prevToken = visibleTokens[part.index];
107
+ if (!prevToken) return null;
108
+ const tokenIndex = part.index;
109
+ if (!shouldShowSeparator(tokenIndex)) return null;
110
+ const separatorKey = `sep-${prevToken.id}`;
111
+ return /* @__PURE__ */ jsx("span", {
112
+ className: cn("waapi-token-separator", separator.className ?? ""),
113
+ children: part.value
114
+ }, separatorKey);
115
+ }).filter(Boolean);
116
+ }, [
117
+ listParts,
118
+ visibleTokens,
119
+ tokenClassName,
120
+ animation,
121
+ separator,
122
+ shouldShowSeparator
123
+ ]);
124
+ const overflowElement = useMemo(() => {
125
+ if (overflowCount === 0) return null;
126
+ return /* @__PURE__ */ jsxs("div", {
127
+ "data-reorder-id": "overflow-counter",
128
+ className: cn("waapi-token-overflow", overflow.className ?? ""),
129
+ children: [
130
+ overflow.showSeparator && overflow.separator && /* @__PURE__ */ jsx("span", {
131
+ className: cn("waapi-token-separator", separator.className ?? ""),
132
+ children: overflow.separator
133
+ }),
134
+ overflow.prefix !== "" && /* @__PURE__ */ jsx("span", {
135
+ className: cn("waapi-token-separator", separator.className ?? ""),
136
+ children: overflow.prefix
137
+ }),
138
+ /* @__PURE__ */ jsx(SlidingText, {
139
+ text: String(overflowCount),
140
+ mode: "character",
141
+ duration: animation.duration,
142
+ blur: animation.blur,
143
+ initial: "initial",
144
+ animate: "animate"
145
+ }),
146
+ overflow.label && overflow.label !== "" && /* @__PURE__ */ jsx(SlidingText, {
147
+ text: overflow.label,
148
+ mode: animation.mode,
149
+ direction: animation.direction,
150
+ staggerDelay: animation.stagger,
151
+ duration: animation.duration,
152
+ blur: animation.blur,
153
+ initial: "initial",
154
+ animate: "animate"
155
+ })
156
+ ]
157
+ }, "overflow-counter");
158
+ }, [
159
+ overflowCount,
160
+ overflow,
161
+ separator,
162
+ animation
163
+ ]);
164
+ return /* @__PURE__ */ jsxs("div", {
165
+ role: "text",
166
+ className: cn("waapi-animated-tokens-container", inline && "waapi-animated-tokens-container--inline", className),
167
+ children: [showPlaceholder && /* @__PURE__ */ jsx(SlidingText, {
168
+ text: placeholder,
169
+ mode: "word",
170
+ direction: "vertical",
171
+ blur: true,
172
+ duration: 150,
173
+ initial: "initial",
174
+ animate: "animate",
175
+ className: cn("waapi-token-placeholder", placeholderClassName)
176
+ }, "placeholder"), /* @__PURE__ */ jsxs(ReorderRoot, {
177
+ layout: inline ? "inline-horizontal" : "horizontal",
178
+ onItemExit: handleItemExit,
179
+ onItemEnter: handleItemEnter,
180
+ children: [tokenElements, overflowElement]
181
+ })]
182
+ });
183
+ };
184
+ TextFlow.displayName = "TextFlow";
185
+
186
+ //#endregion
187
+ export { TextFlow };
@@ -27,5 +27,6 @@ export * from './InputGroup';
27
27
  export * from './Label';
28
28
  export * from './Select';
29
29
  export * from './Separator';
30
- export * from './Textarea';
30
+ export * from './DataCard';
31
+ export * from './TextFlow';
31
32
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react-ui/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAG1B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react-ui/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAG1B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAG3B,cAAc,YAAY,CAAC"}
@@ -23,7 +23,6 @@ import { Button } from "./Button/index.js";
23
23
  import { cardVariants } from "./Card/Card.styles.js";
24
24
  import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./Card/index.js";
25
25
  import { Input } from "./Input/index.js";
26
- import { Textarea } from "./Textarea/index.js";
27
26
  import { inputGroupAddonVariants, inputGroupButtonVariants } from "./InputGroup/InputGroup.styles.js";
28
27
  import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea } from "./InputGroup/index.js";
29
28
  import { Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxLabel, ComboboxList, ComboboxSeparator, ComboboxTrigger, ComboboxValue, useComboboxAnchor } from "./Combobox/index.js";
@@ -35,3 +34,6 @@ import { Separator } from "./Separator/index.js";
35
34
  import { fieldVariants } from "./Field/Field.styles.js";
36
35
  import { Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle } from "./Field/index.js";
37
36
  import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue } from "./Select/index.js";
37
+ import { dataCardStyles, dataCardVariants } from "./DataCard/DataCard.styles.js";
38
+ import { DataCard, DataCardActions, DataCardBracket, DataCardLabel, DataCardToggle, DataCardValue, useDataCard } from "./DataCard/index.js";
39
+ import { TextFlow } from "./TextFlow/index.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mks2508/mks-ui",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "UI component library - Shadcn/Animate UI based with DevEnv components",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -20,7 +20,11 @@
20
20
  "./react/ui/*": "./src/react-ui/ui/*/index.tsx",
21
21
  "./react/primitives/*": "./src/react-ui/primitives/*/index.tsx",
22
22
  "./react/components/*": "./src/react-ui/components/*/index.tsx",
23
- "./react/hooks/*": "./src/react-ui/hooks/*.tsx",
23
+ "./react/hooks/*": "./src/react-ui/hooks/*/*.ts",
24
+ "./react/hooks/Animation/*": "./src/react-ui/hooks/Animation/*.tsx",
25
+ "./react/hooks/DOM/*": "./src/react-ui/hooks/DOM/*.tsx",
26
+ "./react/hooks/State/*": "./src/react-ui/hooks/State/*.tsx",
27
+ "./react/hooks/Formatting/*": "./src/react-ui/hooks/Formatting/*.ts",
24
28
  "./react/icons/*": "./src/react-ui/icons/*.tsx",
25
29
  "./react/lib/*": "./src/react-ui/lib/*.ts"
26
30
  },
@@ -0,0 +1,134 @@
1
+ import { useMemo } from 'react';
2
+
3
+ /** Intl.ListFormat conjunction/disjunction/unit type */
4
+ export type ListFormatType = 'conjunction' | 'disjunction' | 'unit';
5
+
6
+ /** Intl.ListFormat style: long, short, or narrow */
7
+ export type ListFormatStyle = 'long' | 'short' | 'narrow';
8
+
9
+ /** Options for useListFormat hook */
10
+ export interface IUseListFormatOptions {
11
+ locale?: string;
12
+ type?: ListFormatType;
13
+ style?: ListFormatStyle;
14
+ separator?: string;
15
+ /** If true, format as if there's one more item (avoids "and" before overflow counter) */
16
+ hasOverflow?: boolean;
17
+ }
18
+
19
+ /** A single part of a formatted list (element or separator) */
20
+ export interface IListPart {
21
+ type: 'element' | 'literal';
22
+ value: string;
23
+ index: number;
24
+ }
25
+
26
+ const getDefaultLocale = (): string => {
27
+ if (typeof navigator !== 'undefined' && navigator.language) {
28
+ return navigator.language;
29
+ }
30
+ return 'en';
31
+ };
32
+
33
+ /**
34
+ * Hook for locale-aware list formatting using Intl.ListFormat.
35
+ *
36
+ * Provides parts that can be rendered individually with animations.
37
+ * Uses the browser's Intl.ListFormat API for proper locale handling.
38
+ *
39
+ * @param items - Array of strings to format
40
+ * @param options - Locale, type, style, separator override, overflow flag
41
+ * @returns Array of list parts (elements and literals)
42
+ *
43
+ * @example
44
+ * ```tsx
45
+ * // Automatic locale detection
46
+ * const parts = useListFormat(['Apple', 'Banana', 'Cherry']);
47
+ * // en: [{ type: 'element', value: 'Apple' }, { type: 'literal', value: ', ' }, ...]
48
+ *
49
+ * // Spanish
50
+ * const parts = useListFormat(['Apple', 'Banana'], { locale: 'es' });
51
+ *
52
+ * // Disjunction ("or")
53
+ * const parts = useListFormat(['A', 'B', 'C'], { type: 'disjunction' });
54
+ * // en: "A, B, or C"
55
+ *
56
+ * // Manual separator override
57
+ * const parts = useListFormat(['A', 'B', 'C'], { separator: ' | ' });
58
+ * // "A | B | C"
59
+ * ```
60
+ */
61
+ export function useListFormat(
62
+ items: string[],
63
+ options: IUseListFormatOptions = {}
64
+ ): IListPart[] {
65
+ const {
66
+ locale,
67
+ type = 'conjunction',
68
+ style = 'long',
69
+ separator,
70
+ hasOverflow
71
+ } = options;
72
+
73
+ return useMemo(() => {
74
+ if (items.length === 0) return [];
75
+
76
+ // Manual override mode: use fixed separator
77
+ if (separator !== undefined) {
78
+ const parts: IListPart[] = [];
79
+ items.forEach((item, i) => {
80
+ parts.push({ type: 'element', value: item, index: i });
81
+ if (i < items.length - 1) {
82
+ parts.push({ type: 'literal', value: separator, index: i });
83
+ }
84
+ });
85
+ return parts;
86
+ }
87
+
88
+ // Use Intl.ListFormat for locale-aware formatting
89
+ const resolvedLocale = locale ?? getDefaultLocale();
90
+
91
+ try {
92
+ const formatter = new Intl.ListFormat(resolvedLocale, { type, style });
93
+
94
+ // If hasOverflow, add a placeholder to make Intl.ListFormat use commas
95
+ // instead of "and" before the overflow counter
96
+ const itemsToFormat = (hasOverflow && items.length > 1)
97
+ ? [...items, '']
98
+ : items;
99
+
100
+ const formatted = formatter.formatToParts(itemsToFormat);
101
+
102
+ // If hasOverflow, remove the last part (placeholder and its separator)
103
+ const partsToProcess = hasOverflow
104
+ ? formatted.slice(0, -2)
105
+ : formatted;
106
+
107
+ let elementIndex = 0;
108
+ return partsToProcess.map((part): IListPart => {
109
+ if (part.type === 'element') {
110
+ return {
111
+ type: 'element',
112
+ value: part.value,
113
+ index: elementIndex++
114
+ };
115
+ }
116
+ return {
117
+ type: 'literal',
118
+ value: part.value,
119
+ index: Math.max(0, elementIndex - 1)
120
+ };
121
+ });
122
+ } catch {
123
+ // Fallback if Intl.ListFormat fails
124
+ const parts: IListPart[] = [];
125
+ items.forEach((item, i) => {
126
+ parts.push({ type: 'element', value: item, index: i });
127
+ if (i < items.length - 1) {
128
+ parts.push({ type: 'literal', value: ', ', index: i });
129
+ }
130
+ });
131
+ return parts;
132
+ }
133
+ }, [items, locale, type, style, separator, hasOverflow]);
134
+ }
@@ -15,3 +15,6 @@ export type { DataStateValue } from './State/UseDataState';
15
15
 
16
16
  export { useIsInView } from './DOM/UseIsInView';
17
17
  export type { IUseIsInViewOptions } from './DOM/UseIsInView';
18
+
19
+ export { useListFormat } from './Formatting/UseListFormat';
20
+ export type { IUseListFormatOptions, IListPart, ListFormatType, ListFormatStyle } from './Formatting/UseListFormat';
@@ -11,3 +11,6 @@ export * from './Slot';
11
11
  export * from './Highlight';
12
12
  export * from './AutoHeight';
13
13
  export * from './CountingNumber';
14
+
15
+ // WAAPI Animation Primitives
16
+ export * from './waapi';
@@ -0,0 +1,106 @@
1
+ import type { ReactNode, RefObject } from 'react';
2
+
3
+ /**
4
+ * Available morphing techniques.
5
+ */
6
+ export type MorphTechnique = 'flip-clip-path' | 'css-grid' | 'view-transitions';
7
+
8
+ // =============================================================================
9
+ // FLIP + clip-path
10
+ // =============================================================================
11
+
12
+ /** Options for the FLIP + clip-path morphing technique */
13
+ export interface IFLIPClipPathOptions {
14
+ duration?: number;
15
+ easing?: string;
16
+ clipPathStart?: string;
17
+ clipPathEnd?: string;
18
+ }
19
+
20
+ /** API returned by useFLIPClipPath */
21
+ export interface IFLIPClipPathAPI {
22
+ isMorphing: boolean;
23
+ morph: (fromElement: HTMLElement, toElement: HTMLElement) => Promise<void>;
24
+ cancel: () => void;
25
+ }
26
+
27
+ // =============================================================================
28
+ // CSS Grid
29
+ // =============================================================================
30
+
31
+ /** Options for the CSS Grid expand/collapse technique */
32
+ export interface ICSSGridMorphOptions {
33
+ duration?: number;
34
+ easing?: string;
35
+ }
36
+
37
+ /** API returned by useCSSGridMorph */
38
+ export interface ICSSGridMorphAPI {
39
+ isExpanded: boolean;
40
+ expand: () => void;
41
+ collapse: () => void;
42
+ toggle: () => void;
43
+ containerRef: RefObject<HTMLElement | null>;
44
+ }
45
+
46
+ // =============================================================================
47
+ // View Transitions
48
+ // =============================================================================
49
+
50
+ /** Options for the View Transitions API wrapper */
51
+ export interface IViewTransitionsOptions {
52
+ name?: string;
53
+ types?: string[];
54
+ }
55
+
56
+ /** API returned by useViewTransitions */
57
+ export interface IViewTransitionsAPI {
58
+ isSupported: boolean;
59
+ startTransition: (callback: () => void | Promise<void>) => Promise<void>;
60
+ setTypes: (types: string[]) => void;
61
+ }
62
+
63
+ // =============================================================================
64
+ // Unified Morph
65
+ // =============================================================================
66
+
67
+ /** Configuration for useMorph hook */
68
+ export interface IUseMorphConfig {
69
+ technique?: MorphTechnique;
70
+ duration?: number;
71
+ easing?: string;
72
+ onMorphStart?: () => void;
73
+ onMorphEnd?: () => void;
74
+ }
75
+
76
+ /** Return type for useMorph hook */
77
+ export interface IUseMorphReturn {
78
+ isMorphing: boolean;
79
+ technique: MorphTechnique;
80
+ isViewTransitionsSupported: boolean;
81
+ morph: (fromElement: HTMLElement, toElement: HTMLElement) => Promise<void>;
82
+ cancel: () => void;
83
+ flipClipPath: IFLIPClipPathAPI;
84
+ cssGrid: ICSSGridMorphAPI;
85
+ viewTransitions: IViewTransitionsAPI;
86
+ }
87
+
88
+ // =============================================================================
89
+ // Component Props
90
+ // =============================================================================
91
+
92
+ /** Props for Morph container component */
93
+ export interface IMorphProps {
94
+ children: ReactNode;
95
+ technique?: MorphTechnique;
96
+ duration?: number;
97
+ easing?: string;
98
+ className?: string;
99
+ onMorphStart?: () => void;
100
+ onMorphEnd?: () => void;
101
+ }
102
+
103
+ /** Context value for Morph components */
104
+ export interface IMorphContextValue {
105
+ morph: IUseMorphReturn;
106
+ }
@@ -0,0 +1,21 @@
1
+ "use client";
2
+
3
+ import React, { createContext, useContext } from 'react';
4
+ import type { IMorphContextValue } from './Morph.types';
5
+
6
+ const MorphContext = createContext<IMorphContextValue | null>(null);
7
+
8
+ /**
9
+ * Hook to access morph context
10
+ *
11
+ * @throws Error if used outside of Morph provider
12
+ */
13
+ export function useMorphContext(): IMorphContextValue {
14
+ const context = useContext(MorphContext);
15
+ if (!context) {
16
+ throw new Error('useMorphContext must be used within a Morph component');
17
+ }
18
+ return context;
19
+ }
20
+
21
+ export { MorphContext };
@@ -0,0 +1,56 @@
1
+ "use client";
2
+
3
+ import React, { useMemo, type ReactNode } from 'react';
4
+ import { MorphContext } from './MorphContext';
5
+ import { useMorph } from './useMorph';
6
+ import type { IMorphProps, IMorphContextValue } from './Morph.types';
7
+ import { cn } from '@/react-ui/lib/utils';
8
+
9
+ /**
10
+ * Morph - Container component for morphable elements.
11
+ *
12
+ * Provides morphing capabilities to child components through context.
13
+ * Supports FLIP + clip-path, CSS Grid, and View Transitions techniques.
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * <Morph technique="flip-clip-path" duration={300}>
18
+ * <button onClick={() => morph(fromRef.current, toRef.current)}>
19
+ * Morph
20
+ * </button>
21
+ * </Morph>
22
+ * ```
23
+ */
24
+ export function Morph({
25
+ children,
26
+ technique = 'flip-clip-path',
27
+ duration,
28
+ easing,
29
+ className = '',
30
+ onMorphStart,
31
+ onMorphEnd
32
+ }: IMorphProps): ReactNode {
33
+ const morph = useMorph({
34
+ technique,
35
+ duration,
36
+ easing,
37
+ onMorphStart,
38
+ onMorphEnd
39
+ });
40
+
41
+ const contextValue = useMemo<IMorphContextValue>(() => ({
42
+ morph
43
+ }), [morph]);
44
+
45
+ return (
46
+ <MorphContext.Provider value={contextValue}>
47
+ <div className={cn('morph-container', className)}>
48
+ {children}
49
+ </div>
50
+ </MorphContext.Provider>
51
+ );
52
+ }
53
+
54
+ Morph.displayName = 'Morph';
55
+
56
+ export type { IMorphProps, IMorphContextValue };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Morph techniques.
3
+ *
4
+ * Individual morphing implementations that can be used
5
+ * standalone or composed through useMorph.
6
+ *
7
+ * @module primitives/waapi/Morph/techniques
8
+ */
9
+
10
+ export { useFLIPClipPath } from './useFLIPClipPath';
11
+ export { useCSSGridMorph } from './useCSSGridMorph';
12
+ export { useViewTransitions } from './useViewTransitions';
@@ -0,0 +1,88 @@
1
+ import { useRef, useCallback, useState } from 'react';
2
+ import { TIMING, EASINGS } from '../../core/animationConstants';
3
+ import type { ICSSGridMorphOptions, ICSSGridMorphAPI } from '../Morph.types';
4
+
5
+ const DEFAULT_DURATION = TIMING.FLIP_DURATION;
6
+ const DEFAULT_EASING = EASINGS.MATERIAL_STANDARD;
7
+
8
+ /**
9
+ * Hook for CSS Grid-based expand/collapse animations.
10
+ *
11
+ * Uses the `grid-template-rows: 0fr/1fr` technique for smooth height
12
+ * animations without needing to know the content height in advance.
13
+ *
14
+ * CSS required on container:
15
+ * ```css
16
+ * .morph-container {
17
+ * display: grid;
18
+ * grid-template-rows: 0fr;
19
+ * transition: grid-template-rows 300ms ease;
20
+ * }
21
+ * .morph-container.expanded {
22
+ * grid-template-rows: 1fr;
23
+ * }
24
+ * .morph-content {
25
+ * overflow: hidden;
26
+ * }
27
+ * ```
28
+ *
29
+ * @param options - Duration and easing overrides
30
+ * @returns Grid morph API with expand, collapse, toggle, and container ref
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * const { isExpanded, toggle, containerRef } = useCSSGridMorph();
35
+ *
36
+ * return (
37
+ * <div ref={containerRef} className={isExpanded ? 'expanded' : ''}>
38
+ * <div className="morph-content">Collapsible content</div>
39
+ * </div>
40
+ * );
41
+ * ```
42
+ */
43
+ export function useCSSGridMorph(options?: ICSSGridMorphOptions): ICSSGridMorphAPI {
44
+ const [isExpanded, setIsExpanded] = useState(false);
45
+ const containerRef = useRef<HTMLElement | null>(null);
46
+ const optionsRef = useRef(options);
47
+
48
+ const duration = optionsRef.current?.duration ?? DEFAULT_DURATION;
49
+ const easing = optionsRef.current?.easing ?? DEFAULT_EASING;
50
+
51
+ const applyTransition = useCallback(() => {
52
+ if (containerRef.current) {
53
+ containerRef.current.style.transition = `grid-template-rows ${duration}ms ${easing}`;
54
+ }
55
+ }, [duration, easing]);
56
+
57
+ const expand = useCallback(() => {
58
+ applyTransition();
59
+ if (containerRef.current) {
60
+ containerRef.current.style.gridTemplateRows = '1fr';
61
+ }
62
+ setIsExpanded(true);
63
+ }, [applyTransition]);
64
+
65
+ const collapse = useCallback(() => {
66
+ applyTransition();
67
+ if (containerRef.current) {
68
+ containerRef.current.style.gridTemplateRows = '0fr';
69
+ }
70
+ setIsExpanded(false);
71
+ }, [applyTransition]);
72
+
73
+ const toggle = useCallback(() => {
74
+ if (isExpanded) {
75
+ collapse();
76
+ } else {
77
+ expand();
78
+ }
79
+ }, [isExpanded, expand, collapse]);
80
+
81
+ return {
82
+ isExpanded,
83
+ expand,
84
+ collapse,
85
+ toggle,
86
+ containerRef
87
+ };
88
+ }