@ttdk-design-system/library-react 0.0.0-stage → 1.0.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 (184) hide show
  1. package/AI_GUIDE.md +6518 -0
  2. package/llms.txt +6518 -0
  3. package/package.json +1098 -4
  4. package/src/core/index.ts +2 -0
  5. package/src/core/tailwind-preset.cjs +62 -0
  6. package/src/core/tailwind-preset.ts +85 -0
  7. package/src/core/theme/tokens.css +663 -0
  8. package/src/core/types.ts +836 -0
  9. package/src/index.ts +17 -0
  10. package/src/tailwind-preset.cjs +62 -0
  11. package/src/tailwind-preset.ts +85 -0
  12. package/src/types.ts +1 -0
  13. package/src/web/accordion/AI_GUIDE.md +149 -0
  14. package/src/web/accordion/accordion.css +1 -0
  15. package/src/web/accordion/accordion.tsx +430 -0
  16. package/src/web/accordion/index.ts +3 -0
  17. package/src/web/accordion/manifest.json +351 -0
  18. package/src/web/alert/AI_GUIDE.md +139 -0
  19. package/src/web/alert/alert.css +1 -0
  20. package/src/web/alert/alert.tsx +318 -0
  21. package/src/web/alert/index.ts +3 -0
  22. package/src/web/alert/manifest.json +240 -0
  23. package/src/web/avatar/AI_GUIDE.md +118 -0
  24. package/src/web/avatar/avatar.css +1 -0
  25. package/src/web/avatar/avatar.tsx +233 -0
  26. package/src/web/avatar/index.ts +3 -0
  27. package/src/web/avatar/manifest.json +195 -0
  28. package/src/web/badge/AI_GUIDE.md +94 -0
  29. package/src/web/badge/badge.css +1 -0
  30. package/src/web/badge/badge.tsx +140 -0
  31. package/src/web/badge/index.ts +2 -0
  32. package/src/web/badge/manifest.json +141 -0
  33. package/src/web/banner/AI_GUIDE.md +130 -0
  34. package/src/web/banner/banner.css +1 -0
  35. package/src/web/banner/banner.tsx +334 -0
  36. package/src/web/banner/index.ts +3 -0
  37. package/src/web/banner/manifest.json +240 -0
  38. package/src/web/breadcrumb/AI_GUIDE.md +120 -0
  39. package/src/web/breadcrumb/breadcrumb.css +1 -0
  40. package/src/web/breadcrumb/breadcrumb.tsx +306 -0
  41. package/src/web/breadcrumb/index.ts +3 -0
  42. package/src/web/breadcrumb/manifest.json +225 -0
  43. package/src/web/button/AI_GUIDE.md +121 -0
  44. package/src/web/button/button.css +1 -0
  45. package/src/web/button/button.tsx +287 -0
  46. package/src/web/button/index.ts +2 -0
  47. package/src/web/button/manifest.json +177 -0
  48. package/src/web/calendar/AI_GUIDE.md +151 -0
  49. package/src/web/calendar/calendar.css +1 -0
  50. package/src/web/calendar/calendar.tsx +1611 -0
  51. package/src/web/calendar/index.ts +3 -0
  52. package/src/web/calendar/manifest.json +470 -0
  53. package/src/web/card/AI_GUIDE.md +133 -0
  54. package/src/web/card/card.css +1 -0
  55. package/src/web/card/card.tsx +408 -0
  56. package/src/web/card/index.ts +4 -0
  57. package/src/web/card/manifest.json +357 -0
  58. package/src/web/checkbox/AI_GUIDE.md +146 -0
  59. package/src/web/checkbox/checkbox.css +1 -0
  60. package/src/web/checkbox/checkbox.tsx +307 -0
  61. package/src/web/checkbox/index.ts +2 -0
  62. package/src/web/checkbox/manifest.json +294 -0
  63. package/src/web/chip/AI_GUIDE.md +186 -0
  64. package/src/web/chip/chip.css +1 -0
  65. package/src/web/chip/chip.tsx +230 -0
  66. package/src/web/chip/index.ts +2 -0
  67. package/src/web/chip/manifest.json +186 -0
  68. package/src/web/dropdown/AI_GUIDE.md +167 -0
  69. package/src/web/dropdown/dropdown.css +1 -0
  70. package/src/web/dropdown/dropdown.tsx +1274 -0
  71. package/src/web/dropdown/index.ts +3 -0
  72. package/src/web/dropdown/manifest.json +576 -0
  73. package/src/web/flex/AI_GUIDE.md +113 -0
  74. package/src/web/flex/flex-item.tsx +97 -0
  75. package/src/web/flex/flex.css +1 -0
  76. package/src/web/flex/flex.tsx +108 -0
  77. package/src/web/flex/index.ts +5 -0
  78. package/src/web/flex/manifest.json +162 -0
  79. package/src/web/form/index.ts +7 -0
  80. package/src/web/grid/AI_GUIDE.md +112 -0
  81. package/src/web/grid/grid-item.tsx +112 -0
  82. package/src/web/grid/grid.css +1 -0
  83. package/src/web/grid/grid.tsx +99 -0
  84. package/src/web/grid/index.ts +5 -0
  85. package/src/web/grid/manifest.json +153 -0
  86. package/src/web/index.ts +86 -0
  87. package/src/web/input/AI_GUIDE.md +159 -0
  88. package/src/web/input/index.ts +3 -0
  89. package/src/web/input/input.css +1 -0
  90. package/src/web/input/input.tsx +537 -0
  91. package/src/web/input/manifest.json +465 -0
  92. package/src/web/input-number/AI_GUIDE.md +164 -0
  93. package/src/web/input-number/index.ts +3 -0
  94. package/src/web/input-number/input-number.css +1 -0
  95. package/src/web/input-number/input-number.tsx +1193 -0
  96. package/src/web/input-number/manifest.json +575 -0
  97. package/src/web/modal/AI_GUIDE.md +171 -0
  98. package/src/web/modal/index.ts +3 -0
  99. package/src/web/modal/manifest.json +375 -0
  100. package/src/web/modal/modal.css +1 -0
  101. package/src/web/modal/modal.tsx +571 -0
  102. package/src/web/pagination/AI_GUIDE.md +148 -0
  103. package/src/web/pagination/index.ts +21 -0
  104. package/src/web/pagination/manifest.json +432 -0
  105. package/src/web/pagination/pagination-content.tsx +45 -0
  106. package/src/web/pagination/pagination-ellipsis.tsx +54 -0
  107. package/src/web/pagination/pagination-first.tsx +155 -0
  108. package/src/web/pagination/pagination-item.tsx +45 -0
  109. package/src/web/pagination/pagination-last.tsx +155 -0
  110. package/src/web/pagination/pagination-link.tsx +121 -0
  111. package/src/web/pagination/pagination-next.tsx +158 -0
  112. package/src/web/pagination/pagination-previous.tsx +158 -0
  113. package/src/web/pagination/pagination.css +1 -0
  114. package/src/web/pagination/pagination.tsx +687 -0
  115. package/src/web/popover/AI_GUIDE.md +163 -0
  116. package/src/web/popover/index.ts +3 -0
  117. package/src/web/popover/manifest.json +396 -0
  118. package/src/web/popover/popover.css +1 -0
  119. package/src/web/popover/popover.tsx +702 -0
  120. package/src/web/radio/AI_GUIDE.md +155 -0
  121. package/src/web/radio/index.ts +2 -0
  122. package/src/web/radio/manifest.json +294 -0
  123. package/src/web/radio/radio.css +1 -0
  124. package/src/web/radio/radio.tsx +290 -0
  125. package/src/web/selectbox/AI_GUIDE.md +216 -0
  126. package/src/web/selectbox/index.ts +3 -0
  127. package/src/web/selectbox/manifest.json +477 -0
  128. package/src/web/selectbox/selectbox.css +1 -0
  129. package/src/web/selectbox/selectbox.tsx +1048 -0
  130. package/src/web/sonner/AI_GUIDE.md +98 -0
  131. package/src/web/sonner/index.ts +114 -0
  132. package/src/web/sonner/manifest.json +132 -0
  133. package/src/web/sonner/sonner.css +1 -0
  134. package/src/web/sonner/sonner.tsx +177 -0
  135. package/src/web/table/AI_GUIDE.md +145 -0
  136. package/src/web/table/index.ts +35 -0
  137. package/src/web/table/manifest.json +168 -0
  138. package/src/web/table/table-body.tsx +45 -0
  139. package/src/web/table/table-caption.tsx +45 -0
  140. package/src/web/table/table-cell.tsx +74 -0
  141. package/src/web/table/table-empty.tsx +108 -0
  142. package/src/web/table/table-footer.tsx +45 -0
  143. package/src/web/table/table-head.tsx +80 -0
  144. package/src/web/table/table-header.tsx +45 -0
  145. package/src/web/table/table-row.tsx +69 -0
  146. package/src/web/table/table.css +1 -0
  147. package/src/web/table/table.tsx +305 -0
  148. package/src/web/tabs/AI_GUIDE.md +150 -0
  149. package/src/web/tabs/index.ts +3 -0
  150. package/src/web/tabs/manifest.json +315 -0
  151. package/src/web/tabs/tabs.css +1 -0
  152. package/src/web/tabs/tabs.tsx +332 -0
  153. package/src/web/text/AI_GUIDE.md +129 -0
  154. package/src/web/text/index.ts +3 -0
  155. package/src/web/text/manifest.json +195 -0
  156. package/src/web/text/text.css +1 -0
  157. package/src/web/text/text.tsx +689 -0
  158. package/src/web/textarea/AI_GUIDE.md +141 -0
  159. package/src/web/textarea/index.ts +3 -0
  160. package/src/web/textarea/manifest.json +438 -0
  161. package/src/web/textarea/textarea.css +1 -0
  162. package/src/web/textarea/textarea.tsx +467 -0
  163. package/src/web/tooltip/AI_GUIDE.md +110 -0
  164. package/src/web/tooltip/index.ts +3 -0
  165. package/src/web/tooltip/manifest.json +222 -0
  166. package/src/web/tooltip/tooltip.css +1 -0
  167. package/src/web/tooltip/tooltip.tsx +423 -0
  168. package/src/webview/bottomsheet/AI_GUIDE.md +141 -0
  169. package/src/webview/bottomsheet/bottomsheet.css +1 -0
  170. package/src/webview/bottomsheet/bottomsheet.tsx +603 -0
  171. package/src/webview/bottomsheet/index.ts +4 -0
  172. package/src/webview/bottomsheet/manifest.json +377 -0
  173. package/src/webview/button/AI_GUIDE.md +79 -0
  174. package/src/webview/button/button-ios.css +1 -0
  175. package/src/webview/button/button-ios.tsx +138 -0
  176. package/src/webview/button/button-material.css +1 -0
  177. package/src/webview/button/button-material.tsx +138 -0
  178. package/src/webview/button/index.ts +7 -0
  179. package/src/webview/button/manifest.json +148 -0
  180. package/src/webview/index.ts +7 -0
  181. package/tailwind-preset.cjs +62 -0
  182. package/tailwind-preset.js +62 -0
  183. package/ttdk-manifest.json +10188 -0
  184. package/README.md +0 -3
@@ -0,0 +1,222 @@
1
+ {
2
+ "name": "@ttdk-design-system/tooltip",
3
+ "component": "Tooltip",
4
+ "folder": "tooltip",
5
+ "description": "Komponen Tooltip untuk menampilkan informasi kontekstual singkat saat pengguna mengarahkan kursor atau memfokuskan elemen pemicu.",
6
+ "stylesheets": [
7
+ {
8
+ "theme": "common",
9
+ "file": "tooltip.css",
10
+ "importPath": "web/tooltip/tooltip.css"
11
+ }
12
+ ],
13
+ "props": {
14
+ "text": {
15
+ "name": "text",
16
+ "type": "string",
17
+ "optional": true,
18
+ "default": null,
19
+ "description": "Teks konten tooltip yang ditampilkan.",
20
+ "example": null,
21
+ "param": null
22
+ },
23
+ "content": {
24
+ "name": "content",
25
+ "type": "any",
26
+ "optional": true,
27
+ "default": null,
28
+ "description": "Konten kustom tooltip (JSX / VNode atau renderable content).",
29
+ "example": null,
30
+ "param": null
31
+ },
32
+ "kbd": {
33
+ "name": "kbd",
34
+ "type": "string | string[]",
35
+ "optional": true,
36
+ "default": null,
37
+ "description": "Pintasan keyboard opsional yang ditampilkan di dalam tooltip (misal: '⌘K', ['Ctrl', 'S']).",
38
+ "example": null,
39
+ "param": null
40
+ },
41
+ "placement": {
42
+ "name": "placement",
43
+ "type": "TooltipPlacement",
44
+ "optional": true,
45
+ "default": "top",
46
+ "description": "Posisi penempatan tooltip terhadap elemen pemicu.",
47
+ "example": null,
48
+ "param": null
49
+ },
50
+ "offsetDistance": {
51
+ "name": "offsetDistance",
52
+ "type": "number",
53
+ "optional": true,
54
+ "default": "8",
55
+ "description": "Jarak offset dalam pixel dari elemen pemicu (trigger).",
56
+ "example": null,
57
+ "param": null
58
+ },
59
+ "arrow": {
60
+ "name": "arrow",
61
+ "type": "boolean",
62
+ "optional": true,
63
+ "default": "true",
64
+ "description": "Menampilkan elemen panah segitiga penunjuk arah ke elemen pemicu.",
65
+ "example": null,
66
+ "param": null
67
+ },
68
+ "openDelay": {
69
+ "name": "openDelay",
70
+ "type": "number",
71
+ "optional": true,
72
+ "default": "150",
73
+ "description": "Penundaan kemunculan tooltip dalam milidetik (delay saat pointer enter).",
74
+ "example": null,
75
+ "param": null
76
+ },
77
+ "closeDelay": {
78
+ "name": "closeDelay",
79
+ "type": "number",
80
+ "optional": true,
81
+ "default": "100",
82
+ "description": "Penundaan hilangnya tooltip dalam milidetik (delay saat pointer leave).",
83
+ "example": null,
84
+ "param": null
85
+ },
86
+ "open": {
87
+ "name": "open",
88
+ "type": "boolean",
89
+ "optional": true,
90
+ "default": null,
91
+ "description": "Status visibilitas tooltip terkontrol (controlled mode).",
92
+ "example": null,
93
+ "param": null
94
+ },
95
+ "defaultOpen": {
96
+ "name": "defaultOpen",
97
+ "type": "boolean",
98
+ "optional": true,
99
+ "default": "false",
100
+ "description": "Status visibilitas awal (uncontrolled initial state).",
101
+ "example": null,
102
+ "param": null
103
+ },
104
+ "disabled": {
105
+ "name": "disabled",
106
+ "type": "boolean",
107
+ "optional": true,
108
+ "default": "false",
109
+ "description": "Menonaktifkan tooltip sehingga tidak muncul saat di-hover atau difokus.",
110
+ "example": null,
111
+ "param": null
112
+ },
113
+ "ui": {
114
+ "name": "ui",
115
+ "type": "TooltipUiProps",
116
+ "optional": true,
117
+ "default": null,
118
+ "description": "Kustomisasi visual granular container utama (UiProps).",
119
+ "example": null,
120
+ "param": null
121
+ },
122
+ "style": {
123
+ "name": "style",
124
+ "type": "any",
125
+ "optional": true,
126
+ "default": null,
127
+ "description": "Style inline CSS native untuk wrapper luar.",
128
+ "example": null,
129
+ "param": null
130
+ },
131
+ "className": {
132
+ "name": "className",
133
+ "type": "string",
134
+ "optional": true,
135
+ "default": null,
136
+ "description": "Class CSS kustom untuk wrapper luar.",
137
+ "example": null,
138
+ "param": null
139
+ },
140
+ "triggerClass": {
141
+ "name": "triggerClass",
142
+ "type": "string",
143
+ "optional": true,
144
+ "default": null,
145
+ "description": "Class CSS kustom untuk pemicu (trigger container).",
146
+ "example": null,
147
+ "param": null
148
+ },
149
+ "triggerStyle": {
150
+ "name": "triggerStyle",
151
+ "type": "UiProps",
152
+ "optional": true,
153
+ "default": null,
154
+ "description": "Style kustom untuk pemicu (trigger container) (UiProps).",
155
+ "example": null,
156
+ "param": null
157
+ },
158
+ "contentClass": {
159
+ "name": "contentClass",
160
+ "type": "string",
161
+ "optional": true,
162
+ "default": null,
163
+ "description": "Class CSS kustom untuk panel tooltip konten.",
164
+ "example": null,
165
+ "param": null
166
+ },
167
+ "contentStyle": {
168
+ "name": "contentStyle",
169
+ "type": "UiProps",
170
+ "optional": true,
171
+ "default": null,
172
+ "description": "Style kustom untuk panel tooltip konten (UiProps).",
173
+ "example": null,
174
+ "param": null
175
+ },
176
+ "arrowClass": {
177
+ "name": "arrowClass",
178
+ "type": "string",
179
+ "optional": true,
180
+ "default": null,
181
+ "description": "Class CSS kustom untuk elemen panah (arrow).",
182
+ "example": null,
183
+ "param": null
184
+ },
185
+ "arrowStyle": {
186
+ "name": "arrowStyle",
187
+ "type": "UiProps",
188
+ "optional": true,
189
+ "default": null,
190
+ "description": "Style kustom untuk elemen panah (arrow) (UiProps).",
191
+ "example": null,
192
+ "param": null
193
+ },
194
+ "onOpenChange": {
195
+ "name": "onOpenChange",
196
+ "type": "(open: boolean) => void",
197
+ "optional": true,
198
+ "default": null,
199
+ "description": "Callback saat status open berubah.",
200
+ "example": null,
201
+ "param": null
202
+ },
203
+ "children": {
204
+ "name": "children",
205
+ "type": "any",
206
+ "optional": true,
207
+ "default": null,
208
+ "description": "Elemen pemicu (trigger) yang dibungkus oleh tooltip.",
209
+ "example": null,
210
+ "param": null
211
+ }
212
+ },
213
+ "subInterfaces": {},
214
+ "supportedFrameworks": [
215
+ "react",
216
+ "vue",
217
+ "svelte"
218
+ ],
219
+ "themes": [
220
+ "tw"
221
+ ]
222
+ }
@@ -0,0 +1 @@
1
+ .ttdk-tooltip-wrapper{box-sizing:border-box;font-family:var(--ttdk-font-sans);vertical-align:middle;display:inline-flex;position:relative}.ttdk-tooltip-trigger{box-sizing:border-box;color:inherit;cursor:pointer;background:0 0;border:none;outline:none;justify-content:center;align-items:center;margin:0;padding:0;font-family:inherit;display:inline-flex}.ttdk-tooltip-trigger[data-disabled=true]{cursor:not-allowed;opacity:.5;pointer-events:none}.ttdk-tooltip-content{z-index:1000;box-sizing:border-box;background:var(--ttdk-ui-bg,var(--ttdk-color-neutral-900,#1f2328));width:max-content;max-width:280px;color:var(--ttdk-ui-color,var(--ttdk-color-neutral-white,#fff));border-radius:var(--ttdk-ui-radius,6px);font-family:var(--ttdk-font-sans);text-align:center;pointer-events:auto;user-select:none;will-change:transform, opacity;align-items:center;padding:5px 9px;font-size:12px;font-weight:500;line-height:16px;animation:.14s cubic-bezier(.16,1,.3,1) forwards ttdk-tooltip-fade-in;display:inline-flex;position:absolute;top:0;left:0;overflow:visible;box-shadow:0 4px 14px #00000038}.ttdk-tooltip-body{z-index:1;word-break:break-word;overflow-wrap:break-word;flex-wrap:wrap;justify-content:center;align-items:center;gap:6px;display:inline-flex;position:relative}.ttdk-tooltip-text{word-break:break-word;overflow-wrap:break-word;display:inline}.ttdk-tooltip-kbd-list{align-items:center;gap:3px;display:inline-flex}.ttdk-tooltip-kbd{box-sizing:border-box;min-width:16px;height:16px;color:inherit;text-transform:uppercase;background:#fff3;border-radius:3px;justify-content:center;align-items:center;padding:0 4px;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:10px;font-weight:600;line-height:1;display:inline-flex}.ttdk-tooltip-arrow{background:inherit;z-index:0;pointer-events:none;box-sizing:border-box;width:8px;height:8px;position:absolute;transform:rotate(45deg)}@keyframes ttdk-tooltip-fade-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.dark .ttdk-tooltip-content,html.dark .ttdk-tooltip-content,[data-mode=dark] .ttdk-tooltip-content,[data-theme=dark] .ttdk-tooltip-content{background:var(--ttdk-ui-bg,var(--ttdk-color-neutral-800,#2c3035));color:var(--ttdk-ui-color,var(--ttdk-color-neutral-white,#f1f2f3));border:1px solid var(--ttdk-ui-border-color,var(--ttdk-stroke-subtle,#ffffff26));box-shadow:0 6px 18px #00000073}.dark .ttdk-tooltip-content[data-side=top] .ttdk-tooltip-arrow,html.dark .ttdk-tooltip-content[data-side=top] .ttdk-tooltip-arrow,[data-mode=dark] .ttdk-tooltip-content[data-side=top] .ttdk-tooltip-arrow,[data-theme=dark] .ttdk-tooltip-content[data-side=top] .ttdk-tooltip-arrow{border-right:1px solid var(--ttdk-ui-border-color,var(--ttdk-stroke-subtle,#ffffff26));border-bottom:1px solid var(--ttdk-ui-border-color,var(--ttdk-stroke-subtle,#ffffff26))}.dark .ttdk-tooltip-content[data-side=bottom] .ttdk-tooltip-arrow,html.dark .ttdk-tooltip-content[data-side=bottom] .ttdk-tooltip-arrow,[data-mode=dark] .ttdk-tooltip-content[data-side=bottom] .ttdk-tooltip-arrow,[data-theme=dark] .ttdk-tooltip-content[data-side=bottom] .ttdk-tooltip-arrow{border-left:1px solid var(--ttdk-ui-border-color,var(--ttdk-stroke-subtle,#ffffff26));border-top:1px solid var(--ttdk-ui-border-color,var(--ttdk-stroke-subtle,#ffffff26))}.dark .ttdk-tooltip-content[data-side=left] .ttdk-tooltip-arrow,html.dark .ttdk-tooltip-content[data-side=left] .ttdk-tooltip-arrow,[data-mode=dark] .ttdk-tooltip-content[data-side=left] .ttdk-tooltip-arrow,[data-theme=dark] .ttdk-tooltip-content[data-side=left] .ttdk-tooltip-arrow{border-top:1px solid var(--ttdk-ui-border-color,var(--ttdk-stroke-subtle,#ffffff26));border-right:1px solid var(--ttdk-ui-border-color,var(--ttdk-stroke-subtle,#ffffff26))}.dark .ttdk-tooltip-content[data-side=right] .ttdk-tooltip-arrow,html.dark .ttdk-tooltip-content[data-side=right] .ttdk-tooltip-arrow,[data-mode=dark] .ttdk-tooltip-content[data-side=right] .ttdk-tooltip-arrow,[data-theme=dark] .ttdk-tooltip-content[data-side=right] .ttdk-tooltip-arrow{border-left:1px solid var(--ttdk-ui-border-color,var(--ttdk-stroke-subtle,#ffffff26));border-bottom:1px solid var(--ttdk-ui-border-color,var(--ttdk-stroke-subtle,#ffffff26))}.dark .ttdk-tooltip-kbd,html.dark .ttdk-tooltip-kbd,[data-mode=dark] .ttdk-tooltip-kbd,[data-theme=dark] .ttdk-tooltip-kbd{background:#ffffff24}.ttdk-tooltip-content[style*="background: #fff"],.ttdk-tooltip-content[style*="background:#fff"],.ttdk-tooltip-content[style*="background: white"],.ttdk-tooltip-content[style*="background-color: #fff"],.ttdk-tooltip-content[style*="background-color:#fff"],.ttdk-tooltip-content[style*="background-color: white"],.ttdk-tooltip-content[style*="--ttdk-ui-background: #fff"],.ttdk-tooltip-content[style*="--ttdk-ui-background:#fff"],.ttdk-tooltip-content[style*="--ttdk-ui-background-color: #fff"],.ttdk-tooltip-content[style*="--ttdk-ui-background-color:#fff"]{color:var(--ttdk-ui-color,var(--ttdk-color-neutral-900,#1f2328));border:1px solid var(--ttdk-stroke-subtle,#0000001a)}.ttdk-tooltip-content[style*="background: #fff"] .ttdk-tooltip-kbd,.ttdk-tooltip-content[style*="background:#fff"] .ttdk-tooltip-kbd,.ttdk-tooltip-content[style*="background: white"] .ttdk-tooltip-kbd,.ttdk-tooltip-content[style*="background-color: #fff"] .ttdk-tooltip-kbd,.ttdk-tooltip-content[style*="background-color:#fff"] .ttdk-tooltip-kbd,.ttdk-tooltip-content[style*="background-color: white"] .ttdk-tooltip-kbd,.ttdk-tooltip-content[style*="--ttdk-ui-background: #fff"] .ttdk-tooltip-kbd,.ttdk-tooltip-content[style*="--ttdk-ui-background:#fff"] .ttdk-tooltip-kbd,.ttdk-tooltip-content[style*="--ttdk-ui-background-color: #fff"] .ttdk-tooltip-kbd,.ttdk-tooltip-content[style*="--ttdk-ui-background-color:#fff"] .ttdk-tooltip-kbd{color:var(--ttdk-color-neutral-900,#1f2328);background:#00000014;border:1px solid #0000001f}
@@ -0,0 +1,423 @@
1
+ "use client";
2
+ import * as React from "react";
3
+ import { useState, useRef, useEffect } from "react";
4
+
5
+ export type TooltipPlacement =
6
+ | "top"
7
+ | "top-start"
8
+ | "top-end"
9
+ | "bottom"
10
+ | "bottom-start"
11
+ | "bottom-end"
12
+ | "left"
13
+ | "left-start"
14
+ | "left-end"
15
+ | "right"
16
+ | "right-start"
17
+ | "right-end";
18
+ export type TooltipUiProps = UiProps;
19
+ export interface TooltipProps {
20
+ /**
21
+ * Teks konten tooltip yang ditampilkan.
22
+ */
23
+ text?: string;
24
+
25
+ /**
26
+ * Konten kustom tooltip (JSX / VNode atau renderable content).
27
+ */
28
+ content?: any;
29
+
30
+ /**
31
+ * Pintasan keyboard opsional yang ditampilkan di dalam tooltip (misal: '⌘K', ['Ctrl', 'S']).
32
+ */
33
+ kbd?: string | string[];
34
+
35
+ /**
36
+ * Posisi penempatan tooltip terhadap elemen pemicu.
37
+ * @default 'top'
38
+ */
39
+ placement?: TooltipPlacement;
40
+
41
+ /**
42
+ * Jarak offset dalam pixel dari elemen pemicu (trigger).
43
+ * @default 8
44
+ */
45
+ offsetDistance?: number;
46
+
47
+ /**
48
+ * Menampilkan elemen panah segitiga penunjuk arah ke elemen pemicu.
49
+ * @default true
50
+ */
51
+ arrow?: boolean;
52
+
53
+ /**
54
+ * Penundaan kemunculan tooltip dalam milidetik (delay saat pointer enter).
55
+ * @default 150
56
+ */
57
+ openDelay?: number;
58
+
59
+ /**
60
+ * Penundaan hilangnya tooltip dalam milidetik (delay saat pointer leave).
61
+ * @default 100
62
+ */
63
+ closeDelay?: number;
64
+
65
+ /**
66
+ * Status visibilitas tooltip terkontrol (controlled mode).
67
+ */
68
+ open?: boolean;
69
+
70
+ /**
71
+ * Status visibilitas awal (uncontrolled initial state).
72
+ * @default false
73
+ */
74
+ defaultOpen?: boolean;
75
+
76
+ /**
77
+ * Menonaktifkan tooltip sehingga tidak muncul saat di-hover atau difokus.
78
+ * @default false
79
+ */
80
+ disabled?: boolean;
81
+
82
+ /**
83
+ * Kustomisasi visual granular container utama (UiProps).
84
+ */
85
+ ui?: TooltipUiProps;
86
+
87
+ /**
88
+ * Style inline CSS native untuk wrapper luar.
89
+ */
90
+ style?: React.CSSProperties;
91
+
92
+ /**
93
+ * Class CSS kustom untuk wrapper luar.
94
+ */
95
+ className?: string;
96
+
97
+ /**
98
+ * Class CSS kustom untuk pemicu (trigger container).
99
+ */
100
+ triggerClass?: string;
101
+
102
+ /**
103
+ * Style kustom untuk pemicu (trigger container) (UiProps).
104
+ */
105
+ triggerStyle?: UiProps;
106
+
107
+ /**
108
+ * Class CSS kustom untuk panel tooltip konten.
109
+ */
110
+ contentClass?: string;
111
+
112
+ /**
113
+ * Style kustom untuk panel tooltip konten (UiProps).
114
+ */
115
+ contentStyle?: UiProps;
116
+
117
+ /**
118
+ * Class CSS kustom untuk elemen panah (arrow).
119
+ */
120
+ arrowClass?: string;
121
+
122
+ /**
123
+ * Style kustom untuk elemen panah (arrow) (UiProps).
124
+ */
125
+ arrowStyle?: UiProps;
126
+
127
+ /**
128
+ * Callback saat status open berubah.
129
+ */
130
+ onOpenChange?: (open: boolean) => void;
131
+
132
+ /**
133
+ * Elemen pemicu (trigger) yang dibungkus oleh tooltip.
134
+ */
135
+ children?: any;
136
+ }
137
+
138
+ /**
139
+ * Komponen Tooltip untuk menampilkan informasi kontekstual singkat saat pengguna mengarahkan kursor atau memfokuskan elemen pemicu.
140
+ */
141
+
142
+ import {
143
+ computePosition,
144
+ autoUpdate,
145
+ flip,
146
+ shift,
147
+ offset,
148
+ arrow as floatingArrow,
149
+ } from "@floating-ui/dom";
150
+ import { buildUiVars, UiProps } from "../../core/types";
151
+
152
+ function Tooltip(props: TooltipProps) {
153
+ const triggerRef = useRef<any>(null);
154
+ const tooltipRef = useRef<any>(null);
155
+ const arrowRef = useRef<any>(null);
156
+ const [isOpen, setIsOpen] = useState(() =>
157
+ props.open !== undefined ? props.open : props.defaultOpen || false
158
+ );
159
+
160
+ const cleanupAutoUpdateRef = useRef<any>(null);
161
+
162
+ const openTimerRef = useRef<any>(null);
163
+
164
+ const closeTimerRef = useRef<any>(null);
165
+
166
+ function getKbdList() {
167
+ if (!props.kbd) return [];
168
+ if (Array.isArray(props.kbd)) return props.kbd;
169
+ return [props.kbd];
170
+ }
171
+
172
+ function clearTimers() {
173
+ if (openTimerRef.current) {
174
+ clearTimeout(openTimerRef.current);
175
+ openTimerRef.current = null;
176
+ }
177
+ if (closeTimerRef.current) {
178
+ clearTimeout(closeTimerRef.current);
179
+ closeTimerRef.current = null;
180
+ }
181
+ }
182
+
183
+ function setOpen(value: boolean) {
184
+ if (props.disabled && value) return;
185
+ if (props.open === undefined) {
186
+ setIsOpen(value);
187
+ }
188
+ if (props.onOpenChange) {
189
+ props.onOpenChange(value);
190
+ }
191
+ if (value) {
192
+ setTimeout(() => {
193
+ startFloating();
194
+ updatePosition();
195
+ }, 0);
196
+ } else {
197
+ stopFloating();
198
+ }
199
+ }
200
+
201
+ function handlePointerEnter() {
202
+ if (props.disabled) return;
203
+ clearTimers();
204
+ const delay = props.openDelay !== undefined ? props.openDelay : 150;
205
+ if (delay <= 0) {
206
+ setOpen(true);
207
+ } else {
208
+ openTimerRef.current = setTimeout(() => {
209
+ setOpen(true);
210
+ }, delay);
211
+ }
212
+ }
213
+
214
+ function handlePointerLeave() {
215
+ clearTimers();
216
+ const delay = props.closeDelay !== undefined ? props.closeDelay : 100;
217
+ if (delay <= 0) {
218
+ setOpen(false);
219
+ } else {
220
+ closeTimerRef.current = setTimeout(() => {
221
+ setOpen(false);
222
+ }, delay);
223
+ }
224
+ }
225
+
226
+ function handleFocus() {
227
+ if (props.disabled) return;
228
+ clearTimers();
229
+ setOpen(true);
230
+ }
231
+
232
+ function handleBlur() {
233
+ clearTimers();
234
+ setOpen(false);
235
+ }
236
+
237
+ function handleKeyDown(e: any) {
238
+ if (e.key === "Escape" && isOpen) {
239
+ clearTimers();
240
+ setOpen(false);
241
+ }
242
+ }
243
+
244
+ function updatePosition() {
245
+ if (!triggerRef.current || !tooltipRef.current) return;
246
+ const targetPlacement = props.placement || "top";
247
+ const showArrow = props.arrow !== false;
248
+ const offsetDist =
249
+ props.offsetDistance !== undefined ? props.offsetDistance : 8;
250
+ const middleware: any[] = [
251
+ offset(offsetDist),
252
+ flip({
253
+ padding: 8,
254
+ }),
255
+ shift({
256
+ padding: 8,
257
+ }),
258
+ ];
259
+ if (showArrow && arrowRef.current) {
260
+ middleware.push(
261
+ floatingArrow({
262
+ element: arrowRef.current,
263
+ padding: 8,
264
+ })
265
+ );
266
+ }
267
+ computePosition(triggerRef.current, tooltipRef.current, {
268
+ placement: targetPlacement,
269
+ middleware,
270
+ }).then((data: any) => {
271
+ if (!tooltipRef.current) return;
272
+ tooltipRef.current.style.left = `${data.x}px`;
273
+ tooltipRef.current.style.top = `${data.y}px`;
274
+ const side = data.placement.split("-")[0];
275
+ tooltipRef.current.setAttribute("data-side", side);
276
+ tooltipRef.current.setAttribute("data-placement", data.placement);
277
+ if (
278
+ showArrow &&
279
+ arrowRef.current &&
280
+ data.middlewareData &&
281
+ data.middlewareData.arrow
282
+ ) {
283
+ const { x: arrowX, y: arrowY } = data.middlewareData.arrow;
284
+ const staticSide: any = {
285
+ top: "bottom",
286
+ right: "left",
287
+ bottom: "top",
288
+ left: "right",
289
+ }[side];
290
+ arrowRef.current.style.left = arrowX != null ? `${arrowX}px` : "";
291
+ arrowRef.current.style.top = arrowY != null ? `${arrowY}px` : "";
292
+ arrowRef.current.style.right = "";
293
+ arrowRef.current.style.bottom = "";
294
+ if (staticSide) {
295
+ arrowRef.current.style[staticSide] = "-4px";
296
+ }
297
+ }
298
+ });
299
+ }
300
+
301
+ function startFloating() {
302
+ stopFloating();
303
+ if (typeof window === "undefined") return;
304
+ if (triggerRef.current && tooltipRef.current) {
305
+ cleanupAutoUpdateRef.current = autoUpdate(
306
+ triggerRef.current,
307
+ tooltipRef.current,
308
+ () => {
309
+ updatePosition();
310
+ }
311
+ );
312
+ }
313
+ }
314
+
315
+ function stopFloating() {
316
+ if (typeof cleanupAutoUpdateRef.current === "function") {
317
+ cleanupAutoUpdateRef.current();
318
+ cleanupAutoUpdateRef.current = null;
319
+ }
320
+ }
321
+
322
+ useEffect(() => {
323
+ if (props.open !== undefined) {
324
+ setIsOpen(props.open);
325
+ }
326
+ }, [props.open]);
327
+
328
+ useEffect(() => {
329
+ if (isOpen) {
330
+ startFloating();
331
+ updatePosition();
332
+ } else {
333
+ stopFloating();
334
+ }
335
+ }, [isOpen]);
336
+
337
+ useEffect(() => {
338
+ if (isOpen) {
339
+ updatePosition();
340
+ }
341
+ }, [props.placement, props.offsetDistance, props.arrow]);
342
+
343
+ useEffect(() => {
344
+ return () => {
345
+ clearTimers();
346
+ stopFloating();
347
+ };
348
+ }, []);
349
+
350
+ return (
351
+ <div
352
+ className={`ttdk-tooltip-wrapper ${props.className || ""}`.trim()}
353
+ data-open={isOpen ? "true" : "false"}
354
+ data-disabled={props.disabled ? "true" : "false"}
355
+ style={{
356
+ ...buildUiVars(props.ui),
357
+ ...((props as any)["style"] || {}),
358
+ ...props.style,
359
+ }}
360
+ >
361
+ <div
362
+ ref={triggerRef}
363
+ className={`ttdk-tooltip-trigger ${props.triggerClass || ""}`.trim()}
364
+ tabIndex={props.disabled ? -1 : 0}
365
+ aria-describedby={isOpen ? "ttdk-tooltip" : undefined}
366
+ data-open={isOpen ? "true" : "false"}
367
+ data-disabled={props.disabled ? "true" : "false"}
368
+ style={{
369
+ ...buildUiVars(props.triggerStyle),
370
+ }}
371
+ onPointerEnter={(event) => handlePointerEnter()}
372
+ onPointerLeave={(event) => handlePointerLeave()}
373
+ onFocus={(event) => handleFocus()}
374
+ onBlur={(event) => handleBlur()}
375
+ onKeyDown={(e) => handleKeyDown(e)}
376
+ >
377
+ {props.children ? <>{props.children}</> : null}
378
+ </div>
379
+ {isOpen ? (
380
+ <div
381
+ role="tooltip"
382
+ ref={tooltipRef}
383
+ className={`ttdk-tooltip-content ${props.contentClass || ""}`.trim()}
384
+ style={{
385
+ ...buildUiVars(props.contentStyle),
386
+ }}
387
+ onPointerEnter={(event) => handlePointerEnter()}
388
+ onPointerLeave={(event) => handlePointerLeave()}
389
+ >
390
+ {props.arrow !== false ? (
391
+ <div
392
+ ref={arrowRef}
393
+ className={`ttdk-tooltip-arrow ${props.arrowClass || ""}`.trim()}
394
+ style={{
395
+ ...buildUiVars(props.arrowStyle),
396
+ }}
397
+ />
398
+ ) : null}
399
+ <div className="ttdk-tooltip-body">
400
+ {props.text ? (
401
+ <span className="ttdk-tooltip-text">{props.text}</span>
402
+ ) : null}
403
+ {props.content && !props.text ? <>{props.content}</> : null}
404
+ {!props.text && !props.content ? <>{props.content}</> : null}
405
+ {props.kbd ? (
406
+ <span className="ttdk-tooltip-kbd-list">
407
+ {getKbdList()?.map((key, idx) => (
408
+ <kbd className="ttdk-tooltip-kbd" key={idx}>
409
+ {key}
410
+ </kbd>
411
+ ))}
412
+ </span>
413
+ ) : null}
414
+ </div>
415
+ </div>
416
+ ) : null}
417
+ </div>
418
+ );
419
+ }
420
+
421
+ export default Tooltip;
422
+
423
+ export { Tooltip };