@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,289 @@
1
+ import { GenericObjectType } from "@rjsf/utils";
2
+ import {
3
+ CreateFieldConfigWithResourceProps,
4
+ FilteredObject,
5
+ FilterType,
6
+ UiSchema,
7
+ } from "../types";
8
+ import { lodash } from "@repo/ayasofyazilim-ui/lib/utils";
9
+
10
+ /**
11
+ *
12
+ */
13
+ export function bulkCreateUiSchema<T>({
14
+ elements,
15
+ config,
16
+ }: {
17
+ config: UiSchema<T>;
18
+ elements: Array<keyof T>;
19
+ }): UiSchema<T> {
20
+ const uiSchema = {};
21
+ for (const element of elements) {
22
+ Object.assign(uiSchema, { [element]: config });
23
+ }
24
+ return filterUndefinedAndEmpty(uiSchema);
25
+ }
26
+
27
+ /**
28
+ * Creates a UiSchema with internationalization resources and optional overrides.
29
+ */
30
+ export function createUiSchemaWithResource<T = unknown>({
31
+ resources,
32
+ schema,
33
+ extend,
34
+ name = "Form",
35
+ }: CreateFieldConfigWithResourceProps): UiSchema<T> {
36
+ const baseUiSchema = uiSchemaFromSchema({
37
+ object: schema,
38
+ resources,
39
+ name,
40
+ constantKey: name,
41
+ });
42
+
43
+ const targetSchema = lodash.get(baseUiSchema, name, {});
44
+
45
+ if (extend) {
46
+ // Lodash merge handles deep merging recursively and safely
47
+ return lodash.merge({}, targetSchema, extend);
48
+ }
49
+
50
+ return targetSchema;
51
+ }
52
+
53
+ /**
54
+ * Filters a JSON Schema based on include/exclude/fullExclude rules.
55
+ */
56
+ export function createSchemaWithFilters<T = string>({
57
+ schema,
58
+ filter,
59
+ }: {
60
+ filter: FilterType<T>;
61
+ schema: GenericObjectType;
62
+ }): GenericObjectType {
63
+ const { keys, type } = filter;
64
+ // Deep clone first to avoid mutating the original schema reference
65
+ const modifiedSchema = lodash.cloneDeep(schema);
66
+
67
+ const isWildCard = (key: string) => lodash.some(keys, (k) => k === `*${key}`);
68
+ const hasKey = (key: string) => lodash.includes(keys, key);
69
+
70
+ const filterRecursive = (
71
+ currentObj: GenericObjectType,
72
+ parentPath: string = ""
73
+ ): void => {
74
+ if (!currentObj.properties && !currentObj.items) return;
75
+
76
+ // Handle Properties
77
+ if (currentObj.properties) {
78
+ const keptKeys: string[] = [];
79
+
80
+ currentObj.properties = lodash.transform(
81
+ currentObj.properties,
82
+ (result, property, key) => {
83
+ const propertyKey = String(key);
84
+ const currentPath = parentPath
85
+ ? `${parentPath}.${propertyKey}`
86
+ : propertyKey;
87
+
88
+ let shouldKeep = false;
89
+
90
+ switch (type) {
91
+ case "include":
92
+ // Keep if explicitly included or if it's a wildcard match
93
+ shouldKeep = hasKey(currentPath) || isWildCard(currentPath);
94
+ break;
95
+ case "exclude":
96
+ case "fullExclude":
97
+ // Keep if NOT in the exclude list
98
+ shouldKeep = !hasKey(currentPath);
99
+ break;
100
+ }
101
+
102
+ if (shouldKeep) {
103
+ result[propertyKey] = property;
104
+ keptKeys.push(propertyKey);
105
+
106
+ // Recursive step for Objects and Arrays
107
+ if (property.type === "object") {
108
+ filterRecursive(property, currentPath);
109
+ } else if (property.type === "array" && property.items) {
110
+ filterRecursive(property.items, currentPath);
111
+ }
112
+ }
113
+ },
114
+ {} as GenericObjectType["properties"]
115
+ );
116
+
117
+ // Update or Remove 'required' array based on kept keys
118
+ if (currentObj.required) {
119
+ currentObj.required = lodash.intersection(
120
+ currentObj.required,
121
+ keptKeys
122
+ );
123
+ if (lodash.isEmpty(currentObj.required)) {
124
+ delete currentObj.required;
125
+ }
126
+ }
127
+ }
128
+ };
129
+
130
+ filterRecursive(modifiedSchema);
131
+ return modifiedSchema;
132
+ }
133
+
134
+ /**
135
+ * Recursively removes specific keys from any object or array.
136
+ */
137
+ export function removeFieldsfromGenericSchema<T>(
138
+ obj: T,
139
+ keysToRemove: string | string[]
140
+ ): T {
141
+ const keysArray = lodash.castArray(keysToRemove);
142
+
143
+ const transform = (value: unknown): unknown => {
144
+ if (lodash.isArray(value)) {
145
+ return lodash.map(value, transform);
146
+ }
147
+
148
+ if (lodash.isPlainObject(value)) {
149
+ return lodash.transform(
150
+ value as Record<string, unknown>,
151
+ (result, val, key) => {
152
+ if (!keysArray.includes(key)) {
153
+ result[key] = transform(val);
154
+ }
155
+ },
156
+ {} as Record<string, unknown>
157
+ );
158
+ }
159
+
160
+ return value;
161
+ };
162
+
163
+ return transform(obj) as T;
164
+ }
165
+
166
+ /**
167
+ * Merges two UISchema objects recursively.
168
+ */
169
+
170
+ export function mergeUISchemaObjects<
171
+ T extends UiSchema<T> | UiSchema,
172
+ U extends UiSchema<T> | UiSchema,
173
+ >(source: T, target: U): T & U {
174
+ // We pass {} as the first argument to ensure we create a new object
175
+ // rather than mutating 'source'.
176
+ return lodash.merge({}, source, target) as T & U;
177
+ }
178
+
179
+ /**
180
+ * Helper: Generates UiSchema structure from JSON Schema + Resources
181
+ */
182
+ function uiSchemaFromSchema({
183
+ name,
184
+ object,
185
+ resources,
186
+ constantKey,
187
+ }: {
188
+ constantKey: string;
189
+ name: string;
190
+ object: GenericObjectType;
191
+ resources: Record<string, string>;
192
+ }) {
193
+ const uiSchema: Record<string, any> = { [name]: {} };
194
+
195
+ // 1. Handle Objects
196
+ if (object?.type === "object" && object.properties) {
197
+ lodash.forEach(object.properties, (propValue, propKey) => {
198
+ const nestedSchema = uiSchemaFromSchema({
199
+ name: propKey,
200
+ object: propValue,
201
+ resources,
202
+ constantKey: `${constantKey}.${propKey}`,
203
+ });
204
+
205
+ // Merge nested results into the current name scope
206
+ Object.assign(uiSchema[name], {
207
+ "ui:title": resources[constantKey],
208
+ ...nestedSchema,
209
+ });
210
+ });
211
+ }
212
+
213
+ // 2. Handle Arrays
214
+ else if (object?.type === "array" && object.items?.properties) {
215
+ const items = lodash.reduce(
216
+ object.items.properties,
217
+ (acc, propValue, propKey) => {
218
+ const nested = uiSchemaFromSchema({
219
+ name: propKey,
220
+ object: propValue,
221
+ resources,
222
+ constantKey: `${constantKey}.${propKey}`,
223
+ });
224
+ return lodash.merge(acc, nested);
225
+ },
226
+ {}
227
+ );
228
+
229
+ Object.assign(uiSchema[name], {
230
+ "ui:title": resources[constantKey] || lodash.startCase(name),
231
+ items,
232
+ });
233
+ }
234
+
235
+ // 3. Handle Primitives (Plain fields)
236
+ else if (object) {
237
+ const getResource = (suffix?: string) =>
238
+ resources[suffix ? `${constantKey}.${suffix}` : constantKey];
239
+
240
+ const uiSchemaItem: Record<string, any> = {
241
+ "ui:title": getResource() || lodash.startCase(name),
242
+ "ui:placeholder": getResource("ui:placeholder"),
243
+ };
244
+
245
+ if (object.enum) {
246
+ uiSchemaItem["ui:enumNames"] = object.enum.map(
247
+ (key: string) => resources[`${constantKey}.${key}`] || key
248
+ );
249
+ uiSchemaItem["ui:options"] = {
250
+ label: true,
251
+ emptyValue: getResource("emptyValue"),
252
+ searchPlaceholder: getResource("searchPlaceholder"),
253
+ searchResultLabel: getResource("searchResultLabel"),
254
+ };
255
+ }
256
+
257
+ Object.assign(uiSchema[name], uiSchemaItem);
258
+ }
259
+
260
+ return filterUndefinedAndEmpty(uiSchema);
261
+ }
262
+
263
+ /**
264
+ * Recursively filters out undefined values and empty objects/arrays
265
+ */
266
+ function filterUndefinedAndEmpty<T>(obj: T): FilteredObject<T> {
267
+ if (!lodash.isObject(obj)) {
268
+ return obj as FilteredObject<T>;
269
+ }
270
+
271
+ // Use omitBy to filter undefined, then mapValues to recurse
272
+ const result = lodash.transform(
273
+ obj as unknown as object,
274
+ (acc: any, value, key) => {
275
+ const filteredValue = filterUndefinedAndEmpty(value);
276
+
277
+ const isValid =
278
+ filteredValue !== undefined &&
279
+ !(lodash.isPlainObject(filteredValue) && lodash.isEmpty(filteredValue));
280
+
281
+ if (isValid) {
282
+ acc[key] = filteredValue;
283
+ }
284
+ },
285
+ {}
286
+ );
287
+
288
+ return result as FilteredObject<T>;
289
+ }
@@ -0,0 +1,23 @@
1
+ import { FormValidation } from "@rjsf/utils";
2
+
3
+ export function customPasswordValidate<T>({
4
+ formData,
5
+ keyOne,
6
+ keyTwo,
7
+ errorMessage,
8
+ errors,
9
+ }: {
10
+ formData: T | undefined;
11
+ keyOne: keyof T;
12
+ keyTwo: keyof T;
13
+ errorMessage: string;
14
+ errors: FormValidation<T>;
15
+ }): FormValidation<T> {
16
+ if (!formData) {
17
+ return errors;
18
+ }
19
+ if (formData[keyOne] !== formData[keyTwo]) {
20
+ errors[keyTwo]?.addError(errorMessage);
21
+ }
22
+ return errors;
23
+ }
@@ -0,0 +1,77 @@
1
+ import { Checkbox } from "@repo/ayasofyazilim-ui/components/checkbox";
2
+ import { Label } from "@repo/ayasofyazilim-ui/components/label";
3
+ import { Switch } from "@repo/ayasofyazilim-ui/components/switch";
4
+ import { cn } from "@repo/ayasofyazilim-ui/lib/utils";
5
+ import { WidgetProps } from "@rjsf/utils";
6
+ import { ReactNode } from "react";
7
+
8
+ export const SwitchWidget = (props: WidgetProps) => {
9
+ const { id, name, onChange, value, defaultValue, disabled, uiSchema } = props;
10
+ const required = uiSchema?.["ui:required"] || props.required;
11
+ return (
12
+ <BooleanWrapper widgetProps={props}>
13
+ <Switch
14
+ id={id}
15
+ data-testid={id}
16
+ onCheckedChange={() => {
17
+ onChange(!value);
18
+ }}
19
+ checked={value}
20
+ defaultValue={value || defaultValue}
21
+ name={name}
22
+ disabled={disabled}
23
+ required={required}
24
+ />
25
+ </BooleanWrapper>
26
+ );
27
+ };
28
+
29
+ export const CheckboxWidget = (props: WidgetProps) => {
30
+ const { id, name, onChange, value, defaultValue, disabled, uiSchema } = props;
31
+ const required = uiSchema?.["ui:required"] || props.required;
32
+ return (
33
+ <BooleanWrapper widgetProps={props}>
34
+ <Checkbox
35
+ id={id}
36
+ data-testid={id}
37
+ onCheckedChange={() => {
38
+ onChange(!value);
39
+ }}
40
+ checked={value}
41
+ defaultValue={value || defaultValue}
42
+ name={name}
43
+ disabled={disabled}
44
+ required={required}
45
+ />
46
+ </BooleanWrapper>
47
+ );
48
+ };
49
+
50
+ function BooleanWrapper({
51
+ children,
52
+ widgetProps,
53
+ }: {
54
+ children: ReactNode;
55
+ widgetProps: WidgetProps;
56
+ }) {
57
+ const { id, className, label, uiSchema } = widgetProps;
58
+ const required = uiSchema?.["ui:required"] || widgetProps.required;
59
+ const displayLabel = uiSchema?.displayLabel;
60
+ return (
61
+ <div
62
+ data-wrapper="boolean"
63
+ className={cn(
64
+ "flex items-center gap-2 h-9 px-2 border rounded-md shadow-xs",
65
+ className
66
+ )}
67
+ >
68
+ {children}
69
+ {displayLabel !== false && (
70
+ <Label id={id} htmlFor={id} className="w-full">
71
+ {label}
72
+ {required && "*"}
73
+ </Label>
74
+ )}
75
+ </div>
76
+ );
77
+ }
@@ -0,0 +1,274 @@
1
+ import { ChevronsUpDown } from "lucide-react";
2
+ import { WidgetProps } from "@rjsf/utils";
3
+ import { CheckIcon } from "lucide-react";
4
+ import { Dispatch, ReactNode, SetStateAction, useState } from "react";
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
+ Drawer,
17
+ DrawerContent,
18
+ DrawerTrigger,
19
+ } from "@repo/ayasofyazilim-ui/components/drawer";
20
+ import {
21
+ Popover,
22
+ PopoverContent,
23
+ PopoverTrigger,
24
+ } from "@repo/ayasofyazilim-ui/components/popover";
25
+ import { useMediaQuery } from "@repo/ayasofyazilim-ui/hooks/use-media-query";
26
+ import { cn } from "@repo/ayasofyazilim-ui/lib/utils";
27
+
28
+ type BadgeOptions = { className?: string; showValue?: boolean; label?: string };
29
+
30
+ type CustomComboboxProps<T> = {
31
+ emptyValue?: string;
32
+ list: Array<T> | null | undefined;
33
+ onValueChange?: Dispatch<SetStateAction<T | null | undefined>>;
34
+ searchPlaceholder?: string;
35
+ searchResultLabel?: string;
36
+ selectIdentifier: keyof T;
37
+ selectLabel: keyof T;
38
+ disabledItems?: T[keyof T][];
39
+ badges?: Partial<Record<keyof T, BadgeOptions>>;
40
+ customItemRenderer?: (values: T) => ReactNode | undefined | string;
41
+ } & WidgetProps;
42
+
43
+ export function CustomCombobox<T>(props: CustomComboboxProps<T>) {
44
+ const {
45
+ value,
46
+ defaultValue,
47
+ list,
48
+ selectIdentifier,
49
+ selectLabel,
50
+ disabled,
51
+ emptyValue = "Please select",
52
+ } = props;
53
+ const isDesktop = useMediaQuery("(min-width: 768px)");
54
+ const [open, setOpen] = useState(false);
55
+ const fieldValue = value || defaultValue;
56
+ const fieldValueDisplayName = list?.find(
57
+ (x) => x[selectIdentifier] === fieldValue
58
+ )?.[selectLabel];
59
+ const DesktopContent = (
60
+ <Popover open={open} onOpenChange={setOpen} modal>
61
+ <PopoverTrigger
62
+ asChild
63
+ disabled={disabled}
64
+ className={cn(disabled && "cursor-not-allowed")}
65
+ >
66
+ <Button
67
+ id={props.id}
68
+ data-testid={props.id}
69
+ type="button"
70
+ variant="outline"
71
+ role="combobox"
72
+ className={cn(
73
+ "text-muted-foreground w-full justify-between font-normal shadow-xs hover:bg-white overflow-hidden",
74
+ fieldValueDisplayName && "text-foreground",
75
+ disabled &&
76
+ "disabled:pointer-events-auto hover:bg-background hover:text-muted-foreground"
77
+ )}
78
+ >
79
+ <span className="truncate has-[role=dialog]:max-w-xs">
80
+ {typeof fieldValueDisplayName !== "undefined" &&
81
+ fieldValueDisplayName !== null
82
+ ? fieldValueDisplayName.toString()
83
+ : emptyValue}
84
+ </span>
85
+ <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
86
+ </Button>
87
+ </PopoverTrigger>
88
+ <PopoverContent className="p-0">
89
+ <List {...props} setOpen={setOpen} />
90
+ </PopoverContent>
91
+ </Popover>
92
+ );
93
+
94
+ const MobileContent = (
95
+ <Drawer open={open} onOpenChange={setOpen}>
96
+ <DrawerTrigger asChild>
97
+ <Button
98
+ data-testid={props.id}
99
+ type="button"
100
+ disabled={disabled}
101
+ variant="outline"
102
+ className={cn(
103
+ "text-muted-foreground w-full justify-between font-normal shadow-xs hover:bg-white",
104
+ fieldValueDisplayName && "text-black"
105
+ )}
106
+ >
107
+ {fieldValueDisplayName
108
+ ? fieldValueDisplayName.toString()
109
+ : emptyValue}
110
+ <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
111
+ </Button>
112
+ </DrawerTrigger>
113
+ <DrawerContent>
114
+ <div className="mt-4 border-t">
115
+ <List {...props} setOpen={setOpen} />
116
+ </div>
117
+ </DrawerContent>
118
+ </Drawer>
119
+ );
120
+
121
+ return isDesktop ? DesktopContent : MobileContent;
122
+ }
123
+
124
+ function List<T>({
125
+ setOpen,
126
+ ...props
127
+ }: CustomComboboxProps<T> & {
128
+ setOpen: (open: boolean) => void;
129
+ }) {
130
+ const {
131
+ uiSchema,
132
+ onChange,
133
+ value,
134
+ list,
135
+ selectIdentifier,
136
+ selectLabel,
137
+ searchPlaceholder,
138
+ searchResultLabel,
139
+ onValueChange,
140
+ badges,
141
+ customItemRenderer,
142
+ } = props;
143
+ const uiOptions = uiSchema?.["ui:options"];
144
+ return (
145
+ <Command
146
+ filter={(value, search) => {
147
+ const filterResult = list?.find(
148
+ (i) =>
149
+ (i[selectIdentifier] as string).toString()?.toLocaleLowerCase() ===
150
+ value.toLocaleLowerCase()
151
+ )?.[selectLabel] as string;
152
+ if (
153
+ value.includes(search) ||
154
+ filterResult?.toLocaleLowerCase().includes(search.toLocaleLowerCase())
155
+ )
156
+ return 1;
157
+ return 0;
158
+ }}
159
+ >
160
+ <CommandInput
161
+ data-testid={`${props.id}_search`}
162
+ placeholder={searchPlaceholder || "Search..."}
163
+ className="h-9"
164
+ />
165
+ <CommandList className="w-full min-w-full max-w-full">
166
+ <CommandEmpty>{searchResultLabel || "0 search result."}</CommandEmpty>
167
+ <CommandGroup>
168
+ {list?.map((item: T, index) => (
169
+ <CommandItem
170
+ data-testid={`${props.id}_${index}`}
171
+ disabled={props.disabledItems?.includes(item[selectIdentifier])}
172
+ onSelect={() => {
173
+ onChange(
174
+ uiOptions?.allowEmpty !== false
175
+ ? item[selectIdentifier] === value
176
+ ? undefined
177
+ : item[selectIdentifier]
178
+ : item[selectIdentifier]
179
+ );
180
+ if (onValueChange)
181
+ onValueChange(
182
+ list.find((i) => i[selectIdentifier] === value)
183
+ );
184
+ setOpen(false);
185
+ }}
186
+ key={JSON.stringify(item[selectIdentifier])}
187
+ value={item[selectIdentifier] as string}
188
+ >
189
+ {item[selectIdentifier] === value && (
190
+ <CheckIcon className={cn("mr-2 h-4 w-4")} />
191
+ )}
192
+ {customItemRenderer ? (
193
+ customItemRenderer(item)
194
+ ) : (
195
+ <>
196
+ {item[selectLabel] as string}
197
+ {badges && (
198
+ <div className="ml-auto">
199
+ {Object.keys(badges).map((badgeKey) => {
200
+ const badgeOptions = badges[badgeKey as keyof T];
201
+ if (!badgeOptions) return null;
202
+ return (
203
+ <Badge
204
+ key={badgeKey}
205
+ variant="outline"
206
+ className={cn("ml-2", badgeOptions.className)}
207
+ >
208
+ {badgeOptions.showValue !== false &&
209
+ (item[badgeKey as keyof T] as string)}
210
+ {badgeOptions.label && badgeOptions.label}
211
+ </Badge>
212
+ );
213
+ })}
214
+ </div>
215
+ )}
216
+ </>
217
+ )}
218
+ </CommandItem>
219
+ ))}
220
+ </CommandGroup>
221
+ </CommandList>
222
+ </Command>
223
+ );
224
+ }
225
+
226
+ export function CustomComboboxWidget<T>({
227
+ languageData,
228
+ selectLabel,
229
+ selectIdentifier,
230
+ list,
231
+ onChange,
232
+ disabledItems,
233
+ badges,
234
+ customItemRenderer,
235
+ }: {
236
+ languageData: {
237
+ "Select.Placeholder": string;
238
+ "Select.ResultLabel": string;
239
+ "Select.EmptyValue": string;
240
+ };
241
+ selectIdentifier: keyof T;
242
+ selectLabel: keyof T;
243
+ list: T[];
244
+ onChange?: (value: T | null | undefined) => void;
245
+ disabledItems?: T[keyof T][];
246
+ badges?: CustomComboboxProps<T>["badges"];
247
+ customItemRenderer?: CustomComboboxProps<T>["customItemRenderer"];
248
+ }) {
249
+ function Widget(props: WidgetProps) {
250
+ const { uiSchema } = props;
251
+ const uiList = uiSchema?.["ui:optionList"];
252
+ return (
253
+ <CustomCombobox<T>
254
+ {...props}
255
+ list={uiList || list}
256
+ onChange={(value) => {
257
+ props.onChange(value);
258
+ if (onChange) {
259
+ onChange(list.find((i) => i[selectIdentifier] === value));
260
+ }
261
+ }}
262
+ customItemRenderer={customItemRenderer}
263
+ searchPlaceholder={languageData["Select.Placeholder"]}
264
+ searchResultLabel={languageData["Select.ResultLabel"]}
265
+ emptyValue={languageData["Select.EmptyValue"]}
266
+ selectIdentifier={selectIdentifier}
267
+ selectLabel={selectLabel}
268
+ disabledItems={disabledItems}
269
+ badges={badges}
270
+ />
271
+ );
272
+ }
273
+ return Widget;
274
+ }