@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,59 @@
1
+ import { DatePicker } from "@repo/ayasofyazilim-ui/custom/date-picker";
2
+ import { WidgetProps } from "@rjsf/utils";
3
+
4
+ export const DateWidget = (props: WidgetProps) => {
5
+ const { value, onChange, disabled, uiSchema } = props;
6
+
7
+ const initialDate =
8
+ value && !Number.isNaN(new Date(value).getTime())
9
+ ? new Date(new Date(value).toJSON())
10
+ : undefined;
11
+ return (
12
+ <DatePicker
13
+ id={props.id}
14
+ defaultValue={initialDate}
15
+ disabled={disabled}
16
+ classNames={{
17
+ dateInput: " shadow-xs date-input",
18
+ }}
19
+ onChange={(selectedDate) => {
20
+ if (selectedDate) {
21
+ if (props.schema.format === "date") {
22
+ onChange(selectedDate.toISOString().split("T").at(0));
23
+ } else {
24
+ onChange(selectedDate.toISOString());
25
+ }
26
+ }
27
+ }}
28
+ />
29
+ );
30
+ };
31
+
32
+ export const DateTimeWidget = (props: WidgetProps) => {
33
+ const { value, onChange, disabled, uiSchema } = props;
34
+
35
+ const initialDate =
36
+ value && !Number.isNaN(new Date(value).getTime())
37
+ ? new Date(new Date(value).toJSON())
38
+ : undefined;
39
+ return (
40
+ <DatePicker
41
+ id={props.id}
42
+ defaultValue={initialDate}
43
+ useTime
44
+ disabled={disabled}
45
+ classNames={{
46
+ dateInput: "shadow-xs date-input",
47
+ }}
48
+ onChange={(selectedDate) => {
49
+ if (selectedDate) {
50
+ if (props.schema.format === "date") {
51
+ onChange(selectedDate.toISOString().split("T").at(0));
52
+ } else {
53
+ onChange(selectedDate.toISOString());
54
+ }
55
+ }
56
+ }}
57
+ />
58
+ );
59
+ };
@@ -0,0 +1,34 @@
1
+ import { EmailInput } from "@repo/ayasofyazilim-ui/custom/email-input";
2
+ import { cn } from "@repo/ayasofyazilim-ui/lib/utils";
3
+ import { WidgetProps } from "@rjsf/utils";
4
+ import { useState } from "react";
5
+
6
+ export const EmailInputWidget = (props: WidgetProps) => {
7
+ const {
8
+ uiSchema,
9
+ className,
10
+ onChange,
11
+ value,
12
+ defaultValue,
13
+ disabled,
14
+ readOnly,
15
+ } = props;
16
+
17
+ const [email, setEmail] = useState(value || "");
18
+ return (
19
+ <EmailInput
20
+ disabled={disabled}
21
+ id={props.id}
22
+ value={email}
23
+ onValueChange={(val) => {
24
+ setEmail(val);
25
+ onChange(val);
26
+ }}
27
+ defaultValue={defaultValue}
28
+ readOnly={readOnly}
29
+ placeholder={props.placeholder || "Try typing 'john@gmail.com'"}
30
+ className={cn("w-full", className)}
31
+ suggestions={uiSchema?.["ui:baseList"] ?? []}
32
+ />
33
+ );
34
+ };
@@ -0,0 +1,10 @@
1
+ export * from "./boolean";
2
+ export * from "./combobox";
3
+ export * from "./date";
4
+ export * from "./email";
5
+ export * from "./password";
6
+ export * from "./url";
7
+ export * from "./phone";
8
+ export * from "./selectable";
9
+ export * from "./select";
10
+ export * from "./string-array";
@@ -0,0 +1,40 @@
1
+ import { WidgetProps } from "@rjsf/utils";
2
+ import { PasswordInput } from "@repo/ayasofyazilim-ui/custom/password-input";
3
+
4
+ export const PasswordInputWidget = (props: WidgetProps) => {
5
+ const {
6
+ uiSchema,
7
+ id,
8
+ className,
9
+ onChange,
10
+ value,
11
+ defaultValue,
12
+ disabled,
13
+ readOnly,
14
+ } = props;
15
+
16
+ const required = uiSchema?.["ui:required"] || props.required;
17
+
18
+ return (
19
+ <PasswordInput
20
+ id={id}
21
+ data-testid={id}
22
+ onBlur={props.onBlur && ((event) => props.onBlur(id, event.target.value))}
23
+ className={className}
24
+ required={required}
25
+ onChange={(event) => {
26
+ if (event.target.value === "") {
27
+ onChange(undefined);
28
+ } else {
29
+ onChange(event.target.value);
30
+ }
31
+ }}
32
+ defaultValue={value || defaultValue}
33
+ readOnly={readOnly}
34
+ disabled={disabled}
35
+ autoComplete={uiSchema?.["ui:autocomplete"]}
36
+ showGenerator={uiSchema?.["ui:showGenerator"]}
37
+ passwordLength={uiSchema?.["ui:passwordLength"] || 10}
38
+ />
39
+ );
40
+ };
@@ -0,0 +1,40 @@
1
+ "use client";
2
+
3
+ import {
4
+ PhoneInput,
5
+ PhoneInputProps,
6
+ } from "@repo/ayasofyazilim-ui/custom/phone-input";
7
+ import { WidgetProps } from "@rjsf/utils";
8
+
9
+ export function CustomPhoneFieldWithParse<T>(
10
+ props: Partial<Omit<WidgetProps<T>, "onChange" | "id">> &
11
+ Omit<PhoneInputProps, "required" | "defaultValue" | "id">
12
+ ) {
13
+ const required = props.required || props.uiSchema?.["ui:required"];
14
+ return (
15
+ <PhoneInput
16
+ {...props}
17
+ id={props.id || ""}
18
+ required={required}
19
+ defaultValue={undefined}
20
+ />
21
+ );
22
+ }
23
+ export const PhoneWithParseWidget = CustomPhoneFieldWithParse;
24
+
25
+ export const PhoneWithValueWidget = function CustomPhoneFieldWithValue(
26
+ props: WidgetProps & { defaultCountry?: string }
27
+ ) {
28
+ const { onChange, uiSchema } = props;
29
+ const required = uiSchema?.["ui:required"] || props.required;
30
+ return (
31
+ <PhoneInput
32
+ {...props}
33
+ required={required}
34
+ defaultValue={undefined}
35
+ onChange={(values) => {
36
+ onChange(values.value);
37
+ }}
38
+ />
39
+ );
40
+ };
@@ -0,0 +1,105 @@
1
+ import {
2
+ FormContextType,
3
+ RJSFSchema,
4
+ StrictRJSFSchema,
5
+ WidgetProps,
6
+ } from "@rjsf/utils";
7
+
8
+ import {
9
+ Select,
10
+ SelectContent,
11
+ SelectItem,
12
+ SelectTrigger,
13
+ SelectValue,
14
+ } from "@repo/ayasofyazilim-ui/components/select";
15
+ import { Selectable } from "@repo/ayasofyazilim-ui/custom/selectable";
16
+ import { cn } from "@repo/ayasofyazilim-ui/lib/utils";
17
+
18
+ /** The `SelectWidget` is a widget for rendering dropdowns.
19
+ * It is typically used with string properties constrained with enum options.
20
+ *
21
+ * @param props - The `WidgetProps` for this component
22
+ */
23
+
24
+ interface EnumOption {
25
+ value: string;
26
+ label: string;
27
+ // Add other properties if your options have them (e.g., key, rawData)
28
+ }
29
+
30
+ // 2. Define the shape of the transformed item (optional, but good for clarity)
31
+ interface MappedItem {
32
+ value: string;
33
+ label: string;
34
+ disabled: boolean;
35
+ }
36
+
37
+ export function SelectWidget<
38
+ T = any,
39
+ S extends StrictRJSFSchema = RJSFSchema,
40
+ F extends FormContextType = any,
41
+ >({
42
+ id,
43
+ options,
44
+ required,
45
+ disabled,
46
+ value,
47
+ multiple,
48
+ onChange,
49
+ rawErrors = [],
50
+ className,
51
+ }: WidgetProps<T, S, F>) {
52
+ const { enumOptions, enumDisabled, emptyValue: optEmptyValue } = options;
53
+ const items: MappedItem[] = (enumOptions as EnumOption[])?.map(
54
+ ({ value, label }: EnumOption) => {
55
+ return {
56
+ value,
57
+ label,
58
+ disabled: Array.isArray(enumDisabled) && enumDisabled.includes(value),
59
+ };
60
+ }
61
+ );
62
+ const cnClassName = cn(
63
+ "w-full",
64
+ { "border-destructive": rawErrors.length > 0 },
65
+ className
66
+ );
67
+ return !multiple ? (
68
+ <Select
69
+ required={required}
70
+ value={value}
71
+ onValueChange={(val) => {
72
+ onChange(val);
73
+ }}
74
+ >
75
+ <SelectTrigger className={cnClassName} id={id}>
76
+ <SelectValue placeholder={optEmptyValue} />
77
+ </SelectTrigger>
78
+ <SelectContent>
79
+ {items.map((item) => {
80
+ return (
81
+ <SelectItem
82
+ disabled={item.disabled}
83
+ key={item.value}
84
+ value={item.value}
85
+ >
86
+ {item.label}
87
+ </SelectItem>
88
+ );
89
+ })}
90
+ </SelectContent>
91
+ </Select>
92
+ ) : (
93
+ <Selectable<MappedItem>
94
+ id={id}
95
+ getKey={(opt) => opt.value}
96
+ getLabel={(opt) => opt.label}
97
+ options={items}
98
+ onChange={(val) => {
99
+ onChange(val.map((item) => item.value));
100
+ }}
101
+ disabled={disabled}
102
+ makeAChoiceText={optEmptyValue}
103
+ />
104
+ );
105
+ }
@@ -0,0 +1,25 @@
1
+ import {
2
+ Selectable,
3
+ SelectableProps,
4
+ } from "@repo/ayasofyazilim-ui/custom/selectable";
5
+ import { WidgetProps } from "@rjsf/utils";
6
+
7
+ export function CustomSelectableWidget<T>(
8
+ selectableProps: Omit<SelectableProps<T>, "onChange">
9
+ ) {
10
+ const { getKey } = selectableProps;
11
+
12
+ function Widget(widgetProps: Omit<WidgetProps, "options">) {
13
+ const handleChange = (values: T[]) => {
14
+ widgetProps.onChange(values.map((item) => getKey(item)));
15
+ };
16
+ return (
17
+ <Selectable<T>
18
+ {...widgetProps}
19
+ {...selectableProps}
20
+ onChange={handleChange}
21
+ />
22
+ );
23
+ }
24
+ return Widget;
25
+ }
@@ -0,0 +1,296 @@
1
+ import { ButtonProps } from "@repo/ayasofyazilim-ui/components/button";
2
+ import {
3
+ InputGroup,
4
+ InputGroupAddon,
5
+ InputGroupButton,
6
+ InputGroupInput,
7
+ InputGroupText,
8
+ } from "@repo/ayasofyazilim-ui/components/input-group";
9
+ import {
10
+ Tooltip,
11
+ TooltipContent,
12
+ TooltipTrigger,
13
+ } from "@repo/ayasofyazilim-ui/components/tooltip";
14
+ import { cn } from "@repo/ayasofyazilim-ui/lib/utils";
15
+ import { WidgetProps } from "@rjsf/utils";
16
+ import { PlusIcon, Trash2Icon } from "lucide-react";
17
+ import { useCallback, useState } from "react";
18
+
19
+ interface StringArrayWidgetConfig {
20
+ classNames?: {
21
+ container?: string;
22
+ listContainer?: string;
23
+ listItem?: string;
24
+ listItemText?: string;
25
+ removeButton?: string;
26
+ inputContainer?: string;
27
+ input?: string;
28
+ addButton?: string;
29
+ };
30
+ icons?: {
31
+ add?: React.ComponentType<{ className?: string }>;
32
+ remove?: React.ComponentType<{ className?: string }>;
33
+ };
34
+ iconSizes?: {
35
+ add?: string;
36
+ remove?: string;
37
+ };
38
+ buttonVariants?: {
39
+ add?: ButtonProps["variant"];
40
+ remove?: ButtonProps["variant"];
41
+ };
42
+ buttonSizes?: {
43
+ add?: "icon-xs" | "sm" | "icon-sm" | "xs";
44
+ remove?: "icon-xs" | "sm" | "icon-sm" | "xs";
45
+ };
46
+ maxItems?: number;
47
+ maxHeight?: string;
48
+ allowDuplicates?: boolean;
49
+ trimValues?: boolean;
50
+ validateValue?: (value: string) => boolean | string;
51
+ transformValue?: (value: string) => string;
52
+ renderItem?: (
53
+ value: string,
54
+ index: number,
55
+ onRemove: () => void
56
+ ) => React.ReactNode;
57
+ emptyState?: React.ReactNode;
58
+ placeholder?: string;
59
+ addButtonLabel?: string;
60
+ removeButtonLabel?: string;
61
+ errorMessages?: {
62
+ duplicate?: string;
63
+ invalid?: string;
64
+ maxItems?: (max: number) => string;
65
+ };
66
+ messages?: {
67
+ maxReached?: (max: number) => string;
68
+ };
69
+ inputProps?: React.InputHTMLAttributes<HTMLInputElement>;
70
+ onAdd?: (value: string) => void;
71
+ onRemove?: (value: string, index: number) => void;
72
+ disabled?: boolean;
73
+ }
74
+
75
+ export function CustomStringArrayWidget(config: StringArrayWidgetConfig = {}) {
76
+ const {
77
+ classNames = {},
78
+ icons = {},
79
+ iconSizes = { add: "icon-xs", remove: "icon-xs" },
80
+ buttonVariants = { add: "default", remove: "secondary" },
81
+ buttonSizes = { add: "icon-xs", remove: undefined },
82
+ maxItems,
83
+ maxHeight = "max-h-52",
84
+ allowDuplicates = false,
85
+ trimValues = true,
86
+ validateValue,
87
+ transformValue,
88
+ renderItem,
89
+ emptyState,
90
+ placeholder,
91
+ addButtonLabel,
92
+ removeButtonLabel,
93
+ errorMessages = {},
94
+ messages = {},
95
+ inputProps = {},
96
+ onAdd,
97
+ onRemove,
98
+ disabled = false,
99
+ } = config;
100
+
101
+ const AddIcon = icons.add || PlusIcon;
102
+ const RemoveIcon = icons.remove || Trash2Icon;
103
+
104
+ const Widget = (props: WidgetProps) => {
105
+ const [inputValue, setInputValue] = useState("");
106
+ const [error, setError] = useState<string>("");
107
+
108
+ const currentValues = props.value || [];
109
+
110
+ const handleValue = useCallback(
111
+ (value: string) => {
112
+ const processedValue = trimValues ? value.trim() : value;
113
+
114
+ if (!processedValue) {
115
+ return;
116
+ }
117
+ const finalValue = transformValue
118
+ ? transformValue(processedValue)
119
+ : processedValue;
120
+ if (!allowDuplicates && currentValues.includes(finalValue)) {
121
+ if (errorMessages.duplicate) {
122
+ setError(errorMessages.duplicate);
123
+ }
124
+ return;
125
+ }
126
+ if (validateValue) {
127
+ const validationResult = validateValue(finalValue);
128
+ if (validationResult === false) {
129
+ if (errorMessages.invalid) {
130
+ setError(errorMessages.invalid);
131
+ }
132
+ return;
133
+ }
134
+ if (typeof validationResult === "string") {
135
+ setError(validationResult);
136
+ return;
137
+ }
138
+ }
139
+ if (maxItems && currentValues.length >= maxItems) {
140
+ if (errorMessages.maxItems) {
141
+ setError(errorMessages.maxItems(maxItems));
142
+ }
143
+ return;
144
+ }
145
+ setError("");
146
+ props.onChange([...currentValues, finalValue]);
147
+ setInputValue("");
148
+ if (onAdd) {
149
+ onAdd(finalValue);
150
+ }
151
+ },
152
+ [currentValues, props]
153
+ );
154
+
155
+ const handleRemove = useCallback(
156
+ (index: number) => {
157
+ const valueToRemove = currentValues[index];
158
+ const newValues = currentValues.filter(
159
+ (_: string, i: number) => i !== index
160
+ );
161
+ props.onChange(newValues);
162
+ if (onRemove) {
163
+ onRemove(valueToRemove, index);
164
+ }
165
+ },
166
+ [currentValues, props, onRemove]
167
+ );
168
+
169
+ const isDisabled = disabled || props.disabled;
170
+ const isMaxReached = !!(maxItems && currentValues.length >= maxItems);
171
+
172
+ return (
173
+ <InputGroup
174
+ className={cn(
175
+ "p-1.5",
176
+ currentValues.length === 0 && "p-0",
177
+ classNames.container
178
+ )}
179
+ >
180
+ <InputGroupAddon
181
+ align="block-end"
182
+ className={cn(
183
+ "flex-col gap-1.5",
184
+ maxHeight,
185
+ "overflow-auto p-0",
186
+ classNames.listContainer || ""
187
+ )}
188
+ >
189
+ {currentValues.length === 0 && emptyState
190
+ ? emptyState
191
+ : currentValues.map((value: string, index: number) => {
192
+ if (renderItem) {
193
+ return renderItem(value, index, () => handleRemove(index));
194
+ }
195
+
196
+ return (
197
+ <InputGroupText
198
+ className={cn(
199
+ "bg-accent relative w-full overflow-hidden pl-2 pr-1 min-h-8 h-8 rounded-md",
200
+ classNames.listItem || ""
201
+ )}
202
+ key={`${value}-${index}`}
203
+ >
204
+ <span
205
+ className={cn(
206
+ "truncate text-ellipsis",
207
+ classNames.listItemText || ""
208
+ )}
209
+ >
210
+ {value}
211
+ </span>
212
+ <InputGroupButton
213
+ className={cn(
214
+ "rounded-md ml-auto",
215
+ classNames.removeButton || ""
216
+ )}
217
+ variant={buttonVariants.remove}
218
+ size={buttonSizes.remove}
219
+ onClick={() => handleRemove(index)}
220
+ disabled={isDisabled}
221
+ aria-label={removeButtonLabel}
222
+ >
223
+ <RemoveIcon className={iconSizes.remove} />
224
+ </InputGroupButton>
225
+ </InputGroupText>
226
+ );
227
+ })}
228
+ </InputGroupAddon>
229
+ <InputGroupAddon
230
+ align="block-end"
231
+ className={cn(
232
+ "p-0 justify-between",
233
+ currentValues.length === 0 && "pr-1.5 ",
234
+ classNames.inputContainer || ""
235
+ )}
236
+ >
237
+ <Tooltip open={!!error}>
238
+ <TooltipTrigger asChild>
239
+ <div>
240
+ <InputGroupInput
241
+ className={cn(
242
+ "pl-2",
243
+ error && "text-destructive",
244
+ classNames.input || ""
245
+ )}
246
+ placeholder={placeholder || props.options.placeholder}
247
+ value={inputValue}
248
+ onChange={(e) => {
249
+ setInputValue(e.target.value);
250
+ setError("");
251
+ }}
252
+ onKeyDown={(e) => {
253
+ if (e.key === "Enter") {
254
+ e.preventDefault();
255
+ handleValue(inputValue);
256
+ }
257
+ }}
258
+ {...inputProps}
259
+ />
260
+ </div>
261
+ </TooltipTrigger>
262
+ {error && <TooltipContent>{error}</TooltipContent>}
263
+ </Tooltip>
264
+ <Tooltip>
265
+ <TooltipTrigger asChild>
266
+ <div>
267
+ <InputGroupButton
268
+ type="button"
269
+ variant={buttonVariants.add}
270
+ className={cn(
271
+ "rounded-md ml-auto",
272
+ classNames.addButton || ""
273
+ )}
274
+ size={buttonSizes.add}
275
+ onClick={() => handleValue(inputValue)}
276
+ disabled={isDisabled || isMaxReached || !inputValue}
277
+ aria-label={addButtonLabel}
278
+ >
279
+ <AddIcon className={iconSizes.add} />
280
+ <span className="sr-only">{addButtonLabel}</span>
281
+ </InputGroupButton>
282
+ </div>
283
+ </TooltipTrigger>
284
+ {isMaxReached && messages.maxReached && (
285
+ <TooltipContent side="bottom">
286
+ {messages.maxReached(maxItems!)}
287
+ </TooltipContent>
288
+ )}
289
+ </Tooltip>
290
+ </InputGroupAddon>
291
+ </InputGroup>
292
+ );
293
+ };
294
+
295
+ return Widget;
296
+ }
@@ -0,0 +1,56 @@
1
+ import {} from "@radix-ui/react-tooltip";
2
+ import {
3
+ InputGroup,
4
+ InputGroupAddon,
5
+ InputGroupInput,
6
+ InputGroupText,
7
+ } from "@repo/ayasofyazilim-ui/components/input-group";
8
+ import { cn } from "@repo/ayasofyazilim-ui/lib/utils";
9
+ import { WidgetProps } from "@rjsf/utils";
10
+
11
+ export const URLInputWidget = (props: WidgetProps) => {
12
+ const {
13
+ uiSchema,
14
+ id,
15
+ className,
16
+ onChange,
17
+ value,
18
+ defaultValue,
19
+ disabled,
20
+ readOnly,
21
+ } = props;
22
+
23
+ const required = uiSchema?.["ui:required"] || props.required;
24
+
25
+ return (
26
+ <InputGroup>
27
+ <InputGroupInput
28
+ placeholder="example.com"
29
+ id={id}
30
+ data-testid={id}
31
+ onBlur={
32
+ props.onBlur && ((event) => props.onBlur(id, event.target.value))
33
+ }
34
+ className={cn("pl-1!", className)}
35
+ required={required}
36
+ onChange={(event) => {
37
+ if (event.target.value === "") {
38
+ onChange(undefined);
39
+ } else {
40
+ onChange(event.target.value);
41
+ }
42
+ }}
43
+ defaultValue={value || defaultValue}
44
+ readOnly={readOnly}
45
+ disabled={disabled}
46
+ autoComplete={uiSchema?.["ui:autocomplete"]}
47
+ onKeyDown={(e) => {
48
+ e.key === "Enter" && e.preventDefault();
49
+ }}
50
+ />
51
+ <InputGroupAddon>
52
+ <InputGroupText>{props.placeholder}</InputGroupText>
53
+ </InputGroupAddon>
54
+ </InputGroup>
55
+ );
56
+ };