@ttdk-design-system/library-vue 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 (232) hide show
  1. package/AI_GUIDE.md +6518 -0
  2. package/llms.txt +6518 -0
  3. package/package.json +1096 -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.vue +432 -0
  16. package/src/web/accordion/accordion.vue.d.ts +227 -0
  17. package/src/web/accordion/index.ts +3 -0
  18. package/src/web/accordion/manifest.json +351 -0
  19. package/src/web/alert/AI_GUIDE.md +139 -0
  20. package/src/web/alert/alert.css +1 -0
  21. package/src/web/alert/alert.vue +290 -0
  22. package/src/web/alert/alert.vue.d.ts +154 -0
  23. package/src/web/alert/index.ts +3 -0
  24. package/src/web/alert/manifest.json +240 -0
  25. package/src/web/avatar/AI_GUIDE.md +118 -0
  26. package/src/web/avatar/avatar.css +1 -0
  27. package/src/web/avatar/avatar.vue +217 -0
  28. package/src/web/avatar/avatar.vue.d.ts +135 -0
  29. package/src/web/avatar/index.ts +3 -0
  30. package/src/web/avatar/manifest.json +195 -0
  31. package/src/web/badge/AI_GUIDE.md +94 -0
  32. package/src/web/badge/badge.css +1 -0
  33. package/src/web/badge/badge.vue +135 -0
  34. package/src/web/badge/badge.vue.d.ts +108 -0
  35. package/src/web/badge/index.ts +3 -0
  36. package/src/web/badge/manifest.json +141 -0
  37. package/src/web/banner/AI_GUIDE.md +130 -0
  38. package/src/web/banner/banner.css +1 -0
  39. package/src/web/banner/banner.vue +303 -0
  40. package/src/web/banner/banner.vue.d.ts +162 -0
  41. package/src/web/banner/index.ts +3 -0
  42. package/src/web/banner/manifest.json +240 -0
  43. package/src/web/breadcrumb/AI_GUIDE.md +120 -0
  44. package/src/web/breadcrumb/breadcrumb.css +1 -0
  45. package/src/web/breadcrumb/breadcrumb.vue +313 -0
  46. package/src/web/breadcrumb/breadcrumb.vue.d.ts +139 -0
  47. package/src/web/breadcrumb/index.ts +3 -0
  48. package/src/web/breadcrumb/manifest.json +225 -0
  49. package/src/web/button/AI_GUIDE.md +121 -0
  50. package/src/web/button/button.css +1 -0
  51. package/src/web/button/button.vue +283 -0
  52. package/src/web/button/button.vue.d.ts +147 -0
  53. package/src/web/button/index.ts +3 -0
  54. package/src/web/button/manifest.json +177 -0
  55. package/src/web/calendar/AI_GUIDE.md +151 -0
  56. package/src/web/calendar/calendar.css +1 -0
  57. package/src/web/calendar/calendar.vue +1536 -0
  58. package/src/web/calendar/calendar.vue.d.ts +261 -0
  59. package/src/web/calendar/index.ts +3 -0
  60. package/src/web/calendar/manifest.json +470 -0
  61. package/src/web/card/AI_GUIDE.md +133 -0
  62. package/src/web/card/card.css +1 -0
  63. package/src/web/card/card.vue +423 -0
  64. package/src/web/card/card.vue.d.ts +232 -0
  65. package/src/web/card/index.ts +3 -0
  66. package/src/web/card/manifest.json +357 -0
  67. package/src/web/checkbox/AI_GUIDE.md +146 -0
  68. package/src/web/checkbox/checkbox.css +1 -0
  69. package/src/web/checkbox/checkbox.vue +304 -0
  70. package/src/web/checkbox/checkbox.vue.d.ts +189 -0
  71. package/src/web/checkbox/index.ts +3 -0
  72. package/src/web/checkbox/manifest.json +294 -0
  73. package/src/web/chip/AI_GUIDE.md +186 -0
  74. package/src/web/chip/chip.css +1 -0
  75. package/src/web/chip/chip.vue +220 -0
  76. package/src/web/chip/chip.vue.d.ts +140 -0
  77. package/src/web/chip/index.ts +3 -0
  78. package/src/web/chip/manifest.json +186 -0
  79. package/src/web/dropdown/AI_GUIDE.md +167 -0
  80. package/src/web/dropdown/dropdown.css +1 -0
  81. package/src/web/dropdown/dropdown.vue +1257 -0
  82. package/src/web/dropdown/dropdown.vue.d.ts +381 -0
  83. package/src/web/dropdown/index.ts +3 -0
  84. package/src/web/dropdown/manifest.json +576 -0
  85. package/src/web/flex/AI_GUIDE.md +113 -0
  86. package/src/web/flex/flex-item.vue +94 -0
  87. package/src/web/flex/flex-item.vue.d.ts +84 -0
  88. package/src/web/flex/flex.css +1 -0
  89. package/src/web/flex/flex.vue +103 -0
  90. package/src/web/flex/flex.vue.d.ts +92 -0
  91. package/src/web/flex/index.ts +5 -0
  92. package/src/web/flex/manifest.json +162 -0
  93. package/src/web/form/index.ts +7 -0
  94. package/src/web/grid/AI_GUIDE.md +112 -0
  95. package/src/web/grid/grid-item.vue +109 -0
  96. package/src/web/grid/grid-item.vue.d.ts +99 -0
  97. package/src/web/grid/grid.css +1 -0
  98. package/src/web/grid/grid.vue +94 -0
  99. package/src/web/grid/grid.vue.d.ts +84 -0
  100. package/src/web/grid/index.ts +5 -0
  101. package/src/web/grid/manifest.json +153 -0
  102. package/src/web/index.ts +86 -0
  103. package/src/web/input/AI_GUIDE.md +159 -0
  104. package/src/web/input/index.ts +3 -0
  105. package/src/web/input/input.css +1 -0
  106. package/src/web/input/input.vue +521 -0
  107. package/src/web/input/input.vue.d.ts +274 -0
  108. package/src/web/input/manifest.json +465 -0
  109. package/src/web/input-number/AI_GUIDE.md +164 -0
  110. package/src/web/input-number/index.ts +3 -0
  111. package/src/web/input-number/input-number.css +1 -0
  112. package/src/web/input-number/input-number.vue +1153 -0
  113. package/src/web/input-number/input-number.vue.d.ts +343 -0
  114. package/src/web/input-number/manifest.json +575 -0
  115. package/src/web/modal/AI_GUIDE.md +171 -0
  116. package/src/web/modal/index.ts +3 -0
  117. package/src/web/modal/manifest.json +375 -0
  118. package/src/web/modal/modal.css +1 -0
  119. package/src/web/modal/modal.vue +344 -0
  120. package/src/web/modal/modal.vue.d.ts +64 -0
  121. package/src/web/pagination/AI_GUIDE.md +148 -0
  122. package/src/web/pagination/index.ts +21 -0
  123. package/src/web/pagination/manifest.json +432 -0
  124. package/src/web/pagination/pagination-content.vue +42 -0
  125. package/src/web/pagination/pagination-content.vue.d.ts +28 -0
  126. package/src/web/pagination/pagination-ellipsis.vue +55 -0
  127. package/src/web/pagination/pagination-ellipsis.vue.d.ts +28 -0
  128. package/src/web/pagination/pagination-first.vue +161 -0
  129. package/src/web/pagination/pagination-first.vue.d.ts +67 -0
  130. package/src/web/pagination/pagination-item.vue +42 -0
  131. package/src/web/pagination/pagination-item.vue.d.ts +28 -0
  132. package/src/web/pagination/pagination-last.vue +161 -0
  133. package/src/web/pagination/pagination-last.vue.d.ts +67 -0
  134. package/src/web/pagination/pagination-link.vue +118 -0
  135. package/src/web/pagination/pagination-link.vue.d.ts +61 -0
  136. package/src/web/pagination/pagination-next.vue +160 -0
  137. package/src/web/pagination/pagination-next.vue.d.ts +68 -0
  138. package/src/web/pagination/pagination-previous.vue +160 -0
  139. package/src/web/pagination/pagination-previous.vue.d.ts +68 -0
  140. package/src/web/pagination/pagination.css +1 -0
  141. package/src/web/pagination/pagination.vue +730 -0
  142. package/src/web/pagination/pagination.vue.d.ts +247 -0
  143. package/src/web/popover/AI_GUIDE.md +163 -0
  144. package/src/web/popover/index.ts +3 -0
  145. package/src/web/popover/manifest.json +396 -0
  146. package/src/web/popover/popover.css +1 -0
  147. package/src/web/popover/popover.vue +579 -0
  148. package/src/web/popover/popover.vue.d.ts +83 -0
  149. package/src/web/radio/AI_GUIDE.md +155 -0
  150. package/src/web/radio/index.ts +3 -0
  151. package/src/web/radio/manifest.json +294 -0
  152. package/src/web/radio/radio.css +1 -0
  153. package/src/web/radio/radio.vue +288 -0
  154. package/src/web/radio/radio.vue.d.ts +182 -0
  155. package/src/web/selectbox/AI_GUIDE.md +216 -0
  156. package/src/web/selectbox/index.ts +3 -0
  157. package/src/web/selectbox/manifest.json +477 -0
  158. package/src/web/selectbox/selectbox.css +1 -0
  159. package/src/web/selectbox/selectbox.vue +1024 -0
  160. package/src/web/selectbox/selectbox.vue.d.ts +300 -0
  161. package/src/web/sonner/AI_GUIDE.md +98 -0
  162. package/src/web/sonner/index.ts +114 -0
  163. package/src/web/sonner/manifest.json +132 -0
  164. package/src/web/sonner/sonner.css +1 -0
  165. package/src/web/sonner/sonner.vue +180 -0
  166. package/src/web/sonner/sonner.vue.d.ts +113 -0
  167. package/src/web/table/AI_GUIDE.md +145 -0
  168. package/src/web/table/index.ts +34 -0
  169. package/src/web/table/manifest.json +168 -0
  170. package/src/web/table/table-body.vue +42 -0
  171. package/src/web/table/table-body.vue.d.ts +28 -0
  172. package/src/web/table/table-caption.vue +42 -0
  173. package/src/web/table/table-caption.vue.d.ts +28 -0
  174. package/src/web/table/table-cell.vue +71 -0
  175. package/src/web/table/table-cell.vue.d.ts +49 -0
  176. package/src/web/table/table-empty.vue +105 -0
  177. package/src/web/table/table-empty.vue.d.ts +57 -0
  178. package/src/web/table/table-footer.vue +42 -0
  179. package/src/web/table/table-footer.vue.d.ts +28 -0
  180. package/src/web/table/table-head.vue +78 -0
  181. package/src/web/table/table-head.vue.d.ts +50 -0
  182. package/src/web/table/table-header.vue +42 -0
  183. package/src/web/table/table-header.vue.d.ts +28 -0
  184. package/src/web/table/table-row.vue +70 -0
  185. package/src/web/table/table-row.vue.d.ts +44 -0
  186. package/src/web/table/table.css +1 -0
  187. package/src/web/table/table.vue +298 -0
  188. package/src/web/table/table.vue.d.ts +119 -0
  189. package/src/web/tabs/AI_GUIDE.md +150 -0
  190. package/src/web/tabs/index.ts +2 -0
  191. package/src/web/tabs/manifest.json +315 -0
  192. package/src/web/tabs/tabs.css +1 -0
  193. package/src/web/tabs/tabs.vue +164 -0
  194. package/src/web/tabs/tabs.vue.d.ts +60 -0
  195. package/src/web/text/AI_GUIDE.md +129 -0
  196. package/src/web/text/index.ts +2 -0
  197. package/src/web/text/manifest.json +195 -0
  198. package/src/web/text/text.css +1 -0
  199. package/src/web/text/text.vue +195 -0
  200. package/src/web/text/text.vue.d.ts +161 -0
  201. package/src/web/textarea/AI_GUIDE.md +141 -0
  202. package/src/web/textarea/index.ts +3 -0
  203. package/src/web/textarea/manifest.json +438 -0
  204. package/src/web/textarea/textarea.css +1 -0
  205. package/src/web/textarea/textarea.vue +458 -0
  206. package/src/web/textarea/textarea.vue.d.ts +265 -0
  207. package/src/web/tooltip/AI_GUIDE.md +110 -0
  208. package/src/web/tooltip/index.ts +3 -0
  209. package/src/web/tooltip/manifest.json +222 -0
  210. package/src/web/tooltip/tooltip.css +1 -0
  211. package/src/web/tooltip/tooltip.vue +417 -0
  212. package/src/web/tooltip/tooltip.vue.d.ts +150 -0
  213. package/src/webview/bottomsheet/AI_GUIDE.md +141 -0
  214. package/src/webview/bottomsheet/bottomsheet.css +1 -0
  215. package/src/webview/bottomsheet/bottomsheet.vue +441 -0
  216. package/src/webview/bottomsheet/bottomsheet.vue.d.ts +178 -0
  217. package/src/webview/bottomsheet/index.ts +4 -0
  218. package/src/webview/bottomsheet/manifest.json +377 -0
  219. package/src/webview/button/AI_GUIDE.md +79 -0
  220. package/src/webview/button/button-ios.css +1 -0
  221. package/src/webview/button/button-ios.vue +138 -0
  222. package/src/webview/button/button-ios.vue.d.ts +102 -0
  223. package/src/webview/button/button-material.css +1 -0
  224. package/src/webview/button/button-material.vue +138 -0
  225. package/src/webview/button/button-material.vue.d.ts +102 -0
  226. package/src/webview/button/index.ts +7 -0
  227. package/src/webview/button/manifest.json +148 -0
  228. package/src/webview/index.ts +7 -0
  229. package/tailwind-preset.cjs +62 -0
  230. package/tailwind-preset.js +62 -0
  231. package/ttdk-manifest.json +10188 -0
  232. package/README.md +0 -3
@@ -0,0 +1,344 @@
1
+ <template>
2
+ <Teleport to="body">
3
+ <div
4
+ v-if="isOpen"
5
+ ref="rootRef"
6
+ :class="[`ttdk-modal-portal ${className || ''}`.trim(), $attrs.class]"
7
+ role="dialog"
8
+ :aria-modal="modal !== false ? 'true' : 'false'"
9
+ :tabIndex="-1"
10
+ :style="{
11
+ ...buildUiVars(ui),
12
+ ...(typeof style === 'object' ? style : {}),
13
+ }"
14
+ >
15
+ <!-- 1. Backdrop Overlay -->
16
+ <div
17
+ v-if="overlay !== false"
18
+ :class="[`ttdk-modal-overlay ${overlayClass || ''}`.trim()]"
19
+ :style="{
20
+ ...buildUiVars(overlayStyle),
21
+ }"
22
+ @click="handleOverlayClick"
23
+ />
24
+
25
+ <!-- 2. Modal Dialog Card -->
26
+ <div
27
+ ref="dialogRef"
28
+ :class="[
29
+ 'ttdk-modal-dialog',
30
+ isAnimDone ? 'ttdk-modal-anim-done' : '',
31
+ isShaking ? 'ttdk-modal-shake' : '',
32
+ contentClass || ''
33
+ ].filter(Boolean).join(' ').trim()"
34
+ :data-size="size || 'md'"
35
+ :data-fullscreen="fullscreen ? 'true' : undefined"
36
+ :data-scrollable="scrollable !== false ? 'true' : undefined"
37
+ :style="{
38
+ ...buildUiVars(contentStyle),
39
+ }"
40
+ >
41
+ <!-- Slot: Full content override -->
42
+ <slot name="content" :close="() => closeModal(true)">
43
+ <!-- Modal Header -->
44
+ <div
45
+ v-if="hasHeader"
46
+ :class="[`ttdk-modal-header ${headerClass || ''}`.trim()]"
47
+ :style="{
48
+ ...buildUiVars(headerStyle),
49
+ }"
50
+ >
51
+ <slot name="header" :close="() => closeModal(true)">
52
+ <div class="ttdk-modal-header-info">
53
+ <h3 v-if="title" class="ttdk-modal-title">{{ title }}</h3>
54
+ <p v-if="description" class="ttdk-modal-description">{{ description }}</p>
55
+ </div>
56
+
57
+ <slot name="close" :close="() => closeModal(true)">
58
+ <button
59
+ v-if="close !== false"
60
+ type="button"
61
+ class="ttdk-modal-close"
62
+ aria-label="Tutup modal"
63
+ @click="closeModal(true)"
64
+ >
65
+ <component :is="closeIcon" v-if="closeIcon" />
66
+ <svg
67
+ v-else
68
+ width="16"
69
+ height="16"
70
+ viewBox="0 0 24 24"
71
+ fill="none"
72
+ stroke="currentColor"
73
+ >
74
+ <line x1="18" y1="6" x2="6" y2="18" />
75
+ <line x1="6" y1="6" x2="18" y2="18" />
76
+ </svg>
77
+ </button>
78
+ </slot>
79
+ </slot>
80
+ </div>
81
+
82
+ <!-- Modal Body -->
83
+ <div
84
+ :class="[`ttdk-modal-body ${bodyClass || ''}`.trim()]"
85
+ :style="{
86
+ ...buildUiVars(bodyStyle),
87
+ }"
88
+ >
89
+ <slot name="body" :close="() => closeModal(true)">
90
+ <slot :close="() => closeModal(true)" />
91
+ </slot>
92
+ </div>
93
+
94
+ <!-- Modal Footer -->
95
+ <div
96
+ v-if="hasFooter"
97
+ :class="[`ttdk-modal-footer ${footerClass || ''}`.trim()]"
98
+ :style="{
99
+ ...buildUiVars(footerStyle),
100
+ }"
101
+ >
102
+ <slot name="footer" :close="() => closeModal(true)" />
103
+ </div>
104
+ </slot>
105
+ </div>
106
+ </div>
107
+ </Teleport>
108
+ </template>
109
+
110
+ <script setup lang="ts">
111
+ import { ref, watch, onMounted, onUnmounted, useSlots, computed } from "vue";
112
+ import { buildUiVars, type UiProps } from "../../core/types";
113
+
114
+ export type ModalSize =
115
+ | 'xs'
116
+ | 'sm'
117
+ | 'md'
118
+ | 'lg'
119
+ | 'xl'
120
+ | '2xl'
121
+ | '3xl'
122
+ | '4xl'
123
+ | '5xl'
124
+ | 'full';
125
+
126
+ export type ModalUiProps = UiProps;
127
+
128
+ export interface ModalProps {
129
+ open?: boolean;
130
+ defaultOpen?: boolean;
131
+ title?: string;
132
+ description?: string;
133
+ close?: boolean | any;
134
+ closeIcon?: any;
135
+ fullscreen?: boolean;
136
+ size?: ModalSize;
137
+ dismissible?: boolean;
138
+ preventClose?: boolean;
139
+ overlay?: boolean;
140
+ modal?: boolean;
141
+ scrollable?: boolean;
142
+ preventScroll?: boolean;
143
+ lockScroll?: boolean;
144
+ ui?: ModalUiProps;
145
+ className?: string;
146
+ class?: string;
147
+ style?: any;
148
+ overlayClass?: string;
149
+ overlayStyle?: UiProps;
150
+ contentClass?: string;
151
+ contentStyle?: UiProps;
152
+ headerClass?: string;
153
+ headerStyle?: UiProps;
154
+ bodyClass?: string;
155
+ bodyStyle?: UiProps;
156
+ footerClass?: string;
157
+ footerStyle?: UiProps;
158
+ onUpdateOpen?: (open: boolean) => void;
159
+ onOpenChange?: (open: boolean) => void;
160
+ onOpen?: () => void;
161
+ onClose?: () => void;
162
+ onClosePrevent?: () => void;
163
+ }
164
+
165
+ const props = withDefaults(defineProps<ModalProps>(), {
166
+ defaultOpen: false,
167
+ close: true,
168
+ fullscreen: false,
169
+ size: "md",
170
+ dismissible: true,
171
+ preventClose: false,
172
+ overlay: true,
173
+ modal: true,
174
+ scrollable: true,
175
+ preventScroll: true,
176
+ lockScroll: true,
177
+ });
178
+
179
+ const emit = defineEmits<{
180
+ (e: "update:open", value: boolean): void;
181
+ (e: "update:modelValue", value: boolean): void;
182
+ (e: "open"): void;
183
+ (e: "close"): void;
184
+ (e: "close:prevent"): void;
185
+ }>();
186
+
187
+ const slots = useSlots();
188
+ const rootRef = ref<HTMLElement | null>(null);
189
+ const dialogRef = ref<HTMLElement | null>(null);
190
+
191
+ const isOpen = ref(props.open !== undefined ? props.open : props.defaultOpen);
192
+ const isShaking = ref(false);
193
+ const isAnimDone = ref(false);
194
+ let shakeTimer: any = null;
195
+ let animTimer: any = null;
196
+
197
+ const hasHeader = computed(() => {
198
+ return Boolean(slots.header || props.title || props.description || props.close !== false);
199
+ });
200
+
201
+ const hasFooter = computed(() => {
202
+ return Boolean(slots.footer);
203
+ });
204
+
205
+ let lockCount = 0;
206
+ let originalOverflow = "";
207
+ let originalPaddingRight = "";
208
+
209
+ function lockBodyScroll() {
210
+ if (typeof document === "undefined") return;
211
+ if (lockCount === 0) {
212
+ originalOverflow = document.body.style.overflow;
213
+ originalPaddingRight = document.body.style.paddingRight;
214
+ const scrollBarWidth = window.innerWidth - document.documentElement.clientWidth;
215
+ document.body.style.overflow = "hidden";
216
+ if (scrollBarWidth > 0) {
217
+ document.body.style.paddingRight = scrollBarWidth + "px";
218
+ }
219
+ }
220
+ lockCount++;
221
+ }
222
+
223
+ function unlockBodyScroll() {
224
+ if (typeof document === "undefined") return;
225
+ lockCount = Math.max(0, lockCount - 1);
226
+ if (lockCount === 0) {
227
+ document.body.style.overflow = originalOverflow;
228
+ document.body.style.paddingRight = originalPaddingRight;
229
+ }
230
+ }
231
+
232
+ let isScrollLocked = false;
233
+ function applyScrollLock(shouldLock: boolean) {
234
+ if (shouldLock && !isScrollLocked) {
235
+ lockBodyScroll();
236
+ isScrollLocked = true;
237
+ } else if (!shouldLock && isScrollLocked) {
238
+ unlockBodyScroll();
239
+ isScrollLocked = false;
240
+ }
241
+ }
242
+
243
+ function isDismissible() {
244
+ if (props.preventClose === true) return false;
245
+ return props.dismissible !== false;
246
+ }
247
+
248
+ function triggerShake() {
249
+ if (shakeTimer) clearTimeout(shakeTimer);
250
+ isAnimDone.value = true;
251
+ isShaking.value = true;
252
+ shakeTimer = setTimeout(() => {
253
+ isShaking.value = false;
254
+ shakeTimer = null;
255
+ }, 400);
256
+ }
257
+
258
+ function openModal() {
259
+ if (!isOpen.value) {
260
+ isOpen.value = true;
261
+ emit("update:open", true);
262
+ emit("update:modelValue", true);
263
+ emit("open");
264
+ if (props.onOpen) props.onOpen();
265
+ if (props.onUpdateOpen) props.onUpdateOpen(true);
266
+ if (props.onOpenChange) props.onOpenChange(true);
267
+ }
268
+ }
269
+
270
+ function closeModal(force?: boolean) {
271
+ if (!isDismissible() && !force) {
272
+ triggerShake();
273
+ emit("close:prevent");
274
+ if (props.onClosePrevent) props.onClosePrevent();
275
+ return;
276
+ }
277
+ if (isOpen.value) {
278
+ isOpen.value = false;
279
+ emit("update:open", false);
280
+ emit("update:modelValue", false);
281
+ emit("close");
282
+ if (props.onClose) props.onClose();
283
+ if (props.onUpdateOpen) props.onUpdateOpen(false);
284
+ if (props.onOpenChange) props.onOpenChange(false);
285
+ }
286
+ }
287
+
288
+ function handleOverlayClick() {
289
+ closeModal();
290
+ }
291
+
292
+ function handleKeyDown(event: KeyboardEvent) {
293
+ if (!isOpen.value) return;
294
+ if (event.key === "Escape") {
295
+ closeModal();
296
+ }
297
+ }
298
+
299
+ watch(
300
+ () => props.open,
301
+ (val) => {
302
+ if (val !== undefined && val !== isOpen.value) {
303
+ if (val) {
304
+ openModal();
305
+ } else {
306
+ closeModal(true);
307
+ }
308
+ }
309
+ }
310
+ );
311
+
312
+ watch(
313
+ isOpen,
314
+ (val) => {
315
+ const shouldLock = Boolean(props.preventScroll !== false && props.lockScroll !== false);
316
+ if (val) {
317
+ isAnimDone.value = false;
318
+ if (animTimer) clearTimeout(animTimer);
319
+ animTimer = setTimeout(() => {
320
+ isAnimDone.value = true;
321
+ }, 250);
322
+ if (shouldLock) applyScrollLock(true);
323
+ if (typeof document !== "undefined") {
324
+ document.addEventListener("keydown", handleKeyDown, true);
325
+ }
326
+ } else {
327
+ isAnimDone.value = false;
328
+ applyScrollLock(false);
329
+ if (typeof document !== "undefined") {
330
+ document.removeEventListener("keydown", handleKeyDown, true);
331
+ }
332
+ }
333
+ },
334
+ { immediate: true }
335
+ );
336
+
337
+ onUnmounted(() => {
338
+ if (shakeTimer) clearTimeout(shakeTimer);
339
+ applyScrollLock(false);
340
+ if (typeof document !== "undefined") {
341
+ document.removeEventListener("keydown", handleKeyDown, true);
342
+ }
343
+ });
344
+ </script>
@@ -0,0 +1,64 @@
1
+ /* eslint-disable */
2
+ // Auto-generated by TTDK Design System Vue Type Generator
3
+ import type { DefineComponent } from "vue";
4
+ import { buildUiVars, type UiProps } from "../../core/types";
5
+
6
+ export type ModalSize =
7
+ | 'xs'
8
+ | 'sm'
9
+ | 'md'
10
+ | 'lg'
11
+ | 'xl'
12
+ | '2xl'
13
+ | '3xl'
14
+ | '4xl'
15
+ | '5xl'
16
+ | 'full';
17
+
18
+ export type ModalUiProps = UiProps;
19
+
20
+ export interface ModalProps {
21
+ open?: boolean;
22
+ defaultOpen?: boolean;
23
+ title?: string;
24
+ description?: string;
25
+ close?: boolean | any;
26
+ closeIcon?: any;
27
+ fullscreen?: boolean;
28
+ size?: ModalSize;
29
+ dismissible?: boolean;
30
+ preventClose?: boolean;
31
+ overlay?: boolean;
32
+ modal?: boolean;
33
+ scrollable?: boolean;
34
+ preventScroll?: boolean;
35
+ lockScroll?: boolean;
36
+ ui?: ModalUiProps;
37
+ className?: string;
38
+ class?: string;
39
+ style?: any;
40
+ overlayClass?: string;
41
+ overlayStyle?: UiProps;
42
+ contentClass?: string;
43
+ contentStyle?: UiProps;
44
+ headerClass?: string;
45
+ headerStyle?: UiProps;
46
+ bodyClass?: string;
47
+ bodyStyle?: UiProps;
48
+ footerClass?: string;
49
+ footerStyle?: UiProps;
50
+ onUpdateOpen?: (open: boolean) => void;
51
+ onOpenChange?: (open: boolean) => void;
52
+ onOpen?: () => void;
53
+ onClose?: () => void;
54
+ onClosePrevent?: () => void;
55
+ }
56
+
57
+ declare const component: DefineComponent<ModalProps, {}, {}, {}, {}, {}, {}, {
58
+ (e: "update:open", value: boolean): void;
59
+ (e: "update:modelValue", value: boolean): void;
60
+ (e: "open"): void;
61
+ (e: "close"): void;
62
+ (e: "close:prevent"): void;
63
+ }>;
64
+ export default component;
@@ -0,0 +1,148 @@
1
+ # <Pagination /> — TTDK Component Reference
2
+
3
+ Komponen Pagination navigasi halaman dengan dukungan Compound API (ala Shadcn UI) dan Props-driven mode.
4
+
5
+ ## 1. Stylesheets & Theming (Pure CSS)
6
+ Import the corresponding stylesheet for the theme you wish to use:
7
+ - **COMMON Theme**: `import "@ttdk-design-system/library-<react|vue|svelte>/web/pagination/pagination.css";`
8
+
9
+ ## 2. Subpath Component Import
10
+ ```ts
11
+ // React
12
+ import Pagination from "@ttdk-design-system/library-react/web/pagination";
13
+
14
+ // Vue 3
15
+ import Pagination from "@ttdk-design-system/library-vue/web/pagination";
16
+
17
+ // Svelte
18
+ import Pagination from "@ttdk-design-system/library-svelte/web/pagination";
19
+ ```
20
+
21
+ ## 3. Props & API Reference
22
+
23
+ | Prop | Type | Default | Description |
24
+ | :--- | :--- | :--- | :--- |
25
+ | `page?` | `number` | `1` | Nomor halaman saat ini (1-indexed). |
26
+ | `maxPages?` | `number` | `1` | Jumlah total halaman maksimum (alias untuk totalPages). |
27
+ | `totalPages?` | `number` | `1` | Jumlah total halaman (alias maxPages). |
28
+ | `limit?` | `number` | `10` | Jumlah baris/item data per halaman (page size). |
29
+ | `onLimitChange?` | `(limit: number) => void` | `-` | Callback saat jumlah baris per halaman diubah via select box. |
30
+ | `limitOptions?` | `number[]` | `[10, 20, 50, 100]` | Pilihan angka baris per halaman pada select box. |
31
+ | `canPrevPage?` | `boolean` | `false` | Menentukan apakah navigasi ke halaman sebelumnya diizinkan (false = tombol Previous dinonaktifkan). |
32
+ | `canNextPage?` | `boolean` | `false` | Menentukan apakah navigasi ke halaman berikutnya diizinkan (false = tombol Next dinonaktifkan). |
33
+ | `onFirstPage?` | `() => void` | `-` | Callback saat tombol First Page (halaman pertama) diklik. |
34
+ | `onPrevPage?` | `() => void` | `-` | Callback saat tombol Previous Page (halaman sebelumnya) diklik. |
35
+ | `onNextPage?` | `() => void` | `-` | Callback saat tombol Next Page (halaman selanjutnya) diklik. |
36
+ | `onLastPage?` | `() => void` | `-` | Callback saat tombol Last Page (halaman terakhir) diklik. |
37
+ | `onPageChange?` | `(page: number) => void` | `-` | Callback saat nomor halaman tertentu diklik. |
38
+ | `siblingCount?` | `number` | `1` | Jumlah tetangga nomor halaman di samping kiri dan kanan halaman aktif. |
39
+ | `showEdges?` | `boolean` | `false` | Menampilkan tombol First (<<) dan Last (>>). |
40
+ | `showPageNumbers?` | `boolean` | `true` | Menampilkan nomor halaman terpaginasi (1, 2, 3...). |
41
+ | `size?` | `'sm' | 'md' | 'lg'` | `md` | Skala ukuran pagination: - `'sm'`: 28px - `'md'`: 36px (standar) - `'lg'`: 44px |
42
+ | `style?` | `any` | `-` | Inline CSS style object native. |
43
+ | `ui?` | `UiProps` | `-` | Kustomisasi visual granular root kontainer pagination (UiProps). |
44
+ | `contentStyle?` | `UiProps` | `-` | Style kustom untuk kontainer daftar pagination content (ul.ttdk-pagination-content). |
45
+ | `contentClass?` | `string` | `-` | Class CSS kustom untuk kontainer daftar pagination content. |
46
+ | `linkStyle?` | `UiProps` | `-` | Style kustom untuk tombol link nomor halaman / navigasi. |
47
+ | `linkClass?` | `string` | `-` | Class CSS kustom untuk tombol link nomor halaman / navigasi. |
48
+ | `activeLinkStyle?` | `UiProps` | `-` | Style kustom untuk tombol nomor halaman saat aktif. |
49
+ | `activeLinkClass?` | `string` | `-` | Class CSS kustom untuk tombol nomor halaman saat aktif. |
50
+ | `limitStyle?` | `UiProps` | `-` | Style kustom untuk kontainer baris per halaman (limit). |
51
+ | `limitClass?` | `string` | `-` | Class CSS kustom untuk kontainer baris per halaman (limit). |
52
+ | `limitSelectStyle?` | `UiProps` | `-` | Style kustom untuk elemen Selectbox baris per halaman (limit). |
53
+ | `limitSlot?` | `any` | `-` | Slot kustom untuk menggantikan elemen limit selector bawaan. |
54
+ | `showLimit?` | `boolean` | `-` | Menentukan apakah kontrol baris per halaman (limit selectbox) ditampilkan. Jika tidak disetel, otomatis mengikuti keberadaan callback onLimitChange. |
55
+ | `showLimitLabel?` | `boolean` | `true` | Menentukan apakah teks label 'Baris per halaman:' ditampilkan di samping selectbox limit. |
56
+ | `limitLabel?` | `string` | `Baris per halaman:` | Teks label untuk opsi baris per halaman. |
57
+ | `firstIcon?` | `any` | `-` | Ikon kustom untuk tombol halaman pertama (first page, menggantikan <<). |
58
+ | `prevIcon?` | `any` | `-` | Ikon kustom untuk tombol halaman sebelumnya (previous page, menggantikan <). |
59
+ | `nextIcon?` | `any` | `-` | Ikon kustom untuk tombol halaman selanjutnya (next page, menggantikan >). |
60
+ | `lastIcon?` | `any` | `-` | Ikon kustom untuk tombol halaman terakhir (last page, menggantikan >>). |
61
+ | `ellipsisIcon?` | `any` | `-` | Ikon kustom untuk separator elipsis (menggantikan ...). |
62
+ | `prevText?` | `string` | `Previous` | Label teks tombol Previous. |
63
+ | `nextText?` | `string` | `Next` | Label teks tombol Next. |
64
+ | `iconOnly?` | `boolean` | `false` | Mode hanya menampilkan ikon pada tombol Previous dan Next tanpa teks label. |
65
+ | `className?` | `string` | `-` | Class CSS kustom. |
66
+ | `children?` | `any` | `-` | Konten kustom atau subkomponen compound (PaginationContent, PaginationItem, dll). |
67
+
68
+ ### Sub-interface: `PaginationPageItem`
69
+
70
+ | Field | Type | Default | Description |
71
+ | :--- | :--- | :--- | :--- |
72
+ | `type` | `'page' | 'ellipsis'` | `-` | |
73
+ | `value` | `number | string` | `-` | |
74
+ | `key` | `string` | `-` | |
75
+
76
+ ## 4. Usage Examples
77
+
78
+ ### React Example
79
+ ```tsx
80
+ import { useState } from "react";
81
+ import Pagination from "@ttdk-design-system/library-react/pagination";
82
+ import "@ttdk-design-system/library-react/pagination/pagination.css";
83
+
84
+ export function Example() {
85
+ const [page, setPage] = useState(1);
86
+ const [limit, setLimit] = useState(10);
87
+
88
+ return (
89
+ <Pagination
90
+ page={page}
91
+ totalPages={15}
92
+ limit={limit}
93
+ limitOptions={[10, 25, 50, 100]}
94
+ canPrevPage={page > 1}
95
+ canNextPage={page < 15}
96
+ onPageChange={(p) => setPage(p)}
97
+ onLimitChange={(l) => setLimit(l)}
98
+ />
99
+ );
100
+ }
101
+ ```
102
+
103
+ ### Vue 3 Example
104
+ ```vue
105
+ <script setup lang="ts">
106
+ import { ref } from "vue";
107
+ import Pagination from "@ttdk-design-system/library-vue/pagination";
108
+ import "@ttdk-design-system/library-vue/pagination/pagination.css";
109
+
110
+ const page = ref(1);
111
+ const limit = ref(10);
112
+ </script>
113
+
114
+ <template>
115
+ <Pagination
116
+ :page="page"
117
+ :total-pages="15"
118
+ :limit="limit"
119
+ :limit-options="[10, 25, 50, 100]"
120
+ :can-prev-page="page > 1"
121
+ :can-next-page="page < 15"
122
+ @page-change="(p: number) => (page = p)"
123
+ @limit-change="(l: number) => (limit = l)"
124
+ />
125
+ </template>
126
+ ```
127
+
128
+ ### Svelte Example
129
+ ```svelte
130
+ <script lang="ts">
131
+ import Pagination from "@ttdk-design-system/library-svelte/pagination";
132
+ import "@ttdk-design-system/library-svelte/pagination/pagination.css";
133
+
134
+ let page = 1;
135
+ let limit = 10;
136
+ </script>
137
+
138
+ <Pagination
139
+ {page}
140
+ totalPages={15}
141
+ {limit}
142
+ limitOptions={[10, 25, 50, 100]}
143
+ canPrevPage={page > 1}
144
+ canNextPage={page < 15}
145
+ on:pageChange={(e) => (page = e.detail)}
146
+ on:limitChange={(e) => (limit = e.detail)}
147
+ />
148
+ ```
@@ -0,0 +1,21 @@
1
+ export { default as Pagination } from './pagination.vue';
2
+ export { default as PaginationContent } from './pagination-content.vue';
3
+ export { default as PaginationItem } from './pagination-item.vue';
4
+ export { default as PaginationLink } from './pagination-link.vue';
5
+ export { default as PaginationPrevious } from './pagination-previous.vue';
6
+ export { default as PaginationNext } from './pagination-next.vue';
7
+ export { default as PaginationFirst } from './pagination-first.vue';
8
+ export { default as PaginationLast } from './pagination-last.vue';
9
+ export { default as PaginationEllipsis } from './pagination-ellipsis.vue';
10
+
11
+ export * from './pagination.vue';
12
+ export * from './pagination-content.vue';
13
+ export * from './pagination-item.vue';
14
+ export * from './pagination-link.vue';
15
+ export * from './pagination-previous.vue';
16
+ export * from './pagination-next.vue';
17
+ export * from './pagination-first.vue';
18
+ export * from './pagination-last.vue';
19
+ export * from './pagination-ellipsis.vue';
20
+
21
+ export { default } from './pagination.vue';