shadcn-rails 0.2.0 → 0.2.1

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 (152) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -2
  3. data/README.md +21 -8
  4. data/__mocks__/@floating-ui/dom.js +67 -0
  5. data/app/assets/javascripts/shadcn/controllers/combobox_controller.js +23 -2
  6. data/app/assets/javascripts/shadcn/controllers/context_menu_controller.js +4 -31
  7. data/app/assets/javascripts/shadcn/controllers/dropdown_controller.js +32 -41
  8. data/app/assets/javascripts/shadcn/controllers/hover_card_controller.js +29 -55
  9. data/app/assets/javascripts/shadcn/controllers/popover_controller.js +29 -54
  10. data/app/assets/javascripts/shadcn/controllers/select_controller.js +26 -8
  11. data/app/assets/javascripts/shadcn/controllers/tooltip_controller.js +28 -59
  12. data/app/assets/javascripts/shadcn/index.js +7 -1
  13. data/app/assets/javascripts/shadcn/utils/floating.js +179 -0
  14. data/app/assets/stylesheets/shadcn/base.css +32 -0
  15. data/app/components/shadcn/accordion_component.html.erb +8 -0
  16. data/app/components/shadcn/accordion_component.rb +6 -15
  17. data/app/components/shadcn/alert_component.html.erb +6 -0
  18. data/app/components/shadcn/alert_component.rb +0 -18
  19. data/app/components/shadcn/alert_dialog_component.html.erb +12 -0
  20. data/app/components/shadcn/alert_dialog_component.rb +7 -27
  21. data/app/components/shadcn/aspect_ratio_component.html.erb +7 -0
  22. data/app/components/shadcn/aspect_ratio_component.rb +4 -19
  23. data/app/components/shadcn/avatar_component.html.erb +20 -0
  24. data/app/components/shadcn/avatar_component.rb +8 -36
  25. data/app/components/shadcn/badge_component.html.erb +1 -0
  26. data/app/components/shadcn/badge_component.rb +0 -11
  27. data/app/components/shadcn/base_component.rb +15 -2
  28. data/app/components/shadcn/breadcrumb_component.html.erb +5 -0
  29. data/app/components/shadcn/breadcrumb_component.rb +6 -16
  30. data/app/components/shadcn/button_component.html.erb +18 -0
  31. data/app/components/shadcn/button_component.rb +1 -41
  32. data/app/components/shadcn/card_component.html.erb +8 -0
  33. data/app/components/shadcn/card_component.rb +2 -6
  34. data/app/components/shadcn/checkbox_component.html.erb +32 -0
  35. data/app/components/shadcn/checkbox_component.rb +4 -43
  36. data/app/components/shadcn/collapsible_component.html.erb +8 -0
  37. data/app/components/shadcn/collapsible_component.rb +6 -15
  38. data/app/components/shadcn/context_menu_component.html.erb +11 -0
  39. data/app/components/shadcn/context_menu_component.rb +6 -26
  40. data/app/components/shadcn/dialog_component.html.erb +14 -0
  41. data/app/components/shadcn/dialog_component.rb +8 -29
  42. data/app/components/shadcn/drawer_component.html.erb +12 -0
  43. data/app/components/shadcn/drawer_component.rb +7 -27
  44. data/app/components/shadcn/dropdown_menu_component.html.erb +14 -0
  45. data/app/components/shadcn/dropdown_menu_component.rb +9 -29
  46. data/app/components/shadcn/field_component.rb +7 -8
  47. data/app/components/shadcn/hover_card_component.html.erb +12 -0
  48. data/app/components/shadcn/hover_card_component.rb +7 -26
  49. data/app/components/shadcn/input_component.html.erb +18 -0
  50. data/app/components/shadcn/input_component.rb +2 -27
  51. data/app/components/shadcn/input_otp_component.rb +3 -3
  52. data/app/components/shadcn/kbd_component.html.erb +1 -0
  53. data/app/components/shadcn/kbd_component.rb +3 -10
  54. data/app/components/shadcn/label_component.html.erb +3 -0
  55. data/app/components/shadcn/label_component.rb +2 -18
  56. data/app/components/shadcn/menubar_component.html.erb +6 -0
  57. data/app/components/shadcn/menubar_component.rb +4 -15
  58. data/app/components/shadcn/native_select_component.html.erb +22 -0
  59. data/app/components/shadcn/native_select_component.rb +9 -39
  60. data/app/components/shadcn/navigation_menu_component.html.erb +6 -0
  61. data/app/components/shadcn/navigation_menu_component.rb +4 -15
  62. data/app/components/shadcn/pagination_component.html.erb +5 -0
  63. data/app/components/shadcn/pagination_component.rb +11 -15
  64. data/app/components/shadcn/popover_component.html.erb +15 -0
  65. data/app/components/shadcn/popover_component.rb +10 -30
  66. data/app/components/shadcn/progress_component.html.erb +13 -0
  67. data/app/components/shadcn/progress_component.rb +6 -26
  68. data/app/components/shadcn/radio_group_component.html.erb +8 -0
  69. data/app/components/shadcn/radio_group_component.rb +12 -26
  70. data/app/components/shadcn/scroll_area_component.html.erb +7 -0
  71. data/app/components/shadcn/scroll_area_component.rb +4 -16
  72. data/app/components/shadcn/select_component.html.erb +46 -0
  73. data/app/components/shadcn/select_component.rb +6 -80
  74. data/app/components/shadcn/separator_component.html.erb +5 -0
  75. data/app/components/shadcn/separator_component.rb +6 -14
  76. data/app/components/shadcn/sheet_component.html.erb +12 -0
  77. data/app/components/shadcn/sheet_component.rb +7 -27
  78. data/app/components/shadcn/sidebar_component.rb +2 -2
  79. data/app/components/shadcn/skeleton_component.html.erb +1 -0
  80. data/app/components/shadcn/skeleton_component.rb +4 -2
  81. data/app/components/shadcn/slider_component.html.erb +12 -0
  82. data/app/components/shadcn/slider_component.rb +2 -21
  83. data/app/components/shadcn/spinner_component.html.erb +18 -0
  84. data/app/components/shadcn/spinner_component.rb +2 -30
  85. data/app/components/shadcn/switch_component.html.erb +72 -0
  86. data/app/components/shadcn/switch_component.rb +4 -82
  87. data/app/components/shadcn/table_component.html.erb +9 -0
  88. data/app/components/shadcn/table_component.rb +2 -10
  89. data/app/components/shadcn/tabs_component.html.erb +8 -0
  90. data/app/components/shadcn/tabs_component.rb +4 -17
  91. data/app/components/shadcn/textarea_component.html.erb +13 -0
  92. data/app/components/shadcn/textarea_component.rb +6 -22
  93. data/app/components/shadcn/toast_component.html.erb +36 -0
  94. data/app/components/shadcn/toast_component.rb +6 -54
  95. data/app/components/shadcn/toggle_component.html.erb +12 -0
  96. data/app/components/shadcn/toggle_component.rb +6 -21
  97. data/app/components/shadcn/toggle_group_component.html.erb +14 -0
  98. data/app/components/shadcn/toggle_group_component.rb +6 -29
  99. data/app/components/shadcn/tooltip_component.html.erb +20 -0
  100. data/app/components/shadcn/tooltip_component.rb +13 -38
  101. data/lib/generators/shadcn/add/USAGE +24 -0
  102. data/lib/generators/shadcn/add/add_generator.rb +279 -0
  103. data/lib/generators/shadcn/install/USAGE +22 -0
  104. data/lib/generators/shadcn/install/install_generator.rb +8 -3
  105. data/lib/generators/shadcn/install/templates/initializer.rb.tt +7 -27
  106. data/lib/generators/shadcn/install/templates/shadcn.yml.tt +15 -31
  107. data/lib/shadcn/rails/version.rb +1 -1
  108. metadata +47 -45
  109. data/.dockerignore +0 -40
  110. data/CLAUDE.md +0 -612
  111. data/PROGRESS.md +0 -495
  112. data/Rakefile +0 -95
  113. data/__tests__/controllers/__snapshots__/calendar_controller.test.js.snap +0 -13
  114. data/__tests__/controllers/__snapshots__/popover_controller.test.js.snap +0 -46
  115. data/__tests__/controllers/__snapshots__/sheet_controller.test.js.snap +0 -111
  116. data/__tests__/controllers/__snapshots__/tabs_controller.test.js.snap +0 -27
  117. data/__tests__/controllers/accordion_controller.test.js +0 -904
  118. data/__tests__/controllers/calendar_controller.test.js +0 -1370
  119. data/__tests__/controllers/carousel_controller.test.js +0 -912
  120. data/__tests__/controllers/checkbox_controller.test.js +0 -454
  121. data/__tests__/controllers/collapsible_controller.test.js +0 -407
  122. data/__tests__/controllers/combobox_controller.test.js +0 -971
  123. data/__tests__/controllers/context_menu_controller.test.js +0 -905
  124. data/__tests__/controllers/date_picker_controller.test.js +0 -636
  125. data/__tests__/controllers/dialog_controller.test.js +0 -878
  126. data/__tests__/controllers/drawer_controller.test.js +0 -995
  127. data/__tests__/controllers/menubar_controller.test.js +0 -737
  128. data/__tests__/controllers/navigation_menu_controller.test.js +0 -599
  129. data/__tests__/controllers/popover_controller.test.js +0 -982
  130. data/__tests__/controllers/radio_group_controller.test.js +0 -640
  131. data/__tests__/controllers/resizable_controller.test.js +0 -680
  132. data/__tests__/controllers/select_controller.test.js +0 -678
  133. data/__tests__/controllers/sheet_controller.test.js +0 -986
  134. data/__tests__/controllers/slider_controller.test.js +0 -1036
  135. data/__tests__/controllers/switch_controller.test.js +0 -424
  136. data/__tests__/controllers/tabs_controller.test.js +0 -907
  137. data/__tests__/controllers/toggle_group_controller.test.js +0 -839
  138. data/__tests__/controllers/tooltip_controller.test.js +0 -808
  139. data/__tests__/helpers/stimulus-test-helper.js +0 -203
  140. data/babel.config.cjs +0 -5
  141. data/bin/bump +0 -321
  142. data/bin/console +0 -11
  143. data/bin/release +0 -205
  144. data/bin/setup +0 -8
  145. data/bin/test +0 -75
  146. data/jest.config.js +0 -19
  147. data/jest.setup.js +0 -8
  148. data/lib/generators/shadcn/component/component_generator.rb +0 -188
  149. data/lib/generators/shadcn/theme/theme_generator.rb +0 -128
  150. data/package-lock.json +0 -7438
  151. data/package.json +0 -71
  152. data/rollup.config.js +0 -29
@@ -1,454 +0,0 @@
1
- import { Application } from "@hotwired/stimulus"
2
- import CheckboxController from "../../app/assets/javascripts/shadcn/controllers/checkbox_controller.js"
3
- import { setupController, cleanupController, click, nextFrame, keydown } from '../helpers/stimulus-test-helper.js'
4
-
5
- describe("CheckboxController", () => {
6
- let application
7
- let element
8
- let controller
9
-
10
- afterEach(() => {
11
- cleanupController(application)
12
- })
13
-
14
- describe("basic rendering and initialization", () => {
15
- const basicHTML = `
16
- <button data-controller="shadcn--checkbox"
17
- data-shadcn--checkbox-checked-value="false"
18
- data-shadcn--checkbox-name-value="agree"
19
- type="button"
20
- role="checkbox"
21
- aria-checked="false"
22
- data-action="click->shadcn--checkbox#toggle">
23
- <svg style="opacity: 0;">✓</svg>
24
- </button>
25
- `
26
-
27
- beforeEach(async () => {
28
- const setup = await setupController(CheckboxController, basicHTML, 'shadcn--checkbox')
29
- application = setup.application
30
- element = setup.element
31
- controller = setup.controller
32
- })
33
-
34
- test("initializes with unchecked state", () => {
35
- expect(controller.checkedValue).toBe(false)
36
- })
37
-
38
- test("sets data-state unchecked on element", () => {
39
- expect(element.dataset.state).toBe("unchecked")
40
- })
41
-
42
- test("sets aria-checked false on element", () => {
43
- expect(element.getAttribute("aria-checked")).toBe("false")
44
- })
45
-
46
- test("hides checkmark icon initially", () => {
47
- const svg = element.querySelector("svg")
48
- expect(svg.style.opacity).toBe("0")
49
- })
50
-
51
- test("initializes with name value", () => {
52
- expect(controller.nameValue).toBe("agree")
53
- })
54
- })
55
-
56
- describe("toggle functionality", () => {
57
- const toggleHTML = `
58
- <div>
59
- <button data-controller="shadcn--checkbox"
60
- data-shadcn--checkbox-checked-value="false"
61
- data-shadcn--checkbox-name-value="terms"
62
- type="button"
63
- role="checkbox"
64
- data-action="click->shadcn--checkbox#toggle">
65
- <svg style="opacity: 0;">✓</svg>
66
- </button>
67
- <input type="hidden" name="terms" value="0">
68
- </div>
69
- `
70
-
71
- beforeEach(async () => {
72
- const setup = await setupController(CheckboxController, toggleHTML, 'shadcn--checkbox')
73
- application = setup.application
74
- element = setup.element
75
- controller = setup.controller
76
- })
77
-
78
- test("toggles from unchecked to checked", async () => {
79
- controller.toggle()
80
- await nextFrame()
81
-
82
- expect(controller.checkedValue).toBe(true)
83
- })
84
-
85
- test("toggles from checked to unchecked", async () => {
86
- controller.checkedValue = true
87
- controller.toggle()
88
- await nextFrame()
89
-
90
- expect(controller.checkedValue).toBe(false)
91
- })
92
-
93
- test("updates data-state on toggle", async () => {
94
- controller.toggle()
95
- await nextFrame()
96
-
97
- expect(element.dataset.state).toBe("checked")
98
- })
99
-
100
- test("updates aria-checked on toggle", async () => {
101
- controller.toggle()
102
- await nextFrame()
103
-
104
- expect(element.getAttribute("aria-checked")).toBe("true")
105
- })
106
-
107
- test("shows checkmark icon when checked", async () => {
108
- controller.toggle()
109
- await nextFrame()
110
-
111
- const svg = element.querySelector("svg")
112
- expect(svg.style.opacity).toBe("1")
113
- })
114
-
115
- test("hides checkmark icon when unchecked", async () => {
116
- controller.checkedValue = true
117
- controller.updateState()
118
- controller.toggle()
119
- await nextFrame()
120
-
121
- const svg = element.querySelector("svg")
122
- expect(svg.style.opacity).toBe("0")
123
- })
124
-
125
- test("dispatches change event on toggle", async () => {
126
- let eventDetail = null
127
- element.addEventListener("shadcn--checkbox:change", (e) => {
128
- eventDetail = e.detail
129
- })
130
-
131
- controller.toggle()
132
- await nextFrame()
133
-
134
- expect(eventDetail).not.toBeNull()
135
- expect(eventDetail.checked).toBe(true)
136
- })
137
-
138
- test("dispatches change event with false when unchecking", async () => {
139
- controller.checkedValue = true
140
- let eventDetail = null
141
- element.addEventListener("shadcn--checkbox:change", (e) => {
142
- eventDetail = e.detail
143
- })
144
-
145
- controller.toggle()
146
- await nextFrame()
147
-
148
- expect(eventDetail.checked).toBe(false)
149
- })
150
- })
151
-
152
- describe("hidden input synchronization", () => {
153
- const inputSyncHTML = `
154
- <div>
155
- <button data-controller="shadcn--checkbox"
156
- data-shadcn--checkbox-checked-value="false"
157
- data-shadcn--checkbox-name-value="subscribe"
158
- type="button"
159
- data-action="click->shadcn--checkbox#toggle">
160
- <svg style="opacity: 0;">✓</svg>
161
- </button>
162
- <input type="hidden" name="subscribe" value="0">
163
- </div>
164
- `
165
-
166
- beforeEach(async () => {
167
- const setup = await setupController(CheckboxController, inputSyncHTML, 'shadcn--checkbox')
168
- application = setup.application
169
- element = setup.element
170
- controller = setup.controller
171
- })
172
-
173
- test("updates hidden input to 1 when checked", async () => {
174
- controller.toggle()
175
- await nextFrame()
176
-
177
- const input = element.parentElement.querySelector('input[name="subscribe"]')
178
- expect(input.value).toBe("1")
179
- })
180
-
181
- test("updates hidden input to 0 when unchecked", async () => {
182
- controller.checkedValue = true
183
- controller.updateHiddenInput()
184
-
185
- controller.toggle()
186
- await nextFrame()
187
-
188
- const input = element.parentElement.querySelector('input[name="subscribe"]')
189
- expect(input.value).toBe("0")
190
- })
191
- })
192
-
193
- describe("initial checked state", () => {
194
- const checkedHTML = `
195
- <div>
196
- <button data-controller="shadcn--checkbox"
197
- data-shadcn--checkbox-checked-value="true"
198
- data-shadcn--checkbox-name-value="remember"
199
- type="button"
200
- role="checkbox"
201
- aria-checked="true"
202
- data-action="click->shadcn--checkbox#toggle">
203
- <svg style="opacity: 1;">✓</svg>
204
- </button>
205
- <input type="hidden" name="remember" value="1">
206
- </div>
207
- `
208
-
209
- beforeEach(async () => {
210
- const setup = await setupController(CheckboxController, checkedHTML, 'shadcn--checkbox')
211
- application = setup.application
212
- element = setup.element
213
- controller = setup.controller
214
- })
215
-
216
- test("initializes with checked state", () => {
217
- expect(controller.checkedValue).toBe(true)
218
- })
219
-
220
- test("sets data-state checked on init", () => {
221
- expect(element.dataset.state).toBe("checked")
222
- })
223
-
224
- test("sets aria-checked true on init", () => {
225
- expect(element.getAttribute("aria-checked")).toBe("true")
226
- })
227
-
228
- test("shows checkmark on init", () => {
229
- const svg = element.querySelector("svg")
230
- expect(svg.style.opacity).toBe("1")
231
- })
232
- })
233
-
234
- describe("programmatic value change", () => {
235
- const programmaticHTML = `
236
- <button data-controller="shadcn--checkbox"
237
- data-shadcn--checkbox-checked-value="false"
238
- type="button">
239
- <svg style="opacity: 0;">✓</svg>
240
- </button>
241
- `
242
-
243
- beforeEach(async () => {
244
- const setup = await setupController(CheckboxController, programmaticHTML, 'shadcn--checkbox')
245
- application = setup.application
246
- element = setup.element
247
- controller = setup.controller
248
- })
249
-
250
- test("updates UI when checkedValue changes", async () => {
251
- controller.checkedValue = true
252
- await nextFrame()
253
-
254
- expect(element.dataset.state).toBe("checked")
255
- expect(element.getAttribute("aria-checked")).toBe("true")
256
- })
257
-
258
- test("checkedValueChanged callback updates state", async () => {
259
- const updateStateSpy = jest.spyOn(controller, 'updateState')
260
-
261
- controller.checkedValue = true
262
- await nextFrame()
263
-
264
- expect(updateStateSpy).toHaveBeenCalled()
265
- })
266
- })
267
-
268
- describe("without name value", () => {
269
- const noNameHTML = `
270
- <div>
271
- <button data-controller="shadcn--checkbox"
272
- data-shadcn--checkbox-checked-value="false"
273
- type="button"
274
- data-action="click->shadcn--checkbox#toggle">
275
- <svg style="opacity: 0;">✓</svg>
276
- </button>
277
- </div>
278
- `
279
-
280
- beforeEach(async () => {
281
- const setup = await setupController(CheckboxController, noNameHTML, 'shadcn--checkbox')
282
- application = setup.application
283
- element = setup.element
284
- controller = setup.controller
285
- })
286
-
287
- test("works without name value", async () => {
288
- expect(() => {
289
- controller.toggle()
290
- }).not.toThrow()
291
-
292
- expect(controller.checkedValue).toBe(true)
293
- })
294
-
295
- test("does not try to update non-existent hidden input", () => {
296
- expect(() => {
297
- controller.updateHiddenInput()
298
- }).not.toThrow()
299
- })
300
- })
301
-
302
- describe("without SVG icon", () => {
303
- const noIconHTML = `
304
- <button data-controller="shadcn--checkbox"
305
- data-shadcn--checkbox-checked-value="false"
306
- type="button"
307
- data-action="click->shadcn--checkbox#toggle">
308
- Check
309
- </button>
310
- `
311
-
312
- beforeEach(async () => {
313
- const setup = await setupController(CheckboxController, noIconHTML, 'shadcn--checkbox')
314
- application = setup.application
315
- element = setup.element
316
- controller = setup.controller
317
- })
318
-
319
- test("works without SVG icon", async () => {
320
- expect(() => {
321
- controller.toggle()
322
- }).not.toThrow()
323
-
324
- expect(controller.checkedValue).toBe(true)
325
- })
326
-
327
- test("updateState does not throw without SVG", () => {
328
- expect(() => {
329
- controller.updateState()
330
- }).not.toThrow()
331
- })
332
- })
333
-
334
- describe("multiple toggles", () => {
335
- const multipleHTML = `
336
- <button data-controller="shadcn--checkbox"
337
- data-shadcn--checkbox-checked-value="false"
338
- type="button"
339
- data-action="click->shadcn--checkbox#toggle">
340
- <svg style="opacity: 0;">✓</svg>
341
- </button>
342
- `
343
-
344
- beforeEach(async () => {
345
- const setup = await setupController(CheckboxController, multipleHTML, 'shadcn--checkbox')
346
- application = setup.application
347
- element = setup.element
348
- controller = setup.controller
349
- })
350
-
351
- test("handles multiple rapid toggles", async () => {
352
- controller.toggle() // true
353
- controller.toggle() // false
354
- controller.toggle() // true
355
- await nextFrame()
356
-
357
- expect(controller.checkedValue).toBe(true)
358
- })
359
-
360
- test("emits change event for each toggle", async () => {
361
- let changeCount = 0
362
- element.addEventListener("shadcn--checkbox:change", () => {
363
- changeCount++
364
- })
365
-
366
- controller.toggle()
367
- controller.toggle()
368
- controller.toggle()
369
- await nextFrame()
370
-
371
- expect(changeCount).toBe(3)
372
- })
373
-
374
- test("final state is correct after even number of toggles", async () => {
375
- controller.toggle()
376
- controller.toggle()
377
- controller.toggle()
378
- controller.toggle()
379
- await nextFrame()
380
-
381
- expect(controller.checkedValue).toBe(false)
382
- })
383
- })
384
-
385
- describe("click handler", () => {
386
- const clickHTML = `
387
- <button data-controller="shadcn--checkbox"
388
- data-shadcn--checkbox-checked-value="false"
389
- type="button"
390
- data-action="click->shadcn--checkbox#toggle">
391
- <svg style="opacity: 0;">✓</svg>
392
- </button>
393
- `
394
-
395
- beforeEach(async () => {
396
- const setup = await setupController(CheckboxController, clickHTML, 'shadcn--checkbox')
397
- application = setup.application
398
- element = setup.element
399
- controller = setup.controller
400
- })
401
-
402
- test("toggles on click", async () => {
403
- click(element)
404
- await nextFrame()
405
-
406
- expect(controller.checkedValue).toBe(true)
407
- })
408
-
409
- test("toggles again on second click", async () => {
410
- click(element)
411
- await nextFrame()
412
- click(element)
413
- await nextFrame()
414
-
415
- expect(controller.checkedValue).toBe(false)
416
- })
417
- })
418
-
419
- describe("edge cases", () => {
420
- const edgeCaseHTML = `
421
- <div>
422
- <button data-controller="shadcn--checkbox"
423
- data-shadcn--checkbox-checked-value="false"
424
- data-shadcn--checkbox-name-value="test"
425
- type="button"
426
- data-action="click->shadcn--checkbox#toggle">
427
- <svg style="opacity: 0;">✓</svg>
428
- </button>
429
- <input type="hidden" name="other" value="0">
430
- </div>
431
- `
432
-
433
- beforeEach(async () => {
434
- const setup = await setupController(CheckboxController, edgeCaseHTML, 'shadcn--checkbox')
435
- application = setup.application
436
- element = setup.element
437
- controller = setup.controller
438
- })
439
-
440
- test("does not update input with different name", async () => {
441
- controller.toggle()
442
- await nextFrame()
443
-
444
- const otherInput = element.parentElement.querySelector('input[name="other"]')
445
- expect(otherInput.value).toBe("0")
446
- })
447
-
448
- test("gracefully handles missing input with matching name", async () => {
449
- expect(() => {
450
- controller.toggle()
451
- }).not.toThrow()
452
- })
453
- })
454
- })