@ayasofyazilim/ui 0.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 (236) hide show
  1. package/__mocks__/canvas.ts +8 -0
  2. package/components.json +21 -0
  3. package/eslint.config.js +4 -0
  4. package/jest-environment.js +37 -0
  5. package/jest.config.ts +47 -0
  6. package/jest.setup.ts +69 -0
  7. package/package.json +124 -0
  8. package/postcss.config.mjs +6 -0
  9. package/src/aria/index.tsx +1 -0
  10. package/src/aria/number-field.tsx +41 -0
  11. package/src/components/.gitkeep +0 -0
  12. package/src/components/accordion.tsx +66 -0
  13. package/src/components/alert-dialog.tsx +157 -0
  14. package/src/components/alert.tsx +70 -0
  15. package/src/components/aspect-ratio.tsx +11 -0
  16. package/src/components/avatar.tsx +53 -0
  17. package/src/components/badge.tsx +67 -0
  18. package/src/components/breadcrumb.tsx +109 -0
  19. package/src/components/button-group.tsx +83 -0
  20. package/src/components/button.tsx +68 -0
  21. package/src/components/calendar.tsx +219 -0
  22. package/src/components/card.tsx +92 -0
  23. package/src/components/carousel.tsx +241 -0
  24. package/src/components/chart.tsx +363 -0
  25. package/src/components/checkbox.tsx +32 -0
  26. package/src/components/collapsible.tsx +33 -0
  27. package/src/components/command.tsx +184 -0
  28. package/src/components/context-menu.tsx +252 -0
  29. package/src/components/dialog.tsx +144 -0
  30. package/src/components/drawer.tsx +135 -0
  31. package/src/components/dropdown-menu.tsx +258 -0
  32. package/src/components/empty.tsx +100 -0
  33. package/src/components/field.tsx +248 -0
  34. package/src/components/form.tsx +169 -0
  35. package/src/components/hover-card.tsx +44 -0
  36. package/src/components/input-group.tsx +170 -0
  37. package/src/components/input-otp.tsx +77 -0
  38. package/src/components/input.tsx +21 -0
  39. package/src/components/item.tsx +193 -0
  40. package/src/components/kbd.tsx +28 -0
  41. package/src/components/label.tsx +24 -0
  42. package/src/components/menubar.tsx +276 -0
  43. package/src/components/navigation-menu.tsx +168 -0
  44. package/src/components/pagination.tsx +130 -0
  45. package/src/components/popover.tsx +88 -0
  46. package/src/components/progress.tsx +31 -0
  47. package/src/components/radio-group.tsx +45 -0
  48. package/src/components/resizable.tsx +56 -0
  49. package/src/components/scroll-area.tsx +58 -0
  50. package/src/components/select.tsx +189 -0
  51. package/src/components/separator.tsx +28 -0
  52. package/src/components/sheet.tsx +140 -0
  53. package/src/components/sidebar.tsx +862 -0
  54. package/src/components/skeleton.tsx +13 -0
  55. package/src/components/slider.tsx +63 -0
  56. package/src/components/sonner.tsx +40 -0
  57. package/src/components/spinner.tsx +16 -0
  58. package/src/components/stepper.tsx +291 -0
  59. package/src/components/switch.tsx +31 -0
  60. package/src/components/table.tsx +133 -0
  61. package/src/components/tabs.tsx +66 -0
  62. package/src/components/textarea.tsx +18 -0
  63. package/src/components/toggle-group.tsx +83 -0
  64. package/src/components/toggle.tsx +47 -0
  65. package/src/components/tooltip.tsx +66 -0
  66. package/src/custom/action-button.tsx +48 -0
  67. package/src/custom/async-select.tsx +287 -0
  68. package/src/custom/awesome-not-found.tsx +116 -0
  69. package/src/custom/charts/area-chart.tsx +147 -0
  70. package/src/custom/charts/bar-chart.tsx +233 -0
  71. package/src/custom/charts/chart-card.tsx +103 -0
  72. package/src/custom/charts/index.tsx +16 -0
  73. package/src/custom/charts/pie-chart.tsx +168 -0
  74. package/src/custom/charts/radar-chart.tsx +126 -0
  75. package/src/custom/checkbox-tree.tsx +100 -0
  76. package/src/custom/combobox.tsx +296 -0
  77. package/src/custom/confirm-dialog.tsx +102 -0
  78. package/src/custom/country-selector.tsx +204 -0
  79. package/src/custom/date-picker/calendar-rac.tsx +109 -0
  80. package/src/custom/date-picker/datefield-rac.tsx +84 -0
  81. package/src/custom/date-picker/index.tsx +273 -0
  82. package/src/custom/date-picker/types/index.ts +4 -0
  83. package/src/custom/date-picker/utils/index.ts +42 -0
  84. package/src/custom/date-picker-old.tsx +50 -0
  85. package/src/custom/date-tooltip.tsx +98 -0
  86. package/src/custom/document-scanner/consts.ts +5 -0
  87. package/src/custom/document-scanner/corner-adjustment/action-buttons.tsx +33 -0
  88. package/src/custom/document-scanner/corner-adjustment/corner-handle.tsx +43 -0
  89. package/src/custom/document-scanner/corner-adjustment/hooks/use-corner-drag.ts +85 -0
  90. package/src/custom/document-scanner/corner-adjustment/index.tsx +125 -0
  91. package/src/custom/document-scanner/corner-adjustment/types.ts +53 -0
  92. package/src/custom/document-scanner/corner-adjustment/utils/clip-path.ts +22 -0
  93. package/src/custom/document-scanner/corner-adjustment/zoom-magnifier.tsx +115 -0
  94. package/src/custom/document-scanner/hooks/use-document-capture.ts +81 -0
  95. package/src/custom/document-scanner/hooks/use-document-scanner.ts +80 -0
  96. package/src/custom/document-scanner/hooks/use-perspective-crop.ts +38 -0
  97. package/src/custom/document-scanner/index.tsx +255 -0
  98. package/src/custom/document-scanner/lib.ts +407 -0
  99. package/src/custom/document-scanner/types.ts +205 -0
  100. package/src/custom/document-scanner/utils/perspective-correction.ts +139 -0
  101. package/src/custom/document-viewer/controllers.tsx +98 -0
  102. package/src/custom/document-viewer/index.tsx +43 -0
  103. package/src/custom/document-viewer/renderers/image.tsx +37 -0
  104. package/src/custom/document-viewer/renderers/index.tsx +2 -0
  105. package/src/custom/document-viewer/renderers/pdf.tsx +105 -0
  106. package/src/custom/email-input/domains.json +159 -0
  107. package/src/custom/email-input/email.tsx +229 -0
  108. package/src/custom/email-input/index.tsx +4 -0
  109. package/src/custom/email-input/types.ts +104 -0
  110. package/src/custom/file-uploader.tsx +541 -0
  111. package/src/custom/filter-component/fields/async-select.tsx +33 -0
  112. package/src/custom/filter-component/fields/date.tsx +60 -0
  113. package/src/custom/filter-component/fields/multi-select.tsx +30 -0
  114. package/src/custom/filter-component/index.tsx +217 -0
  115. package/src/custom/image-canvas.tsx +260 -0
  116. package/src/custom/json-editor.tsx +22 -0
  117. package/src/custom/master-data-grid/components/dialogs/column-settings-dialog.tsx +100 -0
  118. package/src/custom/master-data-grid/components/dialogs/index.ts +1 -0
  119. package/src/custom/master-data-grid/components/filters/client-filter.tsx +368 -0
  120. package/src/custom/master-data-grid/components/filters/filter-input.tsx +256 -0
  121. package/src/custom/master-data-grid/components/filters/index.ts +3 -0
  122. package/src/custom/master-data-grid/components/filters/inline-column-filter.tsx +233 -0
  123. package/src/custom/master-data-grid/components/filters/multi-filter-dialog.tsx +90 -0
  124. package/src/custom/master-data-grid/components/filters/server-filter.tsx +255 -0
  125. package/src/custom/master-data-grid/components/master-data-grid.tsx +472 -0
  126. package/src/custom/master-data-grid/components/pagination/index.ts +1 -0
  127. package/src/custom/master-data-grid/components/pagination/pagination.tsx +178 -0
  128. package/src/custom/master-data-grid/components/table/cell-renderer.tsx +634 -0
  129. package/src/custom/master-data-grid/components/table/header-cell.tsx +162 -0
  130. package/src/custom/master-data-grid/components/table/index.ts +4 -0
  131. package/src/custom/master-data-grid/components/table/table-body-renderer.tsx +113 -0
  132. package/src/custom/master-data-grid/components/table/virtual-body.tsx +138 -0
  133. package/src/custom/master-data-grid/components/toolbar/index.ts +1 -0
  134. package/src/custom/master-data-grid/components/toolbar/toolbar.tsx +314 -0
  135. package/src/custom/master-data-grid/hooks/index.ts +3 -0
  136. package/src/custom/master-data-grid/hooks/use-columns.tsx +332 -0
  137. package/src/custom/master-data-grid/hooks/use-editing.ts +106 -0
  138. package/src/custom/master-data-grid/hooks/use-table-state-reducer.ts +157 -0
  139. package/src/custom/master-data-grid/hooks/use-table-state.ts +31 -0
  140. package/src/custom/master-data-grid/index.ts +16 -0
  141. package/src/custom/master-data-grid/types.ts +466 -0
  142. package/src/custom/master-data-grid/utils/column-generator.tsx +306 -0
  143. package/src/custom/master-data-grid/utils/export-utils.ts +67 -0
  144. package/src/custom/master-data-grid/utils/filter-fns.ts +290 -0
  145. package/src/custom/master-data-grid/utils/index.ts +8 -0
  146. package/src/custom/master-data-grid/utils/pinning-utils.ts +88 -0
  147. package/src/custom/master-data-grid/utils/translation-utils.ts +42 -0
  148. package/src/custom/multi-select.tsx +432 -0
  149. package/src/custom/password-input.tsx +194 -0
  150. package/src/custom/phone-input.tsx +172 -0
  151. package/src/custom/schema-form/custom/index.tsx +1 -0
  152. package/src/custom/schema-form/custom/label.tsx +53 -0
  153. package/src/custom/schema-form/fields/base-input-field.tsx +82 -0
  154. package/src/custom/schema-form/fields/field.tsx +67 -0
  155. package/src/custom/schema-form/fields/index.tsx +5 -0
  156. package/src/custom/schema-form/fields/object.tsx +12 -0
  157. package/src/custom/schema-form/fields/table-array/array-field-item.tsx +90 -0
  158. package/src/custom/schema-form/fields/table-array/array-field-template.tsx +115 -0
  159. package/src/custom/schema-form/index.tsx +259 -0
  160. package/src/custom/schema-form/templates/description.tsx +20 -0
  161. package/src/custom/schema-form/templates/index.tsx +2 -0
  162. package/src/custom/schema-form/templates/submit.tsx +32 -0
  163. package/src/custom/schema-form/types.ts +64 -0
  164. package/src/custom/schema-form/utils/index.ts +4 -0
  165. package/src/custom/schema-form/utils/schema-dependency.ts +655 -0
  166. package/src/custom/schema-form/utils/schemas.ts +289 -0
  167. package/src/custom/schema-form/utils/validation.ts +23 -0
  168. package/src/custom/schema-form/widgets/boolean.tsx +77 -0
  169. package/src/custom/schema-form/widgets/combobox.tsx +274 -0
  170. package/src/custom/schema-form/widgets/date.tsx +59 -0
  171. package/src/custom/schema-form/widgets/email.tsx +34 -0
  172. package/src/custom/schema-form/widgets/index.tsx +10 -0
  173. package/src/custom/schema-form/widgets/password.tsx +40 -0
  174. package/src/custom/schema-form/widgets/phone.tsx +40 -0
  175. package/src/custom/schema-form/widgets/select.tsx +105 -0
  176. package/src/custom/schema-form/widgets/selectable.tsx +25 -0
  177. package/src/custom/schema-form/widgets/string-array.tsx +296 -0
  178. package/src/custom/schema-form/widgets/url.tsx +56 -0
  179. package/src/custom/section-layout-v2.tsx +212 -0
  180. package/src/custom/select-tabs.tsx +109 -0
  181. package/src/custom/selectable.tsx +316 -0
  182. package/src/custom/stepper.tsx +236 -0
  183. package/src/custom/tab-layout.tsx +213 -0
  184. package/src/custom/tanstack-table/fields/index.tsx +12 -0
  185. package/src/custom/tanstack-table/fields/tanstack-table-action-dialogs.tsx +89 -0
  186. package/src/custom/tanstack-table/fields/tanstack-table-column-header.tsx +66 -0
  187. package/src/custom/tanstack-table/fields/tanstack-table-filter-date.tsx +180 -0
  188. package/src/custom/tanstack-table/fields/tanstack-table-filter-faceted.tsx +158 -0
  189. package/src/custom/tanstack-table/fields/tanstack-table-filter-text.tsx +76 -0
  190. package/src/custom/tanstack-table/fields/tanstack-table-pagination.tsx +136 -0
  191. package/src/custom/tanstack-table/fields/tanstack-table-plain-table.tsx +142 -0
  192. package/src/custom/tanstack-table/fields/tanstack-table-row-actions-confirmation.tsx +77 -0
  193. package/src/custom/tanstack-table/fields/tanstack-table-row-actions-custom-dialog.tsx +87 -0
  194. package/src/custom/tanstack-table/fields/tanstack-table-row-actions.tsx +151 -0
  195. package/src/custom/tanstack-table/fields/tanstack-table-table-actions-custom-dialog.tsx +88 -0
  196. package/src/custom/tanstack-table/fields/tanstack-table-table-actions-schemaform-dialog.tsx +47 -0
  197. package/src/custom/tanstack-table/fields/tanstack-table-toolbar.tsx +143 -0
  198. package/src/custom/tanstack-table/fields/tanstack-table-view-options.tsx +171 -0
  199. package/src/custom/tanstack-table/index.tsx +244 -0
  200. package/src/custom/tanstack-table/types/index.ts +328 -0
  201. package/src/custom/tanstack-table/utils/cell-with-actions.tsx +21 -0
  202. package/src/custom/tanstack-table/utils/column-names.ts +26 -0
  203. package/src/custom/tanstack-table/utils/columns-by-row-data.tsx +312 -0
  204. package/src/custom/tanstack-table/utils/editable-columns-by-row-data.tsx +219 -0
  205. package/src/custom/tanstack-table/utils/faceted-boolean-options.tsx +22 -0
  206. package/src/custom/tanstack-table/utils/index.tsx +10 -0
  207. package/src/custom/tanstack-table/utils/pinning-styles.ts +57 -0
  208. package/src/custom/tanstack-table/utils/table.tsx +83 -0
  209. package/src/custom/tanstack-table/utils/test-conditions.ts +17 -0
  210. package/src/custom/timeline.tsx +208 -0
  211. package/src/custom/tree.tsx +200 -0
  212. package/src/custom/tscanify/browser.ts +66 -0
  213. package/src/custom/tscanify/index.ts +51 -0
  214. package/src/custom/tscanify/tscanify-browser.ts +522 -0
  215. package/src/custom/tscanify/tscanify.ts +262 -0
  216. package/src/custom/tscanify/types.ts +22 -0
  217. package/src/custom/webcam.tsx +737 -0
  218. package/src/hooks/.gitkeep +0 -0
  219. package/src/hooks/use-callback-ref.ts +27 -0
  220. package/src/hooks/use-controllable-state.ts +67 -0
  221. package/src/hooks/use-debounce.ts +19 -0
  222. package/src/hooks/use-is-visible.ts +23 -0
  223. package/src/hooks/use-media-query.ts +21 -0
  224. package/src/hooks/use-mobile.ts +21 -0
  225. package/src/hooks/use-on-window-resize.ts +15 -0
  226. package/src/hooks/use-scroll.tsx +22 -0
  227. package/src/lib/utils.ts +61 -0
  228. package/src/lib/zod.ts +2 -0
  229. package/src/styles/core.css +57 -0
  230. package/src/styles/globals.css +130 -0
  231. package/src/test/email-input.test.tsx +217 -0
  232. package/src/test/password-input.test.tsx +92 -0
  233. package/src/test/select-tabs.test.tsx +302 -0
  234. package/src/test/selectable.test.tsx +1093 -0
  235. package/tsconfig.json +13 -0
  236. package/tsconfig.lint.json +8 -0
@@ -0,0 +1,204 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+
5
+ import { Badge } from "@repo/ayasofyazilim-ui/components/badge";
6
+ import { Button } from "@repo/ayasofyazilim-ui/components/button";
7
+ import {
8
+ Command,
9
+ CommandEmpty,
10
+ CommandGroup,
11
+ CommandInput,
12
+ CommandItem,
13
+ CommandList,
14
+ } from "@repo/ayasofyazilim-ui/components/command";
15
+ import {
16
+ Popover,
17
+ PopoverContent,
18
+ PopoverTrigger,
19
+ } from "@repo/ayasofyazilim-ui/components/popover";
20
+ import { ScrollArea } from "@repo/ayasofyazilim-ui/components/scroll-area";
21
+ import { cn } from "@repo/ayasofyazilim-ui/lib/utils";
22
+ import { Globe } from "lucide-react";
23
+
24
+ export const lang = {
25
+ searchText: "Find",
26
+ searchEmptyValue: "No country found.",
27
+ defaultValue: {
28
+ label: "Test",
29
+ value: "tr",
30
+ },
31
+ countries: [
32
+ {
33
+ cultureName: "en",
34
+ uiCultureName: "en",
35
+ displayName: "English",
36
+ flagIcon: "en",
37
+ isEnabled: true,
38
+ isDefaultLanguage: true,
39
+ concurrencyStamp: "1a736aee0303420f9394ce3a98523e34",
40
+ creationTime: "2024-03-21T08:43:33.6867177",
41
+ creatorId: null,
42
+ id: "75fe277d-5138-285d-8088-3a1171b61635",
43
+ extraProperties: {},
44
+ },
45
+ {
46
+ cultureName: "tr",
47
+ uiCultureName: "tr",
48
+ displayName: "Türkçe",
49
+ flagIcon: "tr",
50
+ isEnabled: true,
51
+ isDefaultLanguage: false,
52
+ concurrencyStamp: "095c8922c1b148bd9e161beca3c635ac",
53
+ creationTime: "2024-03-21T08:43:33.6892353",
54
+ creatorId: null,
55
+ id: "a8483e30-e0e6-e4e8-f2ff-3a1171b61638",
56
+ extraProperties: {},
57
+ },
58
+ ],
59
+ };
60
+ export type CountryItem = {
61
+ concurrencyStamp?: string;
62
+ creationTime?: string;
63
+ creatorId?: string | null;
64
+ cultureName?: string | null;
65
+ direction?: "rtl" | "ltr" | null;
66
+ displayName?: string | null;
67
+ extraProperties?: object | null;
68
+ flagIcon?: string | null;
69
+ id?: string | null;
70
+ isDefaultLanguage?: boolean;
71
+ isEnabled?: boolean;
72
+ twoLetterISOLanguageName?: string | null;
73
+ uiCultureName?: string | null;
74
+ };
75
+
76
+ type CountrySelectorProps = {
77
+ countries?: Array<CountryItem>;
78
+ defaultValue?: string;
79
+ menuAlign?: "start" | "center" | "end";
80
+ onValueChange?: (value: string) => void;
81
+ searchEmptyValue?: string;
82
+ searchText?: string;
83
+ showLabel?: boolean;
84
+ showFlag?: boolean;
85
+ tooltipText?: string;
86
+ className?: string;
87
+ };
88
+
89
+ export function CountrySelector({
90
+ searchText,
91
+ searchEmptyValue,
92
+ defaultValue,
93
+ menuAlign = "end",
94
+ showLabel = true,
95
+ showFlag = false,
96
+ countries = [],
97
+ onValueChange,
98
+ className,
99
+ }: CountrySelectorProps) {
100
+ const [open, setOpen] = React.useState(false);
101
+ const [value, setValue] = React.useState<string>(defaultValue || "");
102
+
103
+ function onSelect(currentValue: string) {
104
+ setValue(currentValue);
105
+ setOpen(false);
106
+ if (onValueChange) onValueChange(currentValue);
107
+ }
108
+ return (
109
+ <Popover open={open} onOpenChange={setOpen}>
110
+ <PopoverTrigger asChild>
111
+ <Button
112
+ role="combobox"
113
+ variant="ghost"
114
+ aria-expanded={open}
115
+ className={cn(className)}
116
+ >
117
+ <Globe />
118
+ </Button>
119
+ </PopoverTrigger>
120
+ <PopoverContent className="w-40 p-0" align={menuAlign}>
121
+ <Command
122
+ filter={(commandValue, search) => {
123
+ if (
124
+ commandValue.includes(search) ||
125
+ countries
126
+ .find(
127
+ (i) =>
128
+ i.cultureName?.toLocaleLowerCase() ===
129
+ commandValue.toLocaleLowerCase()
130
+ )
131
+ ?.displayName?.toLocaleLowerCase()
132
+ ?.includes(search.toLocaleLowerCase())
133
+ )
134
+ return 1;
135
+ return 0;
136
+ }}
137
+ >
138
+ <ScrollArea className="h-full overflow-auto">
139
+ <CommandList className="h-full overflow-visible">
140
+ <CommandInput placeholder={searchText} className="h-9 text-xs" />
141
+ <CommandEmpty>{searchEmptyValue}</CommandEmpty>
142
+ <CommandGroup>
143
+ {countries.map((country) => (
144
+ <CommandItem
145
+ key={country.cultureName}
146
+ value={`${country.cultureName}` || ""}
147
+ onSelect={(currentValue: string) => onSelect(currentValue)}
148
+ >
149
+ <SelectedCountry
150
+ {...country}
151
+ showFlag={showFlag}
152
+ showLabel={showLabel}
153
+ />
154
+ </CommandItem>
155
+ ))}
156
+ </CommandGroup>
157
+ </CommandList>
158
+ </ScrollArea>
159
+ </Command>
160
+ </PopoverContent>
161
+ </Popover>
162
+ );
163
+ }
164
+
165
+ type SelectedCountryProps = Partial<CountryItem> & {
166
+ showLabel?: boolean;
167
+ showFlag?: boolean;
168
+ };
169
+ const SelectedCountry = ({
170
+ displayName,
171
+ flagIcon = "",
172
+ direction,
173
+ showLabel = true,
174
+ showFlag,
175
+ }: SelectedCountryProps) => (
176
+ <div
177
+ className={`${direction === "rtl" && "flex-row-reverse"} rtl:flex-row-reverse flex w-full justify-between gap-2 overflow-hidden items-center`}
178
+ >
179
+ {showLabel && (
180
+ <span className="text-xs text-foreground">{displayName}</span>
181
+ )}
182
+ <div className="h-auto">
183
+ {showFlag && (
184
+ <img
185
+ src={`https://cdn.jsdelivr.net/gh/lipis/flag-icons/flags/1x1/${flagIcon}.svg`}
186
+ alt={displayName || ""}
187
+ className="w-6 h-6 object-cover rounded-full"
188
+ />
189
+ )}
190
+ {!showFlag && (
191
+ <Badge className="text-xs py-0 px-1">
192
+ {flagIcon?.toUpperCase() || "N/A"}
193
+ </Badge>
194
+ )}
195
+ </div>
196
+ {/* <div className="w-6 h-6"> */}
197
+ {/* {showFlag ?<img */}
198
+ {/* className="w-6 h-6 object-cover rounded-full" */}
199
+ {/* src={`https://cdn.jsdelivr.net/gh/lipis/flag-icons/flags/1x1/${flagIcon}.svg`} */}
200
+ {/* alt={displayName || ''} */}
201
+ {/* />: <} */}
202
+ {/* </div> */}
203
+ </div>
204
+ );
@@ -0,0 +1,109 @@
1
+ "use client";
2
+
3
+ import { getLocalTimeZone, today } from "@internationalized/date";
4
+ import { ComponentProps } from "react";
5
+ import {
6
+ Button as ButtonRac,
7
+ CalendarCell as CalendarCellRac,
8
+ CalendarGridBody as CalendarGridBodyRac,
9
+ CalendarGridHeader as CalendarGridHeaderRac,
10
+ CalendarGrid as CalendarGridRac,
11
+ CalendarHeaderCell as CalendarHeaderCellRac,
12
+ Calendar as CalendarRac,
13
+ Heading as HeadingRac,
14
+ RangeCalendar as RangeCalendarRac,
15
+ composeRenderProps,
16
+ } from "react-aria-components";
17
+ import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
18
+ import { cn } from "@repo/ayasofyazilim-ui/lib/utils";
19
+
20
+ interface BaseCalendarProps {
21
+ className?: string;
22
+ }
23
+
24
+ type CalendarProps = ComponentProps<typeof CalendarRac> & BaseCalendarProps;
25
+ type RangeCalendarProps = ComponentProps<typeof RangeCalendarRac> &
26
+ BaseCalendarProps;
27
+
28
+ const CalendarHeader = () => (
29
+ <header className="pb-1 grid">
30
+ <div className="flex w-full items-center gap-1 ">
31
+ <ButtonRac
32
+ slot="previous"
33
+ className="flex size-9 items-center justify-center rounded-lg text-muted-foreground/80 outline-offset-2 transition-colors hover:bg-accent hover:text-foreground focus:outline-none data-focus-visible:outline-2 data-focus-visible:outline-ring/70"
34
+ >
35
+ <ChevronLeftIcon strokeWidth={2} />
36
+ </ButtonRac>
37
+ <HeadingRac className="grow text-center text-sm font-medium" />
38
+ <ButtonRac
39
+ slot="next"
40
+ className="flex size-9 items-center justify-center rounded-lg text-muted-foreground/80 outline-offset-2 transition-colors hover:bg-accent hover:text-foreground focus:outline-none data-focus-visible:outline-2 data-focus-visible:outline-ring/70"
41
+ >
42
+ <ChevronRightIcon strokeWidth={2} />
43
+ </ButtonRac>
44
+ </div>
45
+ </header>
46
+ );
47
+
48
+ const CalendarGridComponent = ({ isRange = false }: { isRange?: boolean }) => {
49
+ const now = today(getLocalTimeZone());
50
+
51
+ return (
52
+ <CalendarGridRac>
53
+ <CalendarGridHeaderRac>
54
+ {(day) => (
55
+ <CalendarHeaderCellRac className="size-9 rounded-lg p-0 text-xs font-medium text-muted-foreground/80">
56
+ {day}
57
+ </CalendarHeaderCellRac>
58
+ )}
59
+ </CalendarGridHeaderRac>
60
+ <CalendarGridBodyRac className="[&_td]:px-0">
61
+ {(date) => (
62
+ <CalendarCellRac
63
+ date={date}
64
+ className={cn(
65
+ "relative flex size-9 items-center justify-center whitespace-nowrap rounded-lg border border-transparent p-0 text-sm font-normal text-foreground outline-offset-2 duration-150 [transition-property:color,background-color,border-radius,box-shadow] focus:outline-none data-disabled:pointer-events-none data-unavailable:pointer-events-none data-focus-visible:z-10 data-hovered:bg-accent data-selected:bg-primary data-hovered:text-foreground data-selected:text-primary-foreground data-unavailable:line-through data-disabled:opacity-30 data-unavailable:opacity-30 data-focus-visible:outline-2 data-focus-visible:outline-ring/70",
66
+ // Range-specific styles
67
+ isRange &&
68
+ "data-selected:rounded-none data-selection-end:rounded-e-lg data-selection-start:rounded-s-lg data-invalid:bg-red-100 data-selected:bg-accent data-selected:text-foreground data-invalid:data-selection-end:[&:not([data-hover])]:bg-destructive data-invalid:data-selection-start:[&:not([data-hover])]:bg-destructive data-selection-end:[&:not([data-hover])]:bg-primary data-selection-start:[&:not([data-hover])]:bg-primary data-invalid:data-selection-end:[&:not([data-hover])]:text-destructive-foreground data-invalid:data-selection-start:[&:not([data-hover])]:text-destructive-foreground data-selection-end:[&:not([data-hover])]:text-primary-foreground data-selection-start:[&:not([data-hover])]:text-primary-foreground",
69
+ // Today indicator styles
70
+ date.compare(now) === 0 &&
71
+ cn(
72
+ "after:pointer-events-none after:absolute after:bottom-1 after:start-1/2 after:z-10 after:size-[3px] after:-translate-x-1/2 after:rounded-full after:bg-primary",
73
+ isRange
74
+ ? "data-selection-end:[&:not([data-hover])]:after:bg-background data-selection-start:[&:not([data-hover])]:after:bg-background"
75
+ : "data-selected:after:bg-background"
76
+ )
77
+ )}
78
+ />
79
+ )}
80
+ </CalendarGridBodyRac>
81
+ </CalendarGridRac>
82
+ );
83
+ };
84
+
85
+ const Calendar = ({ className, ...props }: CalendarProps) => (
86
+ <CalendarRac
87
+ {...props}
88
+ className={composeRenderProps(className, (className) =>
89
+ cn("w-fit", className)
90
+ )}
91
+ >
92
+ <CalendarHeader />
93
+ <CalendarGridComponent />
94
+ </CalendarRac>
95
+ );
96
+
97
+ const RangeCalendar = ({ className, ...props }: RangeCalendarProps) => (
98
+ <RangeCalendarRac
99
+ {...props}
100
+ className={composeRenderProps(className, (className) =>
101
+ cn("w-fit", className)
102
+ )}
103
+ >
104
+ <CalendarHeader />
105
+ <CalendarGridComponent isRange />
106
+ </RangeCalendarRac>
107
+ );
108
+
109
+ export { Calendar, RangeCalendar };
@@ -0,0 +1,84 @@
1
+ "use client";
2
+
3
+ import {
4
+ DateFieldProps,
5
+ DateField as DateFieldRac,
6
+ DateInputProps as DateInputPropsRac,
7
+ DateInput as DateInputRac,
8
+ DateSegmentProps,
9
+ DateSegment as DateSegmentRac,
10
+ DateValue as DateValueRac,
11
+ TimeFieldProps,
12
+ TimeField as TimeFieldRac,
13
+ TimeValue as TimeValueRac,
14
+ composeRenderProps,
15
+ } from "react-aria-components";
16
+ import { cn } from "@repo/ayasofyazilim-ui/lib/utils";
17
+
18
+ const DateField = <T extends DateValueRac>({
19
+ className,
20
+ children,
21
+ ...props
22
+ }: DateFieldProps<T>) => (
23
+ <DateFieldRac
24
+ className={composeRenderProps(className, (className) =>
25
+ cn("space-y-2", className)
26
+ )}
27
+ {...props}
28
+ >
29
+ {children}
30
+ </DateFieldRac>
31
+ );
32
+
33
+ const TimeField = <T extends TimeValueRac>({
34
+ className,
35
+ children,
36
+ ...props
37
+ }: TimeFieldProps<T>) => (
38
+ <TimeFieldRac
39
+ className={composeRenderProps(className, (className) =>
40
+ cn("space-y-2", className)
41
+ )}
42
+ {...props}
43
+ >
44
+ {children}
45
+ </TimeFieldRac>
46
+ );
47
+
48
+ const DateSegment = ({ className, ...props }: DateSegmentProps) => (
49
+ <DateSegmentRac
50
+ className={composeRenderProps(className, (className) =>
51
+ cn(
52
+ "inline rounded p-0.5 text-foreground caret-transparent outline-0 data-disabled:cursor-not-allowed data-focused:bg-accent data-invalid:data-focused:bg-destructive data-[type=literal]:px-0 data-focused:data-placeholder:text-foreground data-focused:text-foreground data-invalid:data-focused:data-placeholder:text-destructive-foreground data-invalid:data-focused:text-destructive-foreground data-invalid:data-placeholder:text-destructive data-invalid:text-destructive data-placeholder:text-muted-foreground/70 data-[type=literal]:text-muted-foreground/70 data-disabled:opacity-50",
53
+ className
54
+ )
55
+ )}
56
+ {...props}
57
+ />
58
+ );
59
+
60
+ const dateInputStyle =
61
+ "relative inline-flex h-9 w-full items-center overflow-hidden whitespace-nowrap rounded-lg border border-input bg-background px-3 py-2 text-sm shadow-sm shadow-black/5 transition-shadow data-[focus-within]:border-ring data-disabled:opacity-50 data-[focus-within]:outline-none data-[focus-within]:ring-[3px] data-[focus-within]:ring-ring/20";
62
+
63
+ interface DateInputProps extends DateInputPropsRac {
64
+ className?: string;
65
+ unstyled?: boolean;
66
+ }
67
+
68
+ const DateInput = ({
69
+ className,
70
+ unstyled = false,
71
+ ...props
72
+ }: Omit<DateInputProps, "children">) => (
73
+ <DateInputRac
74
+ className={composeRenderProps(className, (className) =>
75
+ cn(!unstyled && dateInputStyle, className)
76
+ )}
77
+ {...props}
78
+ >
79
+ {(segment) => <DateSegment segment={segment} />}
80
+ </DateInputRac>
81
+ );
82
+
83
+ export { DateField, DateInput, DateSegment, TimeField, dateInputStyle };
84
+ export type { DateInputProps };
@@ -0,0 +1,273 @@
1
+ "use client";
2
+
3
+ import { CalendarIcon } from "lucide-react";
4
+ import { useEffect, useState } from "react";
5
+ import {
6
+ Button,
7
+ DatePicker as DefaultDatePicker,
8
+ DateRangePicker as DefaultDateRangePicker,
9
+ // Dialog,
10
+ Group,
11
+ Label,
12
+ // Popover,
13
+ } from "react-aria-components";
14
+ import { cn } from "@repo/ayasofyazilim-ui/lib/utils";
15
+ import { Separator } from "@repo/ayasofyazilim-ui/components/separator";
16
+ import { Calendar, RangeCalendar } from "./calendar-rac";
17
+ import { DateInput, TimeField } from "./datefield-rac";
18
+ import { DateRange } from "./types";
19
+ import { createDate, createTime } from "./utils";
20
+ import {
21
+ Popover,
22
+ PopoverContent,
23
+ PopoverTrigger,
24
+ } from "@repo/ayasofyazilim-ui/components/popover";
25
+
26
+ // const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; DO NOT DELETE
27
+ const offset = new Date().getTimezoneOffset() * 60 * 1000;
28
+
29
+ export function DatePicker({
30
+ id,
31
+ label,
32
+ classNames,
33
+ onChange,
34
+ defaultValue,
35
+ disabled = false,
36
+ useTime = false,
37
+ showIcon = true,
38
+ }: {
39
+ id: string;
40
+ label?: string;
41
+ disabled?: boolean;
42
+ classNames?: {
43
+ dateInput?: string;
44
+ };
45
+ showIcon?: boolean;
46
+ useTime?: boolean;
47
+ defaultValue?: Date;
48
+ onChange?: (date: Date) => void;
49
+ }) {
50
+ const [dateValue, setDateValue] = useState(
51
+ createDate({ date: defaultValue, offset })
52
+ );
53
+ const [timeValue, setTimeValue] = useState(
54
+ createTime({ date: defaultValue, offset })
55
+ );
56
+ useEffect(() => {
57
+ if (!dateValue) return;
58
+ if (onChange) {
59
+ if (useTime && timeValue) {
60
+ onChange(
61
+ new Date(
62
+ dateValue.year,
63
+ dateValue.month - 1,
64
+ dateValue.day,
65
+ timeValue.hour,
66
+ timeValue.minute,
67
+ timeValue.second
68
+ )
69
+ );
70
+ } else {
71
+ const timeZone = localStorage.getItem("tenantTimeZone") || "UTC";
72
+
73
+ onChange(dateValue.toDate(timeZone));
74
+ }
75
+ }
76
+ }, [dateValue, timeValue, onChange, useTime]);
77
+
78
+ const [isOpen, setIsOpen] = useState(false);
79
+ return (
80
+ <DefaultDatePicker
81
+ aria-label="x"
82
+ className="space-y-2"
83
+ isDisabled={disabled}
84
+ value={dateValue}
85
+ onChange={(date) => {
86
+ if (date) {
87
+ setDateValue(date);
88
+ }
89
+ }}
90
+ >
91
+ {label && (
92
+ <Label className="text-sm font-medium text-foreground">{label}</Label>
93
+ )}
94
+
95
+ <div className="flex">
96
+ <Group
97
+ className={cn(
98
+ "w-full flex border rounded-md h-9 pl-3 py-2 items-center gap-2 peer",
99
+ showIcon ? "pr-9" : "pr-3",
100
+ classNames?.dateInput
101
+ )}
102
+ >
103
+ <DateInput
104
+ unstyled
105
+ className="peer-focus:ring"
106
+ data-testid={`${id}_calendar_input_1`}
107
+ />
108
+ {useTime && (
109
+ <>
110
+ <Separator orientation="vertical" />
111
+ <TimeField
112
+ aria-label="Time"
113
+ value={timeValue}
114
+ className="h-max *:m-0"
115
+ onChange={(time) => {
116
+ if (time) {
117
+ setTimeValue(time);
118
+ }
119
+ }}
120
+ >
121
+ <DateInput
122
+ unstyled
123
+ className="peer-focus:ring"
124
+ data-testid={`${id}_calendar_input_2`}
125
+ />
126
+ </TimeField>
127
+ </>
128
+ )}
129
+ </Group>
130
+ <Popover
131
+ open={isOpen}
132
+ onOpenChange={setIsOpen}
133
+ // placement="bottom end"
134
+ >
135
+ <PopoverTrigger asChild>
136
+ {showIcon && (
137
+ <Button
138
+ data-testid={`${id}_calendar_icon`}
139
+ className="z-10 -me-px -ms-9 flex w-9 items-center justify-center rounded-e-lg text-muted-foreground/80 outline-offset-2 transition-colors hover:text-foreground focus-visible:outline-none data-focus-visible:outline-2 data-focus-visible:outline-ring/70 border-none"
140
+ >
141
+ <CalendarIcon size={16} strokeWidth={2} />
142
+ </Button>
143
+ )}
144
+ </PopoverTrigger>
145
+ <PopoverContent
146
+ align="end"
147
+ className="z-50 min-w-fit max-w-fit rounded-lg border border-border bg-background text-popover-foreground shadow-lg shadow-black/5 outline-none data-entering:animate-in data-exiting:animate-out data-entering:fade-in-0 data-exiting:fade-out-0 data-entering:zoom-in-95 data-exiting:zoom-out-95 data-[placement=bottom]:slide-in-from-top-2 data-[placement=left]:slide-in-from-right-2 data-[placement=right]:slide-in-from-left-2 data-[placement=top]:slide-in-from-bottom-2"
148
+ >
149
+ {/* <Dialog className="max-h-[inherit] overflow-auto p-2"> */}
150
+ <Calendar onChange={() => setIsOpen(false)} />
151
+ {/* </Dialog> */}
152
+ </PopoverContent>
153
+ </Popover>
154
+ </div>
155
+ </DefaultDatePicker>
156
+ );
157
+ }
158
+
159
+ export function DateRangePicker({
160
+ id,
161
+ label,
162
+ classNames,
163
+ onChange,
164
+ defaultValues,
165
+ disabled = false,
166
+ showIcon = true,
167
+ }: {
168
+ id: string;
169
+ label?: string;
170
+ disabled?: boolean;
171
+ classNames?: {
172
+ dateInput?: string;
173
+ };
174
+ showIcon?: boolean;
175
+ defaultValues?: DateRange;
176
+ onChange?: (date: DateRange) => void;
177
+ }) {
178
+ const [dateValue, setDateValue] = useState({
179
+ start: createDate({ date: defaultValues?.start, offset }),
180
+ end: createDate({ date: defaultValues?.end, offset }),
181
+ });
182
+ useEffect(() => {
183
+ if (!dateValue) return;
184
+ if (onChange) {
185
+ const { start } = dateValue;
186
+ const { end } = dateValue;
187
+ onChange({
188
+ start:
189
+ (start &&
190
+ start.year &&
191
+ start.month &&
192
+ start.day &&
193
+ new Date(start.year, start.month - 1, start.day)) ||
194
+ undefined,
195
+ end:
196
+ (end &&
197
+ end.year &&
198
+ end.month &&
199
+ end.day &&
200
+ new Date(end.year, end.month - 1, end.day)) ||
201
+ undefined,
202
+ });
203
+ }
204
+ }, [dateValue]);
205
+ return (
206
+ <DefaultDateRangePicker
207
+ aria-label="x"
208
+ startName="start"
209
+ endName="end"
210
+ className="space-y-2"
211
+ isDisabled={disabled}
212
+ value={
213
+ dateValue.start &&
214
+ dateValue.end && {
215
+ start: dateValue.start,
216
+ end: dateValue.end,
217
+ }
218
+ }
219
+ onChange={(date) => {
220
+ if (date) {
221
+ setDateValue(date);
222
+ }
223
+ }}
224
+ >
225
+ {label && (
226
+ <Label className="text-sm font-medium text-foreground">{label}</Label>
227
+ )}
228
+ <div className="flex">
229
+ <Group
230
+ className={cn(
231
+ "w-full min-w-fit flex border rounded-md h-9 pl-3 py-2 items-center gap-2 peer",
232
+ showIcon ? "pr-9" : "pr-3",
233
+ classNames?.dateInput
234
+ )}
235
+ >
236
+ <DateInput
237
+ unstyled
238
+ className="peer-focus:ring"
239
+ slot="start"
240
+ data-testid={`${id}_calendar_input_2`}
241
+ />
242
+ <Separator orientation="vertical" />
243
+ <DateInput
244
+ unstyled
245
+ className="peer-focus:ring"
246
+ slot="end"
247
+ data-testid={`${id}_calendar_input_2`}
248
+ />
249
+ </Group>
250
+ <Popover>
251
+ <PopoverTrigger asChild>
252
+ {showIcon && (
253
+ <Button
254
+ data-testid={`${id}_calendar_icon`}
255
+ className="z-10 -me-px -ms-9 flex w-9 items-center justify-center rounded-e-lg text-muted-foreground/80 outline-offset-2 transition-colors hover:text-foreground focus-visible:outline-none data-focus-visible:outline-2 data-focus-visible:outline-ring/70 border-none"
256
+ >
257
+ <CalendarIcon size={16} strokeWidth={2} />
258
+ </Button>
259
+ )}
260
+ </PopoverTrigger>
261
+ <PopoverContent
262
+ align="end"
263
+ className="z-50 rounded-lg border border-border bg-background text-popover-foreground shadow-lg shadow-black/5 outline-none data-entering:animate-in data-exiting:animate-out data-entering:fade-in-0 data-exiting:fade-out-0 data-entering:zoom-in-95 data-exiting:zoom-out-95 data-[placement=bottom]:slide-in-from-top-2 data-[placement=left]:slide-in-from-right-2 data-[placement=right]:slide-in-from-left-2 data-[placement=top]:slide-in-from-bottom-2"
264
+ >
265
+ {/* <Dialog className="max-h-[inherit] overflow-auto p-2"> */}
266
+ <RangeCalendar />
267
+ {/* </Dialog> */}
268
+ </PopoverContent>
269
+ </Popover>
270
+ </div>
271
+ </DefaultDateRangePicker>
272
+ );
273
+ }
@@ -0,0 +1,4 @@
1
+ export type DateRange = {
2
+ start: Date | undefined;
3
+ end: Date | undefined;
4
+ };