@krizaka/ui 2.3.0 → 2.4.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 (280) hide show
  1. package/README.md +16 -1
  2. package/dist/audio-player.d.ts +113 -0
  3. package/dist/audio-player.js +207 -0
  4. package/dist/audio-player.js.map +1 -0
  5. package/dist/chunk-2USC2YT6.js +17 -0
  6. package/dist/chunk-2USC2YT6.js.map +1 -0
  7. package/dist/chunk-EUPTXKH7.js +95 -0
  8. package/dist/chunk-EUPTXKH7.js.map +1 -0
  9. package/dist/chunk-RUPRYJ45.js +82 -0
  10. package/dist/chunk-RUPRYJ45.js.map +1 -0
  11. package/dist/chunk-XUMJA5ZI.js +283 -0
  12. package/dist/chunk-XUMJA5ZI.js.map +1 -0
  13. package/dist/chunk-YH4LMWRU.js +92 -0
  14. package/dist/chunk-YH4LMWRU.js.map +1 -0
  15. package/dist/chunk-YX6CNVJA.js +94 -0
  16. package/dist/chunk-YX6CNVJA.js.map +1 -0
  17. package/dist/chunk-ZIXB33X7.js +95 -0
  18. package/dist/chunk-ZIXB33X7.js.map +1 -0
  19. package/dist/combobox.d.ts +188 -0
  20. package/dist/combobox.js +16 -0
  21. package/dist/combobox.js.map +1 -0
  22. package/dist/data-table.d.ts +246 -0
  23. package/dist/data-table.js +290 -0
  24. package/dist/data-table.js.map +1 -0
  25. package/dist/date-picker.d.ts +288 -0
  26. package/dist/date-picker.js +364 -0
  27. package/dist/date-picker.js.map +1 -0
  28. package/dist/file-upload.d.ts +238 -0
  29. package/dist/file-upload.js +437 -0
  30. package/dist/file-upload.js.map +1 -0
  31. package/dist/filter-bar.d.ts +139 -0
  32. package/dist/filter-bar.js +155 -0
  33. package/dist/filter-bar.js.map +1 -0
  34. package/dist/image-cropper.d.ts +115 -0
  35. package/dist/image-cropper.js +166 -0
  36. package/dist/image-cropper.js.map +1 -0
  37. package/dist/index.js.map +1 -1
  38. package/dist/media-capture.d.ts +155 -0
  39. package/dist/media-capture.js +242 -0
  40. package/dist/media-capture.js.map +1 -0
  41. package/dist/native/media-capture.d.ts +49 -0
  42. package/dist/native/media-capture.js +113 -0
  43. package/dist/native/media-capture.js.map +1 -0
  44. package/dist/native/video-player.d.ts +50 -0
  45. package/dist/native/video-player.js +69 -0
  46. package/dist/native/video-player.js.map +1 -0
  47. package/dist/native.d.ts +66 -2
  48. package/dist/native.js +166 -68
  49. package/dist/native.js.map +1 -1
  50. package/dist/pagination.d.ts +141 -0
  51. package/dist/pagination.js +165 -0
  52. package/dist/pagination.js.map +1 -0
  53. package/dist/search-field.d.ts +100 -0
  54. package/dist/search-field.js +167 -0
  55. package/dist/search-field.js.map +1 -0
  56. package/dist/tag-input.d.ts +44 -0
  57. package/dist/tag-input.js +67 -0
  58. package/dist/tag-input.js.map +1 -0
  59. package/dist/video-player.d.ts +204 -0
  60. package/dist/video-player.js +371 -0
  61. package/dist/video-player.js.map +1 -0
  62. package/dist/video-trimmer.d.ts +142 -0
  63. package/dist/video-trimmer.js +193 -0
  64. package/dist/video-trimmer.js.map +1 -0
  65. package/package.json +232 -4
  66. package/registry/alert.json +1 -1
  67. package/registry/audio-player.json +282 -0
  68. package/registry/avatar.json +1 -1
  69. package/registry/badge.json +1 -1
  70. package/registry/button.json +1 -1
  71. package/registry/card.json +1 -1
  72. package/registry/checkbox.json +1 -1
  73. package/registry/chip-link.json +1 -1
  74. package/registry/chip.json +1 -1
  75. package/registry/combobox.json +475 -0
  76. package/registry/command.json +1 -1
  77. package/registry/confirm-button.json +1 -1
  78. package/registry/countdown.json +1 -1
  79. package/registry/data-table.json +500 -0
  80. package/registry/date-picker.json +978 -0
  81. package/registry/dialog.json +1 -1
  82. package/registry/dropdown-menu.json +1 -1
  83. package/registry/empty-state.json +1 -1
  84. package/registry/examples/audio-player/podcast.d.ts +4 -0
  85. package/registry/examples/audio-player/podcast.js +22 -0
  86. package/registry/examples/audio-player/podcast.tsx +19 -0
  87. package/registry/examples/audio-player/voice-note.d.ts +4 -0
  88. package/registry/examples/audio-player/voice-note.js +12 -0
  89. package/registry/examples/audio-player/voice-note.tsx +12 -0
  90. package/registry/examples/combobox/async.d.ts +4 -0
  91. package/registry/examples/combobox/async.js +23 -0
  92. package/registry/examples/combobox/async.tsx +22 -0
  93. package/registry/examples/combobox/creatable.d.ts +4 -0
  94. package/registry/examples/combobox/creatable.js +32 -0
  95. package/registry/examples/combobox/creatable.tsx +28 -0
  96. package/registry/examples/combobox/invalid.d.ts +4 -0
  97. package/registry/examples/combobox/invalid.js +23 -0
  98. package/registry/examples/combobox/invalid.tsx +17 -0
  99. package/registry/examples/combobox/multiple.d.ts +4 -0
  100. package/registry/examples/combobox/multiple.js +29 -0
  101. package/registry/examples/combobox/multiple.tsx +26 -0
  102. package/registry/examples/data-table/cards.d.ts +4 -0
  103. package/registry/examples/data-table/cards.js +21 -0
  104. package/registry/examples/data-table/cards.tsx +20 -0
  105. package/registry/examples/data-table/empty.d.ts +4 -0
  106. package/registry/examples/data-table/empty.js +26 -0
  107. package/registry/examples/data-table/empty.tsx +21 -0
  108. package/registry/examples/data-table/error.d.ts +4 -0
  109. package/registry/examples/data-table/error.js +28 -0
  110. package/registry/examples/data-table/error.tsx +29 -0
  111. package/registry/examples/data-table/jobs.d.ts +4 -0
  112. package/registry/examples/data-table/jobs.js +37 -0
  113. package/registry/examples/data-table/jobs.tsx +36 -0
  114. package/registry/examples/data-table/loading.d.ts +4 -0
  115. package/registry/examples/data-table/loading.js +16 -0
  116. package/registry/examples/data-table/loading.tsx +12 -0
  117. package/registry/examples/data-table/virtualized.d.ts +4 -0
  118. package/registry/examples/data-table/virtualized.js +21 -0
  119. package/registry/examples/data-table/virtualized.tsx +20 -0
  120. package/registry/examples/date-picker/calendar.d.ts +4 -0
  121. package/registry/examples/date-picker/calendar.js +14 -0
  122. package/registry/examples/date-picker/calendar.tsx +16 -0
  123. package/registry/examples/date-picker/french.d.ts +4 -0
  124. package/registry/examples/date-picker/french.js +20 -0
  125. package/registry/examples/date-picker/french.tsx +14 -0
  126. package/registry/examples/date-picker/open.d.ts +4 -0
  127. package/registry/examples/date-picker/open.js +11 -0
  128. package/registry/examples/date-picker/open.tsx +8 -0
  129. package/registry/examples/date-picker/range.d.ts +4 -0
  130. package/registry/examples/date-picker/range.js +29 -0
  131. package/registry/examples/date-picker/range.tsx +25 -0
  132. package/registry/examples/date-picker/schedule.d.ts +4 -0
  133. package/registry/examples/date-picker/schedule.js +26 -0
  134. package/registry/examples/date-picker/schedule.tsx +22 -0
  135. package/registry/examples/file-upload/images.d.ts +4 -0
  136. package/registry/examples/file-upload/images.js +32 -0
  137. package/registry/examples/file-upload/images.tsx +25 -0
  138. package/registry/examples/file-upload/states.d.ts +4 -0
  139. package/registry/examples/file-upload/states.js +28 -0
  140. package/registry/examples/file-upload/states.tsx +23 -0
  141. package/registry/examples/file-upload/tus.d.ts +4 -0
  142. package/registry/examples/file-upload/tus.js +23 -0
  143. package/registry/examples/file-upload/tus.tsx +26 -0
  144. package/registry/examples/file-upload/video.d.ts +4 -0
  145. package/registry/examples/file-upload/video.js +39 -0
  146. package/registry/examples/file-upload/video.tsx +36 -0
  147. package/registry/examples/filter-bar/address.d.ts +4 -0
  148. package/registry/examples/filter-bar/address.js +29 -0
  149. package/registry/examples/filter-bar/address.tsx +27 -0
  150. package/registry/examples/filter-bar/jobs.d.ts +4 -0
  151. package/registry/examples/filter-bar/jobs.js +37 -0
  152. package/registry/examples/filter-bar/jobs.tsx +37 -0
  153. package/registry/examples/filter-bar/menu.d.ts +4 -0
  154. package/registry/examples/filter-bar/menu.js +27 -0
  155. package/registry/examples/filter-bar/menu.tsx +23 -0
  156. package/registry/examples/image-cropper/avatar.d.ts +4 -0
  157. package/registry/examples/image-cropper/avatar.js +13 -0
  158. package/registry/examples/image-cropper/avatar.tsx +14 -0
  159. package/registry/examples/image-cropper/banner.d.ts +4 -0
  160. package/registry/examples/image-cropper/banner.js +11 -0
  161. package/registry/examples/image-cropper/banner.tsx +10 -0
  162. package/registry/examples/image-cropper/vertical-video.d.ts +4 -0
  163. package/registry/examples/image-cropper/vertical-video.js +12 -0
  164. package/registry/examples/image-cropper/vertical-video.tsx +16 -0
  165. package/registry/examples/media-capture/clip.d.ts +4 -0
  166. package/registry/examples/media-capture/clip.js +20 -0
  167. package/registry/examples/media-capture/clip.tsx +18 -0
  168. package/registry/examples/media-capture/native/photo.d.ts +4 -0
  169. package/registry/examples/media-capture/native/photo.dark.png +0 -0
  170. package/registry/examples/media-capture/native/photo.js +27 -0
  171. package/registry/examples/media-capture/native/photo.light.png +0 -0
  172. package/registry/examples/media-capture/native/photo.tsx +20 -0
  173. package/registry/examples/media-capture/profile-photo.d.ts +4 -0
  174. package/registry/examples/media-capture/profile-photo.js +24 -0
  175. package/registry/examples/media-capture/profile-photo.tsx +20 -0
  176. package/registry/examples/pagination/cursor.d.ts +4 -0
  177. package/registry/examples/pagination/cursor.js +24 -0
  178. package/registry/examples/pagination/cursor.tsx +20 -0
  179. package/registry/examples/pagination/links.d.ts +4 -0
  180. package/registry/examples/pagination/links.js +11 -0
  181. package/registry/examples/pagination/links.tsx +6 -0
  182. package/registry/examples/pagination/native/cursor.d.ts +4 -0
  183. package/registry/examples/pagination/native/cursor.dark.png +0 -0
  184. package/registry/examples/pagination/native/cursor.js +19 -0
  185. package/registry/examples/pagination/native/cursor.light.png +0 -0
  186. package/registry/examples/pagination/native/cursor.tsx +13 -0
  187. package/registry/examples/pagination/native/pages.d.ts +4 -0
  188. package/registry/examples/pagination/native/pages.dark.png +0 -0
  189. package/registry/examples/pagination/native/pages.js +21 -0
  190. package/registry/examples/pagination/native/pages.light.png +0 -0
  191. package/registry/examples/pagination/native/pages.tsx +14 -0
  192. package/registry/examples/pagination/results.d.ts +4 -0
  193. package/registry/examples/pagination/results.js +23 -0
  194. package/registry/examples/pagination/results.tsx +19 -0
  195. package/registry/examples/pagination/table-footer.d.ts +4 -0
  196. package/registry/examples/pagination/table-footer.js +31 -0
  197. package/registry/examples/pagination/table-footer.tsx +27 -0
  198. package/registry/examples/search-field/global.d.ts +4 -0
  199. package/registry/examples/search-field/global.js +19 -0
  200. package/registry/examples/search-field/global.tsx +15 -0
  201. package/registry/examples/search-field/native/default.d.ts +4 -0
  202. package/registry/examples/search-field/native/default.dark.png +0 -0
  203. package/registry/examples/search-field/native/default.js +13 -0
  204. package/registry/examples/search-field/native/default.light.png +0 -0
  205. package/registry/examples/search-field/native/default.tsx +7 -0
  206. package/registry/examples/search-field/suggestions.d.ts +4 -0
  207. package/registry/examples/search-field/suggestions.js +30 -0
  208. package/registry/examples/search-field/suggestions.tsx +27 -0
  209. package/registry/examples/search-field/toolbar.d.ts +4 -0
  210. package/registry/examples/search-field/toolbar.js +17 -0
  211. package/registry/examples/search-field/toolbar.tsx +21 -0
  212. package/registry/examples/tag-input/recipients.d.ts +4 -0
  213. package/registry/examples/tag-input/recipients.js +23 -0
  214. package/registry/examples/tag-input/recipients.tsx +20 -0
  215. package/registry/examples/tag-input/video-tags.d.ts +4 -0
  216. package/registry/examples/tag-input/video-tags.js +26 -0
  217. package/registry/examples/tag-input/video-tags.tsx +22 -0
  218. package/registry/examples/video-player/default.d.ts +4 -0
  219. package/registry/examples/video-player/default.js +12 -0
  220. package/registry/examples/video-player/default.tsx +8 -0
  221. package/registry/examples/video-player/gated.d.ts +4 -0
  222. package/registry/examples/video-player/gated.js +26 -0
  223. package/registry/examples/video-player/gated.tsx +23 -0
  224. package/registry/examples/video-player/native/default.d.ts +4 -0
  225. package/registry/examples/video-player/native/default.dark.png +0 -0
  226. package/registry/examples/video-player/native/default.js +18 -0
  227. package/registry/examples/video-player/native/default.light.png +0 -0
  228. package/registry/examples/video-player/native/default.tsx +12 -0
  229. package/registry/examples/video-player/signed-source.d.ts +4 -0
  230. package/registry/examples/video-player/signed-source.js +16 -0
  231. package/registry/examples/video-player/signed-source.tsx +15 -0
  232. package/registry/examples/video-player/vertical.d.ts +4 -0
  233. package/registry/examples/video-player/vertical.js +12 -0
  234. package/registry/examples/video-player/vertical.tsx +12 -0
  235. package/registry/examples/video-trimmer/custom.d.ts +4 -0
  236. package/registry/examples/video-trimmer/custom.js +24 -0
  237. package/registry/examples/video-trimmer/custom.tsx +26 -0
  238. package/registry/examples/video-trimmer/filmstrip.d.ts +4 -0
  239. package/registry/examples/video-trimmer/filmstrip.js +15 -0
  240. package/registry/examples/video-trimmer/filmstrip.tsx +13 -0
  241. package/registry/examples/video-trimmer/playhead.d.ts +4 -0
  242. package/registry/examples/video-trimmer/playhead.js +15 -0
  243. package/registry/examples/video-trimmer/playhead.tsx +13 -0
  244. package/registry/field.json +1 -1
  245. package/registry/file-upload.json +471 -0
  246. package/registry/filter-bar.json +347 -0
  247. package/registry/glow-card.json +1 -1
  248. package/registry/image-cropper.json +313 -0
  249. package/registry/index.json +255 -1
  250. package/registry/iso.json +1 -1
  251. package/registry/kbd.json +1 -1
  252. package/registry/loader.json +1 -1
  253. package/registry/marquee.json +1 -1
  254. package/registry/mascots.json +1 -1
  255. package/registry/media-capture.json +347 -0
  256. package/registry/meter.json +3 -3
  257. package/registry/nav-bar.json +1 -1
  258. package/registry/number-ticker.json +1 -1
  259. package/registry/page-header.json +1 -1
  260. package/registry/pagination.json +475 -0
  261. package/registry/popover.json +1 -1
  262. package/registry/progress.json +5 -5
  263. package/registry/radio-group.json +1 -1
  264. package/registry/search-field.json +436 -0
  265. package/registry/section-backdrop.json +1 -1
  266. package/registry/separator.json +1 -1
  267. package/registry/skeleton.json +1 -1
  268. package/registry/slider.json +1 -1
  269. package/registry/slot.json +1 -1
  270. package/registry/spinner.json +1 -1
  271. package/registry/stat.json +1 -1
  272. package/registry/stepper.json +1 -1
  273. package/registry/switch.json +1 -1
  274. package/registry/tabs.json +1 -1
  275. package/registry/tag-input.json +363 -0
  276. package/registry/theme.json +1 -1
  277. package/registry/toast.json +1 -1
  278. package/registry/tooltip.json +1 -1
  279. package/registry/video-player.json +598 -0
  280. package/registry/video-trimmer.json +385 -0
@@ -0,0 +1,141 @@
1
+ import * as tailwind_variants from 'tailwind-variants';
2
+ import * as React from 'react';
3
+
4
+ declare const pagination: tailwind_variants.TVReturnType<{
5
+ [key: string]: {
6
+ [key: string]: tailwind_variants.ClassValue | {
7
+ root?: tailwind_variants.ClassValue;
8
+ list?: tailwind_variants.ClassValue;
9
+ pages?: tailwind_variants.ClassValue;
10
+ compact?: tailwind_variants.ClassValue;
11
+ edge?: tailwind_variants.ClassValue;
12
+ ellipsis?: tailwind_variants.ClassValue;
13
+ status?: tailwind_variants.ClassValue;
14
+ size?: tailwind_variants.ClassValue;
15
+ select?: tailwind_variants.ClassValue;
16
+ item?: tailwind_variants.ClassValue;
17
+ };
18
+ };
19
+ } | {
20
+ [x: string]: {
21
+ [x: string]: tailwind_variants.ClassValue | {
22
+ root?: tailwind_variants.ClassValue;
23
+ list?: tailwind_variants.ClassValue;
24
+ pages?: tailwind_variants.ClassValue;
25
+ compact?: tailwind_variants.ClassValue;
26
+ edge?: tailwind_variants.ClassValue;
27
+ ellipsis?: tailwind_variants.ClassValue;
28
+ status?: tailwind_variants.ClassValue;
29
+ size?: tailwind_variants.ClassValue;
30
+ select?: tailwind_variants.ClassValue;
31
+ item?: tailwind_variants.ClassValue;
32
+ };
33
+ };
34
+ } | {}, {
35
+ root: string;
36
+ list: string;
37
+ pages: string;
38
+ compact: string;
39
+ item: string;
40
+ edge: string;
41
+ ellipsis: string;
42
+ status: string;
43
+ size: string;
44
+ select: string;
45
+ }, undefined, {
46
+ [key: string]: {
47
+ [key: string]: tailwind_variants.ClassValue | {
48
+ root?: tailwind_variants.ClassValue;
49
+ list?: tailwind_variants.ClassValue;
50
+ pages?: tailwind_variants.ClassValue;
51
+ compact?: tailwind_variants.ClassValue;
52
+ edge?: tailwind_variants.ClassValue;
53
+ ellipsis?: tailwind_variants.ClassValue;
54
+ status?: tailwind_variants.ClassValue;
55
+ size?: tailwind_variants.ClassValue;
56
+ select?: tailwind_variants.ClassValue;
57
+ item?: tailwind_variants.ClassValue;
58
+ };
59
+ };
60
+ } | {}, {
61
+ root: string;
62
+ list: string;
63
+ pages: string;
64
+ compact: string;
65
+ item: string;
66
+ edge: string;
67
+ ellipsis: string;
68
+ status: string;
69
+ size: string;
70
+ select: string;
71
+ }, tailwind_variants.TVReturnTypeLike<unknown, {
72
+ root: string;
73
+ list: string;
74
+ pages: string;
75
+ compact: string;
76
+ item: string;
77
+ edge: string;
78
+ ellipsis: string;
79
+ status: string;
80
+ size: string;
81
+ select: string;
82
+ }>>;
83
+ type PaginationLabels = {
84
+ /** The accessible name of the navigation ("Pagination", "Pages of results"). */
85
+ nav: string;
86
+ /** The previous-page control. */
87
+ previous: string;
88
+ /** The next-page control. */
89
+ next: string;
90
+ /** The accessible name of a page number: `(3) => "Page 3"`. */
91
+ page: (page: number) => string;
92
+ /** The folded form, in a narrow container: `(3, 12) => "Page 3 of 12"`. */
93
+ compact: (page: number, pageCount: number) => string;
94
+ /** The label of the page-size select ("Rows per page"). */
95
+ pageSize: string;
96
+ };
97
+ /** The English words, for a quick start — pass your translations through `labels`. */
98
+ declare const paginationLabels: PaginationLabels;
99
+ /**
100
+ * The pages to show: the first and last `boundaries`, the current page and its `siblings`, and `"…"` where pages are
101
+ * skipped. A gap of a single page shows that page instead of an ellipsis; few pages are all shown.
102
+ */
103
+ declare function pageRange(page: number, pageCount: number, siblings?: number, boundaries?: number): (number | "…")[];
104
+ type PaginationProps = Omit<React.ComponentProps<"nav">, "children"> & {
105
+ /** Pages mode: the current page, from 1. */
106
+ page?: number;
107
+ /** Pages mode: how many pages there are. Without it, the pagination is in cursor mode. */
108
+ pageCount?: number;
109
+ /** Called with the page asked for (buttons). Ignored for the pages `getHref` turns into links. */
110
+ onPageChange?: (page: number) => void;
111
+ /** The address of a page: the pages become links (crawlable, shareable, the state in the URL). */
112
+ getHref?: (page: number) => string;
113
+ /** Cursor mode: whether there is a previous page. */
114
+ hasPrevious?: boolean;
115
+ /** Cursor mode: whether there is a next page. */
116
+ hasNext?: boolean;
117
+ /** Cursor mode: called by the previous control. */
118
+ onPrevious?: () => void;
119
+ /** Cursor mode: called by the next control. */
120
+ onNext?: () => void;
121
+ /** Pages shown on each side of the current one. Default 1. */
122
+ siblings?: number;
123
+ /** Pages always shown at each end. Default 1. */
124
+ boundaries?: number;
125
+ /** A text beside the controls, read as is: "41–60 of 1,284", "12 results". */
126
+ status?: React.ReactNode;
127
+ /** The page size, with `pageSizeOptions` and `onPageSizeChange`: a select of rows per page. */
128
+ pageSize?: number;
129
+ /** The sizes offered (`[10, 20, 50]`). */
130
+ pageSizeOptions?: readonly number[];
131
+ /** Called with the size chosen. */
132
+ onPageSizeChange?: (size: number) => void;
133
+ /** Disables every control (a page is loading). */
134
+ disabled?: boolean;
135
+ /** The words, passed translated; English by default. */
136
+ labels?: Partial<PaginationLabels>;
137
+ };
138
+ /** Moves through a long list: page numbers (folded on a narrow screen) or previous / next on a cursor. */
139
+ declare function Pagination({ page, pageCount, onPageChange, getHref, hasPrevious, hasNext, onPrevious, onNext, siblings, boundaries, status, pageSize, pageSizeOptions, onPageSizeChange, disabled, labels: given, className, ...props }: PaginationProps): React.JSX.Element;
140
+
141
+ export { Pagination, type PaginationLabels, type PaginationProps, pageRange, pagination, paginationLabels };
@@ -0,0 +1,165 @@
1
+ // src/pagination/pagination.tsx
2
+ import { tv } from "tailwind-variants";
3
+
4
+ // src/glyphs.tsx
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ function Glyph({ className = "h-4 w-4", fill, children }) {
7
+ return /* @__PURE__ */ jsx(
8
+ "svg",
9
+ {
10
+ viewBox: "0 0 24 24",
11
+ className,
12
+ fill: fill ? "currentColor" : "none",
13
+ stroke: fill ? "none" : "currentColor",
14
+ strokeWidth: "2",
15
+ strokeLinecap: "round",
16
+ strokeLinejoin: "round",
17
+ "aria-hidden": true,
18
+ focusable: "false",
19
+ children
20
+ }
21
+ );
22
+ }
23
+ var ChevronGlyph = ({ direction = "down", ...p }) => /* @__PURE__ */ jsx(Glyph, { ...p, children: /* @__PURE__ */ jsx("path", { d: { left: "m15 6-6 6 6 6", right: "m9 6 6 6-6 6", up: "m6 15 6-6 6 6", down: "m6 9 6 6 6-6" }[direction] }) });
24
+
25
+ // src/pagination/pagination.tsx
26
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
27
+ var pagination = tv({
28
+ slots: {
29
+ root: "@container flex w-full flex-wrap items-center justify-between gap-3 text-sm",
30
+ list: "flex items-center gap-1",
31
+ pages: "hidden items-center gap-1 @md:flex",
32
+ compact: "px-2 text-xs font-semibold tabular-nums text-fg-secondary @md:hidden",
33
+ item: "inline-flex h-9 min-w-9 select-none items-center justify-center gap-1.5 rounded-lg px-2.5 text-sm font-semibold tabular-nums text-fg-secondary transition-colors hover:bg-surface-2 hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring aria-[current=page]:bg-accent aria-[current=page]:text-on-accent aria-[current=page]:hover:bg-accent aria-disabled:pointer-events-none aria-disabled:opacity-40 disabled:pointer-events-none disabled:opacity-40",
34
+ edge: "border border-border-default bg-surface-1 px-3",
35
+ ellipsis: "inline-flex h-9 min-w-6 items-end justify-center pb-2 text-fg-muted",
36
+ status: "text-xs text-fg-secondary tabular-nums",
37
+ size: "flex items-center gap-2 text-xs text-fg-secondary",
38
+ select: "h-9 rounded-lg border border-border-default bg-surface-2 px-2 text-sm text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
39
+ }
40
+ });
41
+ var paginationLabels = {
42
+ nav: "Pagination",
43
+ previous: "Previous",
44
+ next: "Next",
45
+ page: (page) => `Page ${page}`,
46
+ compact: (page, count) => `Page ${page} of ${count}`,
47
+ pageSize: "Rows per page"
48
+ };
49
+ function pageRange(page, pageCount, siblings = 1, boundaries = 1) {
50
+ if (pageCount <= 2 * boundaries + 2 * siblings + 3) return Array.from({ length: Math.max(0, pageCount) }, (_, i) => i + 1);
51
+ const pages = /* @__PURE__ */ new Set();
52
+ for (let i = 1; i <= Math.min(boundaries, pageCount); i += 1) pages.add(i);
53
+ for (let i = Math.max(1, pageCount - boundaries + 1); i <= pageCount; i += 1) pages.add(i);
54
+ for (let i = Math.max(1, page - siblings); i <= Math.min(pageCount, page + siblings); i += 1) pages.add(i);
55
+ const sorted = [...pages].sort((a, b) => a - b);
56
+ const out = [];
57
+ sorted.forEach((n, i) => {
58
+ const prev = sorted[i - 1];
59
+ if (prev !== void 0 && n - prev === 2) out.push(prev + 1);
60
+ else if (prev !== void 0 && n - prev > 2) out.push("\u2026");
61
+ out.push(n);
62
+ });
63
+ return out;
64
+ }
65
+ function Item({ href, onClick, disabled, current, label, className, children }) {
66
+ const common = { "aria-label": label, "aria-current": current ? "page" : void 0, className };
67
+ if (href && !disabled) {
68
+ return /* @__PURE__ */ jsx2("a", { href, onClick, ...common, children });
69
+ }
70
+ if (href) {
71
+ return /* @__PURE__ */ jsx2("a", { role: "link", "aria-disabled": true, ...common, children });
72
+ }
73
+ return /* @__PURE__ */ jsx2("button", { type: "button", onClick, disabled, ...common, children });
74
+ }
75
+ function Pagination({
76
+ page = 1,
77
+ pageCount,
78
+ onPageChange,
79
+ getHref,
80
+ hasPrevious,
81
+ hasNext,
82
+ onPrevious,
83
+ onNext,
84
+ siblings = 1,
85
+ boundaries = 1,
86
+ status,
87
+ pageSize,
88
+ pageSizeOptions,
89
+ onPageSizeChange,
90
+ disabled,
91
+ labels: given,
92
+ className,
93
+ ...props
94
+ }) {
95
+ const s = pagination();
96
+ const labels = { ...paginationLabels, ...given };
97
+ const paged = pageCount !== void 0;
98
+ const count = Math.max(1, pageCount ?? 1);
99
+ const current = Math.min(Math.max(1, page), count);
100
+ const canPrevious = paged ? current > 1 : Boolean(hasPrevious);
101
+ const canNext = paged ? current < count : Boolean(hasNext);
102
+ const go = (target) => () => onPageChange?.(target);
103
+ const href = (target) => paged && getHref ? getHref(target) : void 0;
104
+ return /* @__PURE__ */ jsxs2("nav", { "aria-label": labels.nav, className: s.root({ className }), ...props, children: [
105
+ status !== void 0 || pageSizeOptions ? /* @__PURE__ */ jsxs2("div", { className: "flex flex-wrap items-center gap-4", children: [
106
+ status !== void 0 && /* @__PURE__ */ jsx2("p", { className: s.status(), children: status }),
107
+ pageSizeOptions && pageSize !== void 0 && /* @__PURE__ */ jsxs2("label", { className: s.size(), children: [
108
+ labels.pageSize,
109
+ /* @__PURE__ */ jsx2(
110
+ "select",
111
+ {
112
+ className: s.select(),
113
+ value: pageSize,
114
+ disabled,
115
+ onChange: (event) => onPageSizeChange?.(Number(event.target.value)),
116
+ children: pageSizeOptions.map((size) => /* @__PURE__ */ jsx2("option", { value: size, children: size }, size))
117
+ }
118
+ )
119
+ ] })
120
+ ] }) : null,
121
+ /* @__PURE__ */ jsxs2("ul", { className: s.list(), children: [
122
+ /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsxs2(
123
+ Item,
124
+ {
125
+ href: href(current - 1),
126
+ onClick: paged ? go(current - 1) : onPrevious,
127
+ disabled: disabled || !canPrevious,
128
+ label: labels.previous,
129
+ className: s.item({ className: s.edge() }),
130
+ children: [
131
+ /* @__PURE__ */ jsx2(ChevronGlyph, { direction: "left" }),
132
+ /* @__PURE__ */ jsx2("span", { className: "hidden @lg:inline", children: labels.previous })
133
+ ]
134
+ }
135
+ ) }),
136
+ paged && /* @__PURE__ */ jsxs2(Fragment, { children: [
137
+ /* @__PURE__ */ jsx2("li", { className: s.pages(), children: /* @__PURE__ */ jsx2("ul", { className: "contents", children: pageRange(current, count, siblings, boundaries).map(
138
+ (n, i) => n === "\u2026" ? /* @__PURE__ */ jsx2("li", { "aria-hidden": true, className: s.ellipsis(), children: "\u2026" }, `gap-${i}`) : /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsx2(Item, { href: href(n), onClick: go(n), disabled, current: n === current, label: labels.page(n), className: s.item(), children: n }) }, n)
139
+ ) }) }),
140
+ /* @__PURE__ */ jsx2("li", { className: s.compact(), children: labels.compact(current, count) })
141
+ ] }),
142
+ /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsxs2(
143
+ Item,
144
+ {
145
+ href: href(current + 1),
146
+ onClick: paged ? go(current + 1) : onNext,
147
+ disabled: disabled || !canNext,
148
+ label: labels.next,
149
+ className: s.item({ className: s.edge() }),
150
+ children: [
151
+ /* @__PURE__ */ jsx2("span", { className: "hidden @lg:inline", children: labels.next }),
152
+ /* @__PURE__ */ jsx2(ChevronGlyph, { direction: "right" })
153
+ ]
154
+ }
155
+ ) })
156
+ ] })
157
+ ] });
158
+ }
159
+ export {
160
+ Pagination,
161
+ pageRange,
162
+ pagination,
163
+ paginationLabels
164
+ };
165
+ //# sourceMappingURL=pagination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pagination/pagination.tsx","../src/glyphs.tsx"],"sourcesContent":["// Server-safe: no hook, no context. A `<nav>` named by `labels.nav`; the current page is `aria-current=\"page\"`.\n// Two modes: pages (`page` + `pageCount`: numbers with ellipses, which fold into \"Page 3 of 12\" in a narrow container)\n// and cursor (`hasPrevious` / `hasNext`: an API that only knows the next and previous pages). Links (`getHref`) when\n// the page lives in the address — the right default for a list a search engine or a shared link should reach — or\n// buttons (`onPageChange`).\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { ChevronGlyph } from \"../glyphs\";\n\nexport const pagination = tv({\n slots: {\n root: \"@container flex w-full flex-wrap items-center justify-between gap-3 text-sm\",\n list: \"flex items-center gap-1\",\n pages: \"hidden items-center gap-1 @md:flex\",\n compact: \"px-2 text-xs font-semibold tabular-nums text-fg-secondary @md:hidden\",\n item:\n \"inline-flex h-9 min-w-9 select-none items-center justify-center gap-1.5 rounded-lg px-2.5 text-sm font-semibold tabular-nums text-fg-secondary \" +\n \"transition-colors hover:bg-surface-2 hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring \" +\n \"aria-[current=page]:bg-accent aria-[current=page]:text-on-accent aria-[current=page]:hover:bg-accent \" +\n \"aria-disabled:pointer-events-none aria-disabled:opacity-40 disabled:pointer-events-none disabled:opacity-40\",\n edge: \"border border-border-default bg-surface-1 px-3\",\n ellipsis: \"inline-flex h-9 min-w-6 items-end justify-center pb-2 text-fg-muted\",\n status: \"text-xs text-fg-secondary tabular-nums\",\n size: \"flex items-center gap-2 text-xs text-fg-secondary\",\n select:\n \"h-9 rounded-lg border border-border-default bg-surface-2 px-2 text-sm text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n },\n});\n\nexport type PaginationLabels = {\n /** The accessible name of the navigation (\"Pagination\", \"Pages of results\"). */\n nav: string;\n /** The previous-page control. */\n previous: string;\n /** The next-page control. */\n next: string;\n /** The accessible name of a page number: `(3) => \"Page 3\"`. */\n page: (page: number) => string;\n /** The folded form, in a narrow container: `(3, 12) => \"Page 3 of 12\"`. */\n compact: (page: number, pageCount: number) => string;\n /** The label of the page-size select (\"Rows per page\"). */\n pageSize: string;\n};\n\n/** The English words, for a quick start — pass your translations through `labels`. */\nexport const paginationLabels: PaginationLabels = {\n nav: \"Pagination\",\n previous: \"Previous\",\n next: \"Next\",\n page: (page) => `Page ${page}`,\n compact: (page, count) => `Page ${page} of ${count}`,\n pageSize: \"Rows per page\",\n};\n\n/**\n * The pages to show: the first and last `boundaries`, the current page and its `siblings`, and `\"…\"` where pages are\n * skipped. A gap of a single page shows that page instead of an ellipsis; few pages are all shown.\n */\nexport function pageRange(page: number, pageCount: number, siblings = 1, boundaries = 1): (number | \"…\")[] {\n // Few enough pages to show them all in the room the folded form takes: no ellipsis.\n if (pageCount <= 2 * boundaries + 2 * siblings + 3) return Array.from({ length: Math.max(0, pageCount) }, (_, i) => i + 1);\n const pages = new Set<number>();\n for (let i = 1; i <= Math.min(boundaries, pageCount); i += 1) pages.add(i);\n for (let i = Math.max(1, pageCount - boundaries + 1); i <= pageCount; i += 1) pages.add(i);\n for (let i = Math.max(1, page - siblings); i <= Math.min(pageCount, page + siblings); i += 1) pages.add(i);\n const sorted = [...pages].sort((a, b) => a - b);\n const out: (number | \"…\")[] = [];\n sorted.forEach((n, i) => {\n const prev = sorted[i - 1];\n if (prev !== undefined && n - prev === 2) out.push(prev + 1);\n else if (prev !== undefined && n - prev > 2) out.push(\"…\");\n out.push(n);\n });\n return out;\n}\n\nexport type PaginationProps = Omit<React.ComponentProps<\"nav\">, \"children\"> & {\n /** Pages mode: the current page, from 1. */\n page?: number;\n /** Pages mode: how many pages there are. Without it, the pagination is in cursor mode. */\n pageCount?: number;\n /** Called with the page asked for (buttons). Ignored for the pages `getHref` turns into links. */\n onPageChange?: (page: number) => void;\n /** The address of a page: the pages become links (crawlable, shareable, the state in the URL). */\n getHref?: (page: number) => string;\n /** Cursor mode: whether there is a previous page. */\n hasPrevious?: boolean;\n /** Cursor mode: whether there is a next page. */\n hasNext?: boolean;\n /** Cursor mode: called by the previous control. */\n onPrevious?: () => void;\n /** Cursor mode: called by the next control. */\n onNext?: () => void;\n /** Pages shown on each side of the current one. Default 1. */\n siblings?: number;\n /** Pages always shown at each end. Default 1. */\n boundaries?: number;\n /** A text beside the controls, read as is: \"41–60 of 1,284\", \"12 results\". */\n status?: React.ReactNode;\n /** The page size, with `pageSizeOptions` and `onPageSizeChange`: a select of rows per page. */\n pageSize?: number;\n /** The sizes offered (`[10, 20, 50]`). */\n pageSizeOptions?: readonly number[];\n /** Called with the size chosen. */\n onPageSizeChange?: (size: number) => void;\n /** Disables every control (a page is loading). */\n disabled?: boolean;\n /** The words, passed translated; English by default. */\n labels?: Partial<PaginationLabels>;\n};\n\ntype ItemProps = {\n href?: string;\n onClick?: () => void;\n disabled?: boolean;\n current?: boolean;\n label: string;\n className: string;\n children: React.ReactNode;\n};\n\nfunction Item({ href, onClick, disabled, current, label, className, children }: ItemProps) {\n const common = { \"aria-label\": label, \"aria-current\": current ? (\"page\" as const) : undefined, className };\n if (href && !disabled) {\n return (\n <a href={href} onClick={onClick} {...common}>\n {children}\n </a>\n );\n }\n if (href) {\n return (\n <a role=\"link\" aria-disabled {...common}>\n {children}\n </a>\n );\n }\n return (\n <button type=\"button\" onClick={onClick} disabled={disabled} {...common}>\n {children}\n </button>\n );\n}\n\n/** Moves through a long list: page numbers (folded on a narrow screen) or previous / next on a cursor. */\nexport function Pagination({\n page = 1,\n pageCount,\n onPageChange,\n getHref,\n hasPrevious,\n hasNext,\n onPrevious,\n onNext,\n siblings = 1,\n boundaries = 1,\n status,\n pageSize,\n pageSizeOptions,\n onPageSizeChange,\n disabled,\n labels: given,\n className,\n ...props\n}: PaginationProps) {\n const s = pagination();\n const labels = { ...paginationLabels, ...given };\n const paged = pageCount !== undefined;\n const count = Math.max(1, pageCount ?? 1);\n const current = Math.min(Math.max(1, page), count);\n const canPrevious = paged ? current > 1 : Boolean(hasPrevious);\n const canNext = paged ? current < count : Boolean(hasNext);\n const go = (target: number) => () => onPageChange?.(target);\n const href = (target: number) => (paged && getHref ? getHref(target) : undefined);\n\n return (\n <nav aria-label={labels.nav} className={s.root({ className })} {...props}>\n {status !== undefined || pageSizeOptions ? (\n <div className=\"flex flex-wrap items-center gap-4\">\n {status !== undefined && <p className={s.status()}>{status}</p>}\n {pageSizeOptions && pageSize !== undefined && (\n <label className={s.size()}>\n {labels.pageSize}\n <select\n className={s.select()}\n value={pageSize}\n disabled={disabled}\n onChange={(event) => onPageSizeChange?.(Number(event.target.value))}\n >\n {pageSizeOptions.map((size) => (\n <option key={size} value={size}>\n {size}\n </option>\n ))}\n </select>\n </label>\n )}\n </div>\n ) : null}\n <ul className={s.list()}>\n <li>\n <Item\n href={href(current - 1)}\n onClick={paged ? go(current - 1) : onPrevious}\n disabled={disabled || !canPrevious}\n label={labels.previous}\n className={s.item({ className: s.edge() })}\n >\n <ChevronGlyph direction=\"left\" />\n <span className=\"hidden @lg:inline\">{labels.previous}</span>\n </Item>\n </li>\n {paged && (\n <>\n <li className={s.pages()}>\n <ul className=\"contents\">\n {pageRange(current, count, siblings, boundaries).map((n, i) =>\n n === \"…\" ? (\n <li key={`gap-${i}`} aria-hidden className={s.ellipsis()}>\n …\n </li>\n ) : (\n <li key={n}>\n <Item href={href(n)} onClick={go(n)} disabled={disabled} current={n === current} label={labels.page(n)} className={s.item()}>\n {n}\n </Item>\n </li>\n ),\n )}\n </ul>\n </li>\n <li className={s.compact()}>\n {labels.compact(current, count)}\n </li>\n </>\n )}\n <li>\n <Item\n href={href(current + 1)}\n onClick={paged ? go(current + 1) : onNext}\n disabled={disabled || !canNext}\n label={labels.next}\n className={s.item({ className: s.edge() })}\n >\n <span className=\"hidden @lg:inline\">{labels.next}</span>\n <ChevronGlyph direction=\"right\" />\n </Item>\n </li>\n </ul>\n </nav>\n );\n}\n","// The small line glyphs the data and media primitives draw (play, volume, search, chevrons…): decorative, 24-unit\n// box, `currentColor`, sized by the caller's class. Shared by the entries that need them; never exported on its own —\n// a product draws its own icons with @krizaka/icons.\nimport type * as React from \"react\";\n\ntype GlyphProps = { className?: string };\n\nfunction Glyph({ className = \"h-4 w-4\", fill, children }: GlyphProps & { fill?: boolean; children: React.ReactNode }) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n className={className}\n fill={fill ? \"currentColor\" : \"none\"}\n stroke={fill ? \"none\" : \"currentColor\"}\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n focusable=\"false\"\n >\n {children}\n </svg>\n );\n}\n\nexport const PlayGlyph = (p: GlyphProps) => (\n <Glyph {...p} fill>\n <path d=\"M8 5.5v13a1 1 0 0 0 1.5.86l10.4-6.5a1 1 0 0 0 0-1.72L9.5 4.64A1 1 0 0 0 8 5.5Z\" />\n </Glyph>\n);\nexport const PauseGlyph = (p: GlyphProps) => (\n <Glyph {...p} fill>\n <rect x=\"6\" y=\"5\" width=\"4\" height=\"14\" rx=\"1\" />\n <rect x=\"14\" y=\"5\" width=\"4\" height=\"14\" rx=\"1\" />\n </Glyph>\n);\nexport const VolumeGlyph = ({ muted, ...p }: GlyphProps & { muted?: boolean }) => (\n <Glyph {...p}>\n <path d=\"M11 5 6 9H3v6h3l5 4V5Z\" />\n {muted ? <path d=\"m22 9-6 6m0-6 6 6\" /> : <path d=\"M15.5 8.5a5 5 0 0 1 0 7M18.5 5.5a9 9 0 0 1 0 13\" />}\n </Glyph>\n);\nexport const FullscreenGlyph = ({ exit, ...p }: GlyphProps & { exit?: boolean }) => (\n <Glyph {...p}>\n {exit ? <path d=\"M9 4v5H4m11-5v5h5M9 20v-5H4m11 5v-5h5\" /> : <path d=\"M4 9V4h5m6 0h5v5M4 15v5h5m11-5v5h-5\" />}\n </Glyph>\n);\nexport const CaptionsGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"2\" />\n <path d=\"M10 10.5a2 2 0 1 0 0 3M17 10.5a2 2 0 1 0 0 3\" />\n </Glyph>\n);\nexport const PipGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"2\" />\n <rect x=\"12\" y=\"12\" width=\"6\" height=\"4\" rx=\"1\" />\n </Glyph>\n);\nexport const SearchGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <circle cx=\"11\" cy=\"11\" r=\"7\" />\n <path d=\"m20 20-3.5-3.5\" />\n </Glyph>\n);\nexport const CloseGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </Glyph>\n);\nexport const ChevronGlyph = ({ direction = \"down\", ...p }: GlyphProps & { direction?: \"left\" | \"right\" | \"up\" | \"down\" }) => (\n <Glyph {...p}>\n <path d={{ left: \"m15 6-6 6 6 6\", right: \"m9 6 6 6-6 6\", up: \"m6 15 6-6 6 6\", down: \"m6 9 6 6 6-6\" }[direction]} />\n </Glyph>\n);\nexport const CheckGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M20 6 9 17l-5-5\" />\n </Glyph>\n);\nexport const PlusGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M12 5v14M5 12h14\" />\n </Glyph>\n);\nexport const CalendarGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <rect x=\"3\" y=\"5\" width=\"18\" height=\"16\" rx=\"2\" />\n <path d=\"M3 10h18M8 3v4M16 3v4\" />\n </Glyph>\n);\nexport const UploadGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M12 16V4m0 0L7 9m5-5 5 5\" />\n <path d=\"M4 16v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2\" />\n </Glyph>\n);\nexport const FileGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8l-5-5Z\" />\n <path d=\"M14 3v5h5\" />\n </Glyph>\n);\nexport const CameraGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M4 8h3l2-3h6l2 3h3a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1Z\" />\n <circle cx=\"12\" cy=\"13.5\" r=\"3.5\" />\n </Glyph>\n);\nexport const FlipGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M4 12a8 8 0 0 1 14-5.3M20 12a8 8 0 0 1-14 5.3\" />\n <path d=\"M18 3v4h-4M6 21v-4h4\" />\n </Glyph>\n);\nexport const RetryGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M4 12a8 8 0 1 0 2.3-5.7L4 8.5\" />\n <path d=\"M4 4v4.5h4.5\" />\n </Glyph>\n);\nexport const SortGlyph = ({ direction, ...p }: GlyphProps & { direction?: \"ascending\" | \"descending\" }) => (\n <Glyph {...p}>\n {direction === \"ascending\" ? <path d=\"m7 14 5-5 5 5\" /> : direction === \"descending\" ? <path d=\"m7 10 5 5 5-5\" /> : <path d=\"m8 9 4-4 4 4M8 15l4 4 4-4\" />}\n </Glyph>\n);\n"],"mappings":"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","names":["jsx","jsxs"]}
@@ -0,0 +1,100 @@
1
+ import * as tailwind_variants from 'tailwind-variants';
2
+ import * as React from 'react';
3
+
4
+ declare const searchField: tailwind_variants.TVReturnType<{
5
+ size: {
6
+ md: {};
7
+ sm: {
8
+ control: string;
9
+ clear: string;
10
+ };
11
+ };
12
+ }, {
13
+ root: string;
14
+ control: string;
15
+ icon: string;
16
+ input: string;
17
+ clear: string;
18
+ shortcut: string;
19
+ list: string;
20
+ option: string;
21
+ group: string;
22
+ }, undefined, {
23
+ size: {
24
+ md: {};
25
+ sm: {
26
+ control: string;
27
+ clear: string;
28
+ };
29
+ };
30
+ }, {
31
+ root: string;
32
+ control: string;
33
+ icon: string;
34
+ input: string;
35
+ clear: string;
36
+ shortcut: string;
37
+ list: string;
38
+ option: string;
39
+ group: string;
40
+ }, tailwind_variants.TVReturnTypeLike<{
41
+ size: {
42
+ md: {};
43
+ sm: {
44
+ control: string;
45
+ clear: string;
46
+ };
47
+ };
48
+ }, {
49
+ root: string;
50
+ control: string;
51
+ icon: string;
52
+ input: string;
53
+ clear: string;
54
+ shortcut: string;
55
+ list: string;
56
+ option: string;
57
+ group: string;
58
+ }>>;
59
+ type SearchSuggestion = {
60
+ /** What `onSuggestionSelect` receives. */
61
+ value: string;
62
+ /** What the person reads. */
63
+ label: string;
64
+ /** A small text on the right ("Creator", "12 videos"). */
65
+ meta?: string;
66
+ };
67
+ type SearchFieldProps = Omit<React.ComponentProps<"input">, "value" | "defaultValue" | "onChange" | "onSubmit" | "size" | "type"> & {
68
+ /** The accessible name of the input ("Search videos"), passed translated. */
69
+ label: string;
70
+ /** The query (controlled). */
71
+ value?: string;
72
+ /** The query at first, uncontrolled. */
73
+ defaultValue?: string;
74
+ /** Called on every key with the query. */
75
+ onValueChange?: (query: string) => void;
76
+ /** Called with the query once typing pauses (`debounce` ms): fetch the results here. */
77
+ onSearch?: (query: string) => void;
78
+ /** The pause, in ms, before `onSearch`. Default 250. */
79
+ debounce?: number;
80
+ /** Called on Enter (no suggestion active) with the query: go to the results page. */
81
+ onSubmit?: (query: string) => void;
82
+ /** The global key that focuses the field with ⌘ / Ctrl (`"k"`), or `false` for none. Default `false`. */
83
+ shortcut?: string | false;
84
+ /** Suggestions under the field (recent searches, completions). */
85
+ suggestions?: readonly SearchSuggestion[];
86
+ /** A title above the suggestions ("Recent searches"). */
87
+ suggestionsLabel?: string;
88
+ /** Called with a suggestion picked (Enter on it, or a click). */
89
+ onSuggestionSelect?: (suggestion: SearchSuggestion) => void;
90
+ /** The clear button's name ("Clear search"), passed translated. */
91
+ clearLabel?: string;
92
+ /** Wraps the field in a `role="search"` landmark (default) — `false` inside a form that already is one. */
93
+ landmark?: boolean;
94
+ /** md (default) · sm. */
95
+ size?: "md" | "sm";
96
+ };
97
+ /** A search box: clear, shortcut, debounced search, submit on Enter, suggestions with the keyboard. */
98
+ declare function SearchField({ label, value: valueProp, defaultValue, onValueChange, onSearch, debounce, onSubmit, shortcut, suggestions, suggestionsLabel, onSuggestionSelect, clearLabel, landmark, size, className, placeholder, ...props }: SearchFieldProps): React.JSX.Element;
99
+
100
+ export { SearchField, type SearchFieldProps, type SearchSuggestion, searchField };
@@ -0,0 +1,167 @@
1
+ "use client";
2
+ import {
3
+ useListbox
4
+ } from "./chunk-ZIXB33X7.js";
5
+ import {
6
+ useControllable,
7
+ useDebouncedCallback
8
+ } from "./chunk-EUPTXKH7.js";
9
+ import {
10
+ CloseGlyph,
11
+ SearchGlyph
12
+ } from "./chunk-YX6CNVJA.js";
13
+
14
+ // src/search-field/search-field.tsx
15
+ import * as React from "react";
16
+ import { tv as tv2 } from "tailwind-variants";
17
+
18
+ // src/kbd/kbd.tsx
19
+ import { tv } from "tailwind-variants";
20
+ import { jsx } from "react/jsx-runtime";
21
+ var kbdVariants = tv({
22
+ base: "inline-flex shrink-0 items-center justify-center rounded-md border border-border-default border-b-2 bg-surface-2 font-mono font-semibold text-fg-secondary",
23
+ variants: { size: { sm: "h-5 min-w-5 px-1 text-[10px]", md: "h-6 min-w-6 px-1.5 text-xs" } },
24
+ defaultVariants: { size: "md" }
25
+ });
26
+ function Kbd({ size, className, ...props }) {
27
+ return /* @__PURE__ */ jsx("kbd", { className: kbdVariants({ size, className }), ...props });
28
+ }
29
+
30
+ // src/search-field/search-field.tsx
31
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
32
+ var searchField = tv2({
33
+ slots: {
34
+ root: "relative w-full",
35
+ control: "flex h-11 w-full items-center gap-2 rounded-lg border border-border-default bg-surface-2 pl-3 pr-1.5 text-sm text-fg transition-[border-color,box-shadow] focus-within:border-accent focus-within:ring-2 focus-within:ring-ring",
36
+ icon: "h-4 w-4 shrink-0 text-fg-secondary",
37
+ input: "h-full min-w-0 flex-1 bg-transparent text-sm text-fg outline-hidden placeholder:text-fg-muted [&::-webkit-search-cancel-button]:appearance-none",
38
+ clear: "inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-fg-secondary hover:bg-surface-3 hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
39
+ shortcut: "hidden shrink-0 items-center gap-0.5 pr-1 sm:flex",
40
+ list: "absolute inset-x-0 top-full z-50 mt-1 max-h-72 overflow-y-auto rounded-lg border border-border-default bg-surface-2 p-1 shadow-lg",
41
+ option: "flex cursor-pointer select-none items-center gap-2.5 rounded-md px-2.5 py-2 text-sm text-fg data-[active]:bg-surface-3",
42
+ group: "px-2.5 pb-1 pt-2 text-[11px] font-semibold uppercase text-fg-muted"
43
+ },
44
+ variants: {
45
+ size: { md: {}, sm: { control: "h-9", clear: "h-7 w-7" } }
46
+ },
47
+ defaultVariants: { size: "md" }
48
+ });
49
+ var isApple = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);
50
+ function SearchField({
51
+ label,
52
+ value: valueProp,
53
+ defaultValue = "",
54
+ onValueChange,
55
+ onSearch,
56
+ debounce = 250,
57
+ onSubmit,
58
+ shortcut = false,
59
+ suggestions,
60
+ suggestionsLabel,
61
+ onSuggestionSelect,
62
+ clearLabel = "Clear search",
63
+ landmark = true,
64
+ size,
65
+ className,
66
+ placeholder,
67
+ ...props
68
+ }) {
69
+ const s = searchField({ size });
70
+ const [value, setValue] = useControllable(valueProp, defaultValue, onValueChange);
71
+ const [open, setOpen] = React.useState(false);
72
+ const [apple, setApple] = React.useState(false);
73
+ const input = React.useRef(null);
74
+ const search = useDebouncedCallback((query) => onSearch?.(query), debounce);
75
+ const list = suggestions ?? [];
76
+ const pick = (i) => {
77
+ const suggestion = list[i];
78
+ if (!suggestion) return;
79
+ setOpen(false);
80
+ onSuggestionSelect?.(suggestion);
81
+ };
82
+ const listbox = useListbox({ count: list.length, onPick: pick, open: open && list.length > 0, setOpen });
83
+ React.useEffect(() => {
84
+ if (!shortcut) return;
85
+ setApple(isApple());
86
+ const onKey = (event) => {
87
+ if (event.key.toLowerCase() === shortcut.toLowerCase() && (event.metaKey || event.ctrlKey) && !event.altKey) {
88
+ event.preventDefault();
89
+ input.current?.focus();
90
+ input.current?.select();
91
+ }
92
+ };
93
+ window.addEventListener("keydown", onKey);
94
+ return () => window.removeEventListener("keydown", onKey);
95
+ }, [shortcut]);
96
+ const change = (query) => {
97
+ setValue(query);
98
+ search(query);
99
+ listbox.setActive(-1);
100
+ setOpen(true);
101
+ };
102
+ const field = /* @__PURE__ */ jsxs("div", { className: s.root({ className }), onBlur: (event) => !event.currentTarget.contains(event.relatedTarget) && setOpen(false), children: [
103
+ /* @__PURE__ */ jsxs("div", { className: s.control(), children: [
104
+ /* @__PURE__ */ jsx2(SearchGlyph, { className: s.icon() }),
105
+ /* @__PURE__ */ jsx2(
106
+ "input",
107
+ {
108
+ ref: input,
109
+ type: "search",
110
+ "aria-label": label,
111
+ enterKeyHint: "search",
112
+ autoComplete: "off",
113
+ ...list.length > 0 ? listbox.inputProps : {},
114
+ value,
115
+ placeholder,
116
+ className: s.input(),
117
+ onChange: (event) => change(event.target.value),
118
+ onFocus: () => setOpen(true),
119
+ onKeyDown: (event) => {
120
+ if (list.length > 0 && listbox.onKeyDown(event)) return;
121
+ if (event.key === "Enter") {
122
+ event.preventDefault();
123
+ search.cancel();
124
+ onSearch?.(value);
125
+ onSubmit?.(value);
126
+ setOpen(false);
127
+ } else if (event.key === "Escape" && value) {
128
+ event.preventDefault();
129
+ change("");
130
+ }
131
+ },
132
+ ...props
133
+ }
134
+ ),
135
+ value ? /* @__PURE__ */ jsx2(
136
+ "button",
137
+ {
138
+ type: "button",
139
+ "aria-label": clearLabel,
140
+ className: s.clear(),
141
+ onClick: () => {
142
+ change("");
143
+ input.current?.focus();
144
+ },
145
+ children: /* @__PURE__ */ jsx2(CloseGlyph, { className: "h-4 w-4" })
146
+ }
147
+ ) : shortcut ? /* @__PURE__ */ jsxs("span", { className: s.shortcut(), "aria-hidden": true, children: [
148
+ /* @__PURE__ */ jsx2(Kbd, { size: "sm", children: apple ? "\u2318" : "Ctrl" }),
149
+ /* @__PURE__ */ jsx2(Kbd, { size: "sm", children: shortcut.toUpperCase() })
150
+ ] }) : null
151
+ ] }),
152
+ list.length > 0 && /* @__PURE__ */ jsxs("div", { hidden: !open, className: s.list(), children: [
153
+ suggestionsLabel ? /* @__PURE__ */ jsx2("div", { "aria-hidden": true, className: s.group(), children: suggestionsLabel }) : null,
154
+ /* @__PURE__ */ jsx2("div", { id: listbox.listId, role: "listbox", "aria-label": suggestionsLabel ?? label, children: list.map((suggestion, i) => /* @__PURE__ */ jsxs("div", { ...listbox.optionProps(i, false), className: s.option(), children: [
155
+ /* @__PURE__ */ jsx2(SearchGlyph, { className: "h-3.5 w-3.5 shrink-0 text-fg-muted" }),
156
+ /* @__PURE__ */ jsx2("span", { className: "min-w-0 flex-1 truncate", children: suggestion.label }),
157
+ suggestion.meta ? /* @__PURE__ */ jsx2("span", { className: "shrink-0 text-xs text-fg-secondary", children: suggestion.meta }) : null
158
+ ] }, suggestion.value)) })
159
+ ] })
160
+ ] });
161
+ return landmark ? /* @__PURE__ */ jsx2("div", { role: "search", children: field }) : field;
162
+ }
163
+ export {
164
+ SearchField,
165
+ searchField
166
+ };
167
+ //# sourceMappingURL=search-field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/search-field/search-field.tsx","../src/kbd/kbd.tsx"],"sourcesContent":["// Client. A search input (`type=\"search\"` inside a `role=\"search\"` landmark when it stands alone): a leading glyph,\n// a clear button, a global shortcut that focuses it (⌘K on Apple devices, Ctrl+K elsewhere, shown as `Kbd`), and\n// optional suggestions through the shared combobox engine. `onSearch` is debounced; Enter submits.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { CloseGlyph, SearchGlyph } from \"../glyphs\";\nimport { Kbd } from \"../kbd\";\nimport { useListbox } from \"../listbox\";\nimport { useControllable, useDebouncedCallback } from \"../media\";\n\nexport const searchField = tv({\n slots: {\n root: \"relative w-full\",\n control:\n \"flex h-11 w-full items-center gap-2 rounded-lg border border-border-default bg-surface-2 pl-3 pr-1.5 text-sm text-fg \" +\n \"transition-[border-color,box-shadow] focus-within:border-accent focus-within:ring-2 focus-within:ring-ring\",\n icon: \"h-4 w-4 shrink-0 text-fg-secondary\",\n input:\n \"h-full min-w-0 flex-1 bg-transparent text-sm text-fg outline-hidden placeholder:text-fg-muted \" +\n \"[&::-webkit-search-cancel-button]:appearance-none\",\n clear:\n \"inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-fg-secondary hover:bg-surface-3 hover:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n shortcut: \"hidden shrink-0 items-center gap-0.5 pr-1 sm:flex\",\n list: \"absolute inset-x-0 top-full z-50 mt-1 max-h-72 overflow-y-auto rounded-lg border border-border-default bg-surface-2 p-1 shadow-lg\",\n option: \"flex cursor-pointer select-none items-center gap-2.5 rounded-md px-2.5 py-2 text-sm text-fg data-[active]:bg-surface-3\",\n group: \"px-2.5 pb-1 pt-2 text-[11px] font-semibold uppercase text-fg-muted\",\n },\n variants: {\n size: { md: {}, sm: { control: \"h-9\", clear: \"h-7 w-7\" } },\n },\n defaultVariants: { size: \"md\" },\n});\n\nexport type SearchSuggestion = {\n /** What `onSuggestionSelect` receives. */\n value: string;\n /** What the person reads. */\n label: string;\n /** A small text on the right (\"Creator\", \"12 videos\"). */\n meta?: string;\n};\n\nexport type SearchFieldProps = Omit<React.ComponentProps<\"input\">, \"value\" | \"defaultValue\" | \"onChange\" | \"onSubmit\" | \"size\" | \"type\"> & {\n /** The accessible name of the input (\"Search videos\"), passed translated. */\n label: string;\n /** The query (controlled). */\n value?: string;\n /** The query at first, uncontrolled. */\n defaultValue?: string;\n /** Called on every key with the query. */\n onValueChange?: (query: string) => void;\n /** Called with the query once typing pauses (`debounce` ms): fetch the results here. */\n onSearch?: (query: string) => void;\n /** The pause, in ms, before `onSearch`. Default 250. */\n debounce?: number;\n /** Called on Enter (no suggestion active) with the query: go to the results page. */\n onSubmit?: (query: string) => void;\n /** The global key that focuses the field with ⌘ / Ctrl (`\"k\"`), or `false` for none. Default `false`. */\n shortcut?: string | false;\n /** Suggestions under the field (recent searches, completions). */\n suggestions?: readonly SearchSuggestion[];\n /** A title above the suggestions (\"Recent searches\"). */\n suggestionsLabel?: string;\n /** Called with a suggestion picked (Enter on it, or a click). */\n onSuggestionSelect?: (suggestion: SearchSuggestion) => void;\n /** The clear button's name (\"Clear search\"), passed translated. */\n clearLabel?: string;\n /** Wraps the field in a `role=\"search\"` landmark (default) — `false` inside a form that already is one. */\n landmark?: boolean;\n /** md (default) · sm. */\n size?: \"md\" | \"sm\";\n};\n\nconst isApple = () => typeof navigator !== \"undefined\" && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);\n\n/** A search box: clear, shortcut, debounced search, submit on Enter, suggestions with the keyboard. */\nexport function SearchField({\n label,\n value: valueProp,\n defaultValue = \"\",\n onValueChange,\n onSearch,\n debounce = 250,\n onSubmit,\n shortcut = false,\n suggestions,\n suggestionsLabel,\n onSuggestionSelect,\n clearLabel = \"Clear search\",\n landmark = true,\n size,\n className,\n placeholder,\n ...props\n}: SearchFieldProps) {\n const s = searchField({ size });\n const [value, setValue] = useControllable(valueProp, defaultValue, onValueChange);\n const [open, setOpen] = React.useState(false);\n const [apple, setApple] = React.useState(false);\n const input = React.useRef<HTMLInputElement>(null);\n const search = useDebouncedCallback((query: string) => onSearch?.(query), debounce);\n const list = suggestions ?? [];\n\n const pick = (i: number) => {\n const suggestion = list[i];\n if (!suggestion) return;\n setOpen(false);\n onSuggestionSelect?.(suggestion);\n };\n const listbox = useListbox({ count: list.length, onPick: pick, open: open && list.length > 0, setOpen });\n\n React.useEffect(() => {\n if (!shortcut) return;\n // eslint-disable-next-line react-hooks/set-state-in-effect -- the platform is only known in the browser (no hydration mismatch)\n setApple(isApple());\n const onKey = (event: KeyboardEvent) => {\n if (event.key.toLowerCase() === shortcut.toLowerCase() && (event.metaKey || event.ctrlKey) && !event.altKey) {\n event.preventDefault();\n input.current?.focus();\n input.current?.select();\n }\n };\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [shortcut]);\n\n const change = (query: string) => {\n setValue(query);\n search(query);\n listbox.setActive(-1);\n setOpen(true);\n };\n\n const field = (\n <div className={s.root({ className })} onBlur={(event) => !event.currentTarget.contains(event.relatedTarget as Node | null) && setOpen(false)}>\n <div className={s.control()}>\n <SearchGlyph className={s.icon()} />\n <input\n ref={input}\n type=\"search\"\n aria-label={label}\n enterKeyHint=\"search\"\n autoComplete=\"off\"\n {...(list.length > 0 ? listbox.inputProps : {})}\n value={value}\n placeholder={placeholder}\n className={s.input()}\n onChange={(event) => change(event.target.value)}\n onFocus={() => setOpen(true)}\n onKeyDown={(event) => {\n if (list.length > 0 && listbox.onKeyDown(event)) return;\n if (event.key === \"Enter\") {\n event.preventDefault();\n search.cancel();\n onSearch?.(value);\n onSubmit?.(value);\n setOpen(false);\n } else if (event.key === \"Escape\" && value) {\n event.preventDefault();\n change(\"\");\n }\n }}\n {...props}\n />\n {value ? (\n <button\n type=\"button\"\n aria-label={clearLabel}\n className={s.clear()}\n onClick={() => {\n change(\"\");\n input.current?.focus();\n }}\n >\n <CloseGlyph className=\"h-4 w-4\" />\n </button>\n ) : shortcut ? (\n <span className={s.shortcut()} aria-hidden>\n <Kbd size=\"sm\">{apple ? \"⌘\" : \"Ctrl\"}</Kbd>\n <Kbd size=\"sm\">{shortcut.toUpperCase()}</Kbd>\n </span>\n ) : null}\n </div>\n {list.length > 0 && (\n <div hidden={!open} className={s.list()}>\n {suggestionsLabel ? (\n <div aria-hidden className={s.group()}>\n {suggestionsLabel}\n </div>\n ) : null}\n <div id={listbox.listId} role=\"listbox\" aria-label={suggestionsLabel ?? label}>\n {list.map((suggestion, i) => (\n <div key={suggestion.value} {...listbox.optionProps(i, false)} className={s.option()}>\n <SearchGlyph className=\"h-3.5 w-3.5 shrink-0 text-fg-muted\" />\n <span className=\"min-w-0 flex-1 truncate\">{suggestion.label}</span>\n {suggestion.meta ? <span className=\"shrink-0 text-xs text-fg-secondary\">{suggestion.meta}</span> : null}\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n );\n return landmark ? <div role=\"search\">{field}</div> : field;\n}\n","// Server-safe. A key, or a shortcut of several (`<Kbd>⌘</Kbd><Kbd>K</Kbd>`).\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nexport const kbdVariants = tv({\n base:\n \"inline-flex shrink-0 items-center justify-center rounded-md border border-border-default border-b-2 bg-surface-2 \" +\n \"font-mono font-semibold text-fg-secondary\",\n variants: { size: { sm: \"h-5 min-w-5 px-1 text-[10px]\", md: \"h-6 min-w-6 px-1.5 text-xs\" } },\n defaultVariants: { size: \"md\" },\n});\n\nexport type KbdProps = React.ComponentProps<\"kbd\"> & {\n /**\n * sm · md.\n * @default \"md\"\n */\n size?: VariantProps<typeof kbdVariants>[\"size\"];\n};\n\nexport function Kbd({ size, className, ...props }: KbdProps) {\n return <kbd className={kbdVariants({ size, className })} {...props} />;\n}\n"],"mappings":"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","names":["tv","jsx","tv"]}