hakumi_components 0.1.16.pre → 0.1.17.pre
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.
- checksums.yaml +4 -4
- data/README.md +169 -23
- data/app/assets/javascripts/hakumi_components.js +12 -12
- data/app/assets/stylesheets/hakumi_components.css +1 -1
- data/app/components/hakumi/alert/component.html.erb +12 -8
- data/app/components/hakumi/alert/component.rb +18 -62
- data/app/components/hakumi/base_component.rb +13 -0
- data/app/components/hakumi/card/component.html.erb +14 -22
- data/app/components/hakumi/card/component.rb +38 -31
- data/app/components/hakumi/checkbox/component.html.erb +39 -21
- data/app/components/hakumi/checkbox/component.rb +12 -2
- data/app/components/hakumi/collapse/component.html.erb +2 -2
- data/app/components/hakumi/collapse/component.rb +1 -1
- data/app/components/hakumi/collapse/panel/component.rb +9 -0
- data/app/components/hakumi/color_picker/component.rb +0 -4
- data/app/components/hakumi/drawer/component.html.erb +7 -7
- data/app/components/hakumi/drawer/component.rb +12 -19
- data/app/components/hakumi/input/component.rb +0 -2
- data/app/components/hakumi/input/text_area/component.rb +0 -2
- data/app/components/hakumi/input_number/component.rb +3 -4
- data/app/components/hakumi/mentions/component.rb +0 -1
- data/app/components/hakumi/modal/component.html.erb +40 -0
- data/app/components/hakumi/modal/component.rb +24 -102
- data/app/components/hakumi/modal/confirm/component.html.erb +23 -0
- data/app/components/hakumi/modal/confirm/component.rb +23 -41
- data/app/components/hakumi/modal/error/component.rb +12 -11
- data/app/components/hakumi/modal/info/component.rb +12 -11
- data/app/components/hakumi/modal/success/component.rb +12 -11
- data/app/components/hakumi/modal/warning/component.rb +15 -10
- data/app/components/hakumi/popconfirm/component.html.erb +25 -25
- data/app/components/hakumi/popconfirm/component.rb +11 -27
- data/app/components/hakumi/rate/component.rb +0 -1
- data/app/components/hakumi/segmented/component.rb +0 -4
- data/app/components/hakumi/slider/component.rb +2 -6
- data/app/components/hakumi/statistic/component.rb +0 -4
- data/app/components/hakumi/switch/component.html.erb +4 -0
- data/app/components/hakumi/switch/component.rb +1 -2
- data/app/components/hakumi/table/component.rb +3 -229
- data/app/components/hakumi/table/concerns/columns.rb +1 -1
- data/app/components/hakumi/table/concerns/editable.rb +121 -0
- data/app/components/hakumi/table/concerns/ellipsis.rb +63 -0
- data/app/components/hakumi/table/concerns/fixed_columns.rb +87 -0
- data/app/components/hakumi/transfer/component.rb +0 -4
- data/app/controllers/{hakumi_components → hakumi}/components_controller.rb +2 -2
- data/app/form_builders/hakumi/form_builder.rb +217 -175
- data/app/helpers/hakumi/form_helper.rb +39 -0
- data/app/javascript/hakumi_components/controllers/base/registry_controller.js +83 -3
- data/app/javascript/hakumi_components/controllers/hakumi/affix_controller.js +0 -23
- data/app/javascript/hakumi_components/controllers/hakumi/alert_controller.js +2 -1
- data/app/javascript/hakumi_components/controllers/hakumi/button_controller.js +0 -7
- data/app/javascript/hakumi_components/controllers/hakumi/calendar_controller.js +0 -2
- data/app/javascript/hakumi_components/controllers/hakumi/color_picker_controller.js +1 -6
- data/app/javascript/hakumi_components/controllers/hakumi/date_picker_controller.js +28 -34
- data/app/javascript/hakumi_components/controllers/hakumi/drawer_controller.js +2 -1
- data/app/javascript/hakumi_components/controllers/hakumi/form_item_controller.js +9 -63
- data/app/javascript/hakumi_components/controllers/hakumi/mentions_controller.js +4 -11
- data/app/javascript/hakumi_components/controllers/hakumi/message_controller.js +1 -1
- data/app/javascript/hakumi_components/controllers/hakumi/modal_controller.js +4 -20
- data/app/javascript/hakumi_components/controllers/hakumi/notification_controller.js +1 -1
- data/app/javascript/hakumi_components/controllers/hakumi/popconfirm_controller.js +33 -27
- data/app/javascript/hakumi_components/controllers/hakumi/popover_controller.js +2 -23
- data/app/javascript/hakumi_components/controllers/hakumi/qr_code_controller.js +0 -20
- data/app/javascript/hakumi_components/controllers/hakumi/segmented_controller.js +0 -2
- data/app/javascript/hakumi_components/controllers/hakumi/spin_controller.js +1 -19
- data/app/javascript/hakumi_components/controllers/hakumi/statistic_controller.js +0 -2
- data/app/javascript/hakumi_components/controllers/hakumi/table_controller.js +48 -74
- data/app/javascript/hakumi_components/controllers/hakumi/tag_controller.js +15 -14
- data/app/javascript/hakumi_components/controllers/hakumi/tag_group_controller.js +14 -13
- data/app/javascript/hakumi_components/controllers/hakumi/theme_controller.js +24 -1
- data/app/javascript/hakumi_components/controllers/hakumi/time_picker_controller.js +3 -7
- data/app/javascript/hakumi_components/controllers/hakumi/timeline_controller.js +0 -16
- data/app/javascript/hakumi_components/controllers/hakumi/transfer_controller.js +2 -2
- data/app/javascript/hakumi_components/controllers/hakumi/tree_controller.js +0 -2
- data/app/javascript/hakumi_components/controllers/hakumi/tree_select_controller.js +3 -3
- data/app/javascript/hakumi_components/controllers/hakumi/upload_controller.js +12 -26
- data/app/javascript/hakumi_components/core/persistence.js +3 -3
- data/app/javascript/hakumi_components/core/render_component.js +3 -1
- data/app/javascript/lib/validation_manager.js +101 -0
- data/app/javascript/stylesheets/_theme-tokens.scss +2 -1
- data/app/javascript/stylesheets/components/_modal.scss +13 -0
- data/app/services/{hakumi_components → hakumi}/component_handler.rb +1 -1
- data/app/services/hakumi/icon/loader.rb +2 -2
- data/app/services/hakumi/illustrations/loader.rb +3 -3
- data/app/views/hakumi/_drawer.html.erb +21 -0
- data/app/views/hakumi/_modal.html.erb +18 -0
- data/lib/hakumi_components/documentation.rb +127 -0
- data/lib/hakumi_components/engine.rb +13 -4
- data/lib/hakumi_components/rails/attribute_introspection.rb +1 -1
- data/lib/hakumi_components/rails/validation_introspection.rb +5 -5
- data/lib/hakumi_components/rails/validation_mapper.rb +484 -0
- data/lib/hakumi_components/rails.rb +2 -1
- data/lib/hakumi_components/version.rb +2 -2
- data/lib/hakumi_components.rb +3 -1
- data/lib/tasks/coverage.rake +37 -0
- data/sig/hakumi/base_component.rbs +5 -0
- data/sig/hakumi/checkbox/component.rbs +10 -0
- data/sig/hakumi/color_picker/component.rbs +0 -1
- data/sig/hakumi/form_builder.rbs +9 -1
- data/sig/{hakumi_components → hakumi}/rails/attribute_introspection.rbs +1 -1
- data/sig/{hakumi_components → hakumi}/rails/validation_introspection.rbs +1 -1
- data/sig/hakumi/rails/validation_mapper.rbs +53 -0
- data/sig/{hakumi_components → hakumi}/rails.rbs +1 -1
- data/sig/hakumi/segmented/component.rbs +0 -1
- data/sig/hakumi/slider/component.rbs +0 -1
- data/sig/hakumi/statistic/component.rbs +0 -2
- data/sig/hakumi/table/component.rbs +3 -4
- data/sig/hakumi/table/concerns/columns.rbs +2 -1
- data/sig/hakumi/table/concerns/editable.rbs +40 -0
- data/sig/hakumi/table/concerns/ellipsis.rbs +27 -0
- data/sig/hakumi/table/concerns/fixed_columns.rbs +33 -0
- data/sig/hakumi/transfer/component.rbs +0 -1
- data/sig/{hakumi_components.rbs → hakumi.rbs} +20 -3
- data/sig/rails/active_model/validations/comparison_validator.rbs +6 -0
- metadata +44 -29
- data/app/views/hakumi_components/_drawer.html.erb +0 -3
- data/app/views/hakumi_components/_modal.html.erb +0 -3
- /data/app/views/{hakumi_components → hakumi}/_admin_panel.html.erb +0 -0
- /data/app/views/{hakumi_components → hakumi}/_affix.html.erb +0 -0
- /data/app/views/{hakumi_components → hakumi}/_alert.html.erb +0 -0
- /data/app/views/{hakumi_components → hakumi}/_confirm.html.erb +0 -0
- /data/app/views/{hakumi_components → hakumi}/_message.html.erb +0 -0
- /data/app/views/{hakumi_components → hakumi}/_notification.html.erb +0 -0
- /data/app/views/{hakumi_components → hakumi}/_popconfirm.html.erb +0 -0
- /data/app/views/{hakumi_components → hakumi}/_popover.html.erb +0 -0
- /data/app/views/{hakumi_components → hakumi}/_qr_code.html.erb +0 -0
- /data/app/views/{hakumi_components → hakumi}/_result.html.erb +0 -0
- /data/app/views/{hakumi_components → hakumi}/_segmented.html.erb +0 -0
- /data/app/views/{hakumi_components → hakumi}/_skeleton.html.erb +0 -0
- /data/app/views/{hakumi_components → hakumi}/_spin.html.erb +0 -0
- /data/app/views/{hakumi_components → hakumi}/_statistic.html.erb +0 -0
- /data/app/views/{hakumi_components → hakumi}/_table.html.erb +0 -0
- /data/app/views/{hakumi_components → hakumi}/_tag.html.erb +0 -0
- /data/app/views/{hakumi_components → hakumi}/_timeline.html.erb +0 -0
- /data/app/views/{hakumi_components → hakumi}/_tree.html.erb +0 -0
|
@@ -41,13 +41,6 @@ export default class extends RegistryController {
|
|
|
41
41
|
setOffsetBottom: (value) => this.setOffsetBottom(value)
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
this.#exposeApi()
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
disconnect() {
|
|
49
|
-
delete this.element.hakumiAffix
|
|
50
|
-
super.disconnect()
|
|
51
44
|
}
|
|
52
45
|
|
|
53
46
|
updatePosition() {
|
|
@@ -187,20 +180,4 @@ export default class extends RegistryController {
|
|
|
187
180
|
if (this.hasOffsetBottomValue) return Number(this.offsetBottomValue)
|
|
188
181
|
return null
|
|
189
182
|
}
|
|
190
|
-
|
|
191
|
-
#exposeApi() {
|
|
192
|
-
this.element.hakumiAffix = {
|
|
193
|
-
check: () => this.updatePosition(),
|
|
194
|
-
update: () => this.updatePosition(),
|
|
195
|
-
isAffixed: () => this.affixed,
|
|
196
|
-
getState: () => ({
|
|
197
|
-
affixed: this.affixed,
|
|
198
|
-
offsetTop: this.offsetTop,
|
|
199
|
-
offsetBottom: this.offsetBottom,
|
|
200
|
-
targetSelector: this.targetSelectorValue || null
|
|
201
|
-
}),
|
|
202
|
-
setOffsetTop: (value) => this.setOffsetTop(value),
|
|
203
|
-
setOffsetBottom: (value) => this.setOffsetBottom(value)
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
183
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import RegistryController from "../base/registry_controller.js"
|
|
2
2
|
|
|
3
3
|
export default class extends RegistryController {
|
|
4
|
+
static declarativeActions = ["close"]
|
|
4
5
|
static targets = ["message", "description"]
|
|
5
6
|
|
|
6
7
|
registerApi() {
|
|
@@ -20,7 +21,7 @@ export default class extends RegistryController {
|
|
|
20
21
|
|
|
21
22
|
close(event) {
|
|
22
23
|
if (event) event.preventDefault()
|
|
23
|
-
this.
|
|
24
|
+
this.dispatch("hidden")
|
|
24
25
|
this.element.remove()
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -14,13 +14,6 @@ export default class extends RegistryController {
|
|
|
14
14
|
this.element.removeEventListener('click', this.boundRipple)
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
handleClick(event) {
|
|
18
|
-
if (this.loadingValue || this.element.disabled) {
|
|
19
|
-
event.preventDefault()
|
|
20
|
-
event.stopImmediatePropagation()
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
17
|
createRipple(event) {
|
|
25
18
|
const button = event.currentTarget
|
|
26
19
|
const circle = document.createElement("span")
|
|
@@ -41,7 +41,6 @@ export default class extends RegistryController {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
teardown() {
|
|
44
|
-
delete this.element.hakumiCalendar
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
#bindNestedEvents() {
|
|
@@ -1096,6 +1095,5 @@ export default class extends RegistryController {
|
|
|
1096
1095
|
}
|
|
1097
1096
|
|
|
1098
1097
|
|
|
1099
|
-
this.element.hakumiCalendar = api
|
|
1100
1098
|
}
|
|
1101
1099
|
}
|
|
@@ -725,11 +725,6 @@ export default class extends RegistryController {
|
|
|
725
725
|
}
|
|
726
726
|
|
|
727
727
|
dispatchEvent(name, detail) {
|
|
728
|
-
this.
|
|
729
|
-
new CustomEvent(`hakumi:color-picker:${name}`, {
|
|
730
|
-
detail,
|
|
731
|
-
bubbles: true
|
|
732
|
-
})
|
|
733
|
-
)
|
|
728
|
+
this.dispatch(name, { detail })
|
|
734
729
|
}
|
|
735
730
|
}
|
|
@@ -28,7 +28,7 @@ export default class extends RegistryController {
|
|
|
28
28
|
teardown() {
|
|
29
29
|
document.removeEventListener("click", this.boundClickOutside)
|
|
30
30
|
|
|
31
|
-
//
|
|
31
|
+
// Clean up calendar listeners if they exist
|
|
32
32
|
if (this.rangeValue && this.hasPanelTarget) {
|
|
33
33
|
const calendars = this.panelTarget.querySelectorAll(".hakumi-calendar")
|
|
34
34
|
const [startCalendar, endCalendar] = calendars
|
|
@@ -355,13 +355,13 @@ export default class extends RegistryController {
|
|
|
355
355
|
if (!api) return
|
|
356
356
|
|
|
357
357
|
if (this.tempRangeStart && this.tempRangeEnd) {
|
|
358
|
-
//
|
|
358
|
+
// Complete range
|
|
359
359
|
api.setRange(this.tempRangeStart, this.tempRangeEnd, null)
|
|
360
360
|
} else if (this.tempRangeStart) {
|
|
361
|
-
//
|
|
361
|
+
// Start only, no end (show only start)
|
|
362
362
|
api.setRange(this.tempRangeStart, null, null)
|
|
363
363
|
} else {
|
|
364
|
-
//
|
|
364
|
+
// No range
|
|
365
365
|
api.clearRange()
|
|
366
366
|
}
|
|
367
367
|
})
|
|
@@ -391,12 +391,12 @@ export default class extends RegistryController {
|
|
|
391
391
|
|
|
392
392
|
if (!startApi || !endApi) return
|
|
393
393
|
|
|
394
|
-
//
|
|
394
|
+
// Get current month/year from first calendar
|
|
395
395
|
const startDate = startApi.getValue()
|
|
396
396
|
const startMonth = startDate.getMonth()
|
|
397
397
|
const startYear = startDate.getFullYear()
|
|
398
398
|
|
|
399
|
-
//
|
|
399
|
+
// Calculate next month for second calendar
|
|
400
400
|
let endMonth = startMonth + 1
|
|
401
401
|
let endYear = startYear
|
|
402
402
|
|
|
@@ -405,7 +405,7 @@ export default class extends RegistryController {
|
|
|
405
405
|
endYear += 1
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
-
//
|
|
408
|
+
// Set second calendar to next month
|
|
409
409
|
const endDate = new Date(endYear, endMonth, 1)
|
|
410
410
|
endApi.setValue(endDate, { navigate: true })
|
|
411
411
|
}
|
|
@@ -419,7 +419,7 @@ export default class extends RegistryController {
|
|
|
419
419
|
const startCalendar = calendars[0]
|
|
420
420
|
const endCalendar = calendars[1]
|
|
421
421
|
|
|
422
|
-
//
|
|
422
|
+
// Remove previous listeners if they exist
|
|
423
423
|
if (this.boundSyncFromStart) {
|
|
424
424
|
startCalendar.removeEventListener("hakumi--calendar:change", this.boundSyncFromStart)
|
|
425
425
|
}
|
|
@@ -427,7 +427,7 @@ export default class extends RegistryController {
|
|
|
427
427
|
endCalendar.removeEventListener("hakumi--calendar:change", this.boundSyncFromEnd)
|
|
428
428
|
}
|
|
429
429
|
|
|
430
|
-
//
|
|
430
|
+
// Create listeners
|
|
431
431
|
this.boundSyncFromStart = (event) => {
|
|
432
432
|
const startApi = startCalendar.hakumiComponent?.api
|
|
433
433
|
const endApi = endCalendar.hakumiComponent?.api
|
|
@@ -437,7 +437,7 @@ export default class extends RegistryController {
|
|
|
437
437
|
const startMonth = startDate.getMonth()
|
|
438
438
|
const startYear = startDate.getFullYear()
|
|
439
439
|
|
|
440
|
-
//
|
|
440
|
+
// Calculate next month
|
|
441
441
|
let endMonth = startMonth + 1
|
|
442
442
|
let endYear = startYear
|
|
443
443
|
|
|
@@ -450,7 +450,7 @@ export default class extends RegistryController {
|
|
|
450
450
|
const currentEndMonth = endDate.getMonth()
|
|
451
451
|
const currentEndYear = endDate.getFullYear()
|
|
452
452
|
|
|
453
|
-
//
|
|
453
|
+
// Only sync if second calendar is not already on the correct month
|
|
454
454
|
if (currentEndMonth !== endMonth || currentEndYear !== endYear) {
|
|
455
455
|
endApi.setValue(new Date(endYear, endMonth, 1), { navigate: true })
|
|
456
456
|
}
|
|
@@ -465,7 +465,7 @@ export default class extends RegistryController {
|
|
|
465
465
|
const endMonth = endDate.getMonth()
|
|
466
466
|
const endYear = endDate.getFullYear()
|
|
467
467
|
|
|
468
|
-
//
|
|
468
|
+
// Calculate previous month
|
|
469
469
|
let startMonth = endMonth - 1
|
|
470
470
|
let startYear = endYear
|
|
471
471
|
|
|
@@ -478,13 +478,13 @@ export default class extends RegistryController {
|
|
|
478
478
|
const currentStartMonth = startDate.getMonth()
|
|
479
479
|
const currentStartYear = startDate.getFullYear()
|
|
480
480
|
|
|
481
|
-
//
|
|
481
|
+
// Only sync if first calendar is not already on the correct month
|
|
482
482
|
if (currentStartMonth !== startMonth || currentStartYear !== startYear) {
|
|
483
483
|
startApi.setValue(new Date(startYear, startMonth, 1), { navigate: true })
|
|
484
484
|
}
|
|
485
485
|
}
|
|
486
486
|
|
|
487
|
-
//
|
|
487
|
+
// Hover listener
|
|
488
488
|
this.boundHandleHover = (event) => {
|
|
489
489
|
const hoveredDate = event.detail?.date
|
|
490
490
|
if (!hoveredDate) return
|
|
@@ -602,23 +602,23 @@ export default class extends RegistryController {
|
|
|
602
602
|
const formatted = this.formatDate(date)
|
|
603
603
|
|
|
604
604
|
if (this.rangeValue) {
|
|
605
|
-
//
|
|
605
|
+
// New visual range selection logic
|
|
606
606
|
if (!this.tempRangeStart) {
|
|
607
|
-
//
|
|
607
|
+
// First selection: set range start
|
|
608
608
|
this.tempRangeStart = date
|
|
609
609
|
this.tempRangeEnd = null
|
|
610
610
|
|
|
611
|
-
//
|
|
611
|
+
// Update start input
|
|
612
612
|
if (this.hasStartInputTarget) {
|
|
613
613
|
this.startInputTarget.value = formatted
|
|
614
614
|
}
|
|
615
615
|
|
|
616
|
-
//
|
|
616
|
+
// Clear end input
|
|
617
617
|
if (this.hasEndInputTarget) {
|
|
618
618
|
this.endInputTarget.value = ""
|
|
619
619
|
}
|
|
620
620
|
|
|
621
|
-
//
|
|
621
|
+
// Update visual in both calendars
|
|
622
622
|
this.updateRangeVisuals()
|
|
623
623
|
|
|
624
624
|
this.dispatchChangeEvent({
|
|
@@ -626,10 +626,10 @@ export default class extends RegistryController {
|
|
|
626
626
|
endValue: ""
|
|
627
627
|
})
|
|
628
628
|
} else if (!this.tempRangeEnd) {
|
|
629
|
-
//
|
|
629
|
+
// Second selection: set range end
|
|
630
630
|
const clickedDate = date
|
|
631
631
|
|
|
632
|
-
//
|
|
632
|
+
// If clicked date is before start, restart selection
|
|
633
633
|
if (clickedDate < this.tempRangeStart) {
|
|
634
634
|
this.tempRangeStart = clickedDate
|
|
635
635
|
this.tempRangeEnd = null
|
|
@@ -648,7 +648,7 @@ export default class extends RegistryController {
|
|
|
648
648
|
endValue: ""
|
|
649
649
|
})
|
|
650
650
|
} else {
|
|
651
|
-
//
|
|
651
|
+
// Complete the range
|
|
652
652
|
this.tempRangeEnd = clickedDate
|
|
653
653
|
|
|
654
654
|
if (this.hasEndInputTarget) {
|
|
@@ -664,11 +664,11 @@ export default class extends RegistryController {
|
|
|
664
664
|
|
|
665
665
|
this.updateClearVisibility()
|
|
666
666
|
|
|
667
|
-
//
|
|
667
|
+
// Close picker after completing selection
|
|
668
668
|
this.close()
|
|
669
669
|
}
|
|
670
670
|
} else {
|
|
671
|
-
//
|
|
671
|
+
// Already have a complete range, restart with new selection
|
|
672
672
|
this.tempRangeStart = date
|
|
673
673
|
this.tempRangeEnd = null
|
|
674
674
|
|
|
@@ -688,10 +688,10 @@ export default class extends RegistryController {
|
|
|
688
688
|
}
|
|
689
689
|
|
|
690
690
|
this.updateClearVisibility()
|
|
691
|
-
return //
|
|
691
|
+
return // Early return for ranges
|
|
692
692
|
}
|
|
693
693
|
|
|
694
|
-
//
|
|
694
|
+
// Code for non-range pickers
|
|
695
695
|
// Use setValue to update both hidden and display inputs
|
|
696
696
|
this.setValue(formatted)
|
|
697
697
|
this.syncCalendarWithInput()
|
|
@@ -781,17 +781,11 @@ export default class extends RegistryController {
|
|
|
781
781
|
}
|
|
782
782
|
|
|
783
783
|
dispatchChangeEvent(detail) {
|
|
784
|
-
this.
|
|
785
|
-
bubbles: true,
|
|
786
|
-
detail
|
|
787
|
-
}))
|
|
784
|
+
this.dispatch("change", { detail })
|
|
788
785
|
}
|
|
789
786
|
|
|
790
787
|
dispatchOpenChange(open) {
|
|
791
|
-
this.
|
|
792
|
-
bubbles: true,
|
|
793
|
-
detail: { open }
|
|
794
|
-
}))
|
|
788
|
+
this.dispatch("openChange", { detail: { open } })
|
|
795
789
|
}
|
|
796
790
|
|
|
797
791
|
// DateTime methods (for show_time: true)
|
|
@@ -2,6 +2,7 @@ import RegistryController from "../base/registry_controller.js"
|
|
|
2
2
|
import { ensureOverlayContainer } from "../../core/overlay_container.js"
|
|
3
3
|
|
|
4
4
|
export default class extends RegistryController {
|
|
5
|
+
static declarativeActions = ["close"]
|
|
5
6
|
static targets = ["mask", "wrapper", "body"]
|
|
6
7
|
static values = {
|
|
7
8
|
open: Boolean,
|
|
@@ -103,7 +104,7 @@ export default class extends RegistryController {
|
|
|
103
104
|
document.body.style.overflow = ""
|
|
104
105
|
document.body.style.userSelect = ""
|
|
105
106
|
document.body.style.webkitUserSelect = ""
|
|
106
|
-
this.
|
|
107
|
+
this.dispatch("hidden")
|
|
107
108
|
}
|
|
108
109
|
this.closeTimeout = null
|
|
109
110
|
}, 300)
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import RegistryController from "../base/registry_controller.js"
|
|
2
2
|
import { validationManager } from "../../../lib/validation_manager.js"
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
4
|
export default class extends RegistryController {
|
|
7
5
|
static values = {
|
|
8
6
|
dependencies: Array,
|
|
@@ -11,14 +9,7 @@ export default class extends RegistryController {
|
|
|
11
9
|
}
|
|
12
10
|
|
|
13
11
|
setup() {
|
|
14
|
-
console.log('[FormItemController] Setup', {
|
|
15
|
-
fieldName: this.fieldNameValue,
|
|
16
|
-
dependencies: this.dependenciesValue,
|
|
17
|
-
rules: this.rulesValue
|
|
18
|
-
})
|
|
19
|
-
|
|
20
12
|
this.hasInteracted = false
|
|
21
|
-
|
|
22
13
|
this.setupListeners()
|
|
23
14
|
this.setupFormSubmitValidation()
|
|
24
15
|
}
|
|
@@ -61,10 +52,8 @@ export default class extends RegistryController {
|
|
|
61
52
|
}
|
|
62
53
|
|
|
63
54
|
handleFormSubmit(event) {
|
|
64
|
-
|
|
65
55
|
this.hasInteracted = true
|
|
66
56
|
|
|
67
|
-
|
|
68
57
|
const currentField = this.element.querySelector('input, textarea')
|
|
69
58
|
if (!currentField) return
|
|
70
59
|
|
|
@@ -77,7 +66,6 @@ export default class extends RegistryController {
|
|
|
77
66
|
if (!result.valid) {
|
|
78
67
|
event.preventDefault()
|
|
79
68
|
this.showError(result.errors[0])
|
|
80
|
-
console.log('[FormItemController] Form submission prevented due to validation error')
|
|
81
69
|
}
|
|
82
70
|
}
|
|
83
71
|
}
|
|
@@ -85,29 +73,32 @@ export default class extends RegistryController {
|
|
|
85
73
|
setupListeners() {
|
|
86
74
|
this.boundValidate = this.validate.bind(this)
|
|
87
75
|
|
|
88
|
-
|
|
89
76
|
if (this.hasRulesValue && this.rulesValue.length > 0) {
|
|
90
77
|
const currentField = this.element.querySelector('input, textarea')
|
|
91
78
|
if (currentField) {
|
|
92
79
|
currentField.addEventListener('input', this.boundValidate)
|
|
93
80
|
currentField.addEventListener('blur', this.boundValidate)
|
|
94
|
-
console.log('[FormItemController] Attached validation listeners to current field')
|
|
95
81
|
}
|
|
96
82
|
}
|
|
97
83
|
|
|
98
|
-
|
|
99
84
|
let dependencies = []
|
|
100
85
|
|
|
101
|
-
|
|
102
86
|
if (this.hasDependenciesValue) {
|
|
103
87
|
dependencies.push(...this.dependenciesValue)
|
|
104
88
|
}
|
|
105
89
|
|
|
106
|
-
|
|
107
90
|
if (this.hasRulesValue) {
|
|
108
91
|
this.rulesValue.forEach(rule => {
|
|
109
92
|
if (rule.match) {
|
|
110
|
-
|
|
93
|
+
if (rule.match.field) {
|
|
94
|
+
dependencies.push(rule.match.field)
|
|
95
|
+
} else if (typeof rule.match === 'string') {
|
|
96
|
+
dependencies.push(rule.match)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (rule.comparison && rule.comparison.field) {
|
|
101
|
+
dependencies.push(rule.comparison.field)
|
|
111
102
|
}
|
|
112
103
|
|
|
113
104
|
if (rule.dependent && rule.dependent.field) {
|
|
@@ -116,36 +107,26 @@ export default class extends RegistryController {
|
|
|
116
107
|
})
|
|
117
108
|
}
|
|
118
109
|
|
|
119
|
-
|
|
120
110
|
dependencies = [...new Set(dependencies)]
|
|
121
111
|
|
|
122
112
|
if (dependencies.length === 0) {
|
|
123
113
|
return
|
|
124
114
|
}
|
|
125
115
|
|
|
126
|
-
console.log('[FormItemController] Setting up dependency listeners for:', dependencies)
|
|
127
|
-
|
|
128
116
|
this.dependencyFields = []
|
|
129
117
|
|
|
130
|
-
|
|
131
118
|
const currentField = this.element.querySelector('input, textarea')
|
|
132
119
|
if (currentField) {
|
|
133
120
|
currentField.addEventListener('input', this.boundValidate)
|
|
134
121
|
currentField.addEventListener('change', this.boundValidate)
|
|
135
|
-
console.log('[FormItemController] Attached listeners to current field')
|
|
136
122
|
}
|
|
137
123
|
|
|
138
|
-
|
|
139
124
|
dependencies.forEach(depFieldName => {
|
|
140
125
|
const depField = this.findField(depFieldName)
|
|
141
|
-
console.log('[FormItemController] Looking for field:', depFieldName, 'Found:', depField)
|
|
142
126
|
if (depField) {
|
|
143
127
|
this.dependencyFields.push(depField)
|
|
144
128
|
depField.addEventListener('input', this.boundValidate)
|
|
145
129
|
depField.addEventListener('change', this.boundValidate)
|
|
146
|
-
console.log('[FormItemController] Attached listeners to:', depFieldName)
|
|
147
|
-
} else {
|
|
148
|
-
console.warn('[FormItemController] Could not find dependency field:', depFieldName)
|
|
149
130
|
}
|
|
150
131
|
})
|
|
151
132
|
}
|
|
@@ -153,7 +134,6 @@ export default class extends RegistryController {
|
|
|
153
134
|
removeListeners() {
|
|
154
135
|
if (!this.boundValidate) return
|
|
155
136
|
|
|
156
|
-
|
|
157
137
|
const currentField = this.element.querySelector('input, textarea')
|
|
158
138
|
if (currentField) {
|
|
159
139
|
currentField.removeEventListener('input', this.boundValidate)
|
|
@@ -161,7 +141,6 @@ export default class extends RegistryController {
|
|
|
161
141
|
currentField.removeEventListener('change', this.boundValidate)
|
|
162
142
|
}
|
|
163
143
|
|
|
164
|
-
|
|
165
144
|
if (!this.dependencyFields) return
|
|
166
145
|
|
|
167
146
|
this.dependencyFields.forEach(field => {
|
|
@@ -171,62 +150,44 @@ export default class extends RegistryController {
|
|
|
171
150
|
}
|
|
172
151
|
|
|
173
152
|
findField(fieldName) {
|
|
174
|
-
|
|
175
153
|
const form = this.element.closest('form')
|
|
176
154
|
if (!form) return null
|
|
177
155
|
|
|
178
|
-
|
|
179
156
|
let field = form.querySelector(`[name="${fieldName}"]`)
|
|
180
157
|
if (field) return field
|
|
181
158
|
|
|
182
|
-
|
|
183
159
|
field = form.querySelector(`#${fieldName}`)
|
|
184
160
|
if (field) return field
|
|
185
161
|
|
|
186
|
-
|
|
187
162
|
field = form.querySelector(`[name*="[${fieldName}]"]`)
|
|
188
163
|
if (field) return field
|
|
189
164
|
|
|
190
|
-
|
|
191
165
|
field = form.querySelector(`[name$="${fieldName}"]`)
|
|
192
166
|
return field
|
|
193
167
|
}
|
|
194
168
|
|
|
195
169
|
validate() {
|
|
196
|
-
console.log('[FormItemController] validate() called, hasInteracted:', this.hasInteracted)
|
|
197
|
-
|
|
198
|
-
|
|
199
170
|
if (!this.hasInteracted) {
|
|
200
171
|
this.hasInteracted = true
|
|
201
172
|
}
|
|
202
173
|
|
|
203
174
|
const currentField = this.element.querySelector('input, textarea')
|
|
204
175
|
if (!currentField) {
|
|
205
|
-
console.warn('[FormItemController] No input/textarea found in element')
|
|
206
176
|
return
|
|
207
177
|
}
|
|
208
|
-
console.log('[FormItemController] Current field:', currentField)
|
|
209
178
|
|
|
210
179
|
const value = currentField.value
|
|
211
180
|
|
|
212
|
-
|
|
213
181
|
if (this.hasRulesValue && this.rulesValue.length > 0) {
|
|
214
|
-
console.log('[FormItemController] Running rule-based validation')
|
|
215
182
|
this.validateWithRules(value)
|
|
216
183
|
}
|
|
217
184
|
}
|
|
218
185
|
|
|
219
186
|
validateWithRules(value) {
|
|
220
|
-
|
|
221
187
|
const formData = this.collectFormData()
|
|
222
|
-
|
|
223
|
-
|
|
224
188
|
const result = validationManager.validate(value, this.rulesValue, formData)
|
|
225
189
|
|
|
226
|
-
console.log('[FormItemController] Validation result:', result)
|
|
227
|
-
|
|
228
190
|
if (!result.valid) {
|
|
229
|
-
|
|
230
191
|
this.showError(result.errors[0])
|
|
231
192
|
} else {
|
|
232
193
|
this.clearError()
|
|
@@ -242,7 +203,6 @@ export default class extends RegistryController {
|
|
|
242
203
|
|
|
243
204
|
inputs.forEach(input => {
|
|
244
205
|
if (input.name) {
|
|
245
|
-
|
|
246
206
|
const match = input.name.match(/\[([^\]]+)\]$/)
|
|
247
207
|
const fieldName = match ? match[1] : input.name
|
|
248
208
|
formData[fieldName] = input.value
|
|
@@ -253,10 +213,8 @@ export default class extends RegistryController {
|
|
|
253
213
|
}
|
|
254
214
|
|
|
255
215
|
showError(message) {
|
|
256
|
-
|
|
257
216
|
this.element.classList.add('hakumi-form-item-has-error')
|
|
258
217
|
|
|
259
|
-
|
|
260
218
|
let errorContainer = this.element.querySelector('.hakumi-form-item-explain')
|
|
261
219
|
if (!errorContainer) {
|
|
262
220
|
const controlDiv = this.element.querySelector('.hakumi-form-item-control')
|
|
@@ -279,26 +237,21 @@ export default class extends RegistryController {
|
|
|
279
237
|
errorElement.textContent = message
|
|
280
238
|
}
|
|
281
239
|
|
|
282
|
-
|
|
283
240
|
const input = this.element.querySelector('input, textarea')
|
|
284
241
|
if (input) {
|
|
285
242
|
input.setAttribute('aria-invalid', 'true')
|
|
286
243
|
}
|
|
287
244
|
|
|
288
|
-
|
|
289
245
|
this.updateSubmitButton(true)
|
|
290
246
|
}
|
|
291
247
|
|
|
292
248
|
clearError() {
|
|
293
|
-
|
|
294
249
|
this.element.classList.remove('hakumi-form-item-has-error')
|
|
295
250
|
|
|
296
|
-
|
|
297
251
|
const errorContainer = this.element.querySelector('.hakumi-form-item-explain')
|
|
298
252
|
if (errorContainer) {
|
|
299
253
|
const errorElement = errorContainer.querySelector('.hakumi-form-item-explain-error')
|
|
300
254
|
if (errorElement) {
|
|
301
|
-
|
|
302
255
|
const caption = this.element.querySelector('.hakumi-form-item-extra')
|
|
303
256
|
if (caption) {
|
|
304
257
|
errorContainer.innerHTML = ''
|
|
@@ -309,13 +262,11 @@ export default class extends RegistryController {
|
|
|
309
262
|
}
|
|
310
263
|
}
|
|
311
264
|
|
|
312
|
-
|
|
313
265
|
const input = this.element.querySelector('input, textarea')
|
|
314
266
|
if (input) {
|
|
315
267
|
input.removeAttribute('aria-invalid')
|
|
316
268
|
}
|
|
317
269
|
|
|
318
|
-
|
|
319
270
|
this.updateSubmitButton(false)
|
|
320
271
|
}
|
|
321
272
|
|
|
@@ -328,15 +279,10 @@ export default class extends RegistryController {
|
|
|
328
279
|
|
|
329
280
|
if (hasError) {
|
|
330
281
|
submitButton.setAttribute('disabled', 'disabled')
|
|
331
|
-
console.log('[FormItemController] Submit button disabled')
|
|
332
282
|
} else {
|
|
333
|
-
|
|
334
283
|
const otherErrors = form.querySelectorAll('.hakumi-form-item-has-error')
|
|
335
284
|
if (otherErrors.length === 0) {
|
|
336
285
|
submitButton.removeAttribute('disabled')
|
|
337
|
-
console.log('[FormItemController] Submit button enabled')
|
|
338
|
-
} else {
|
|
339
|
-
console.log('[FormItemController] Submit button still disabled (other errors present)')
|
|
340
286
|
}
|
|
341
287
|
}
|
|
342
288
|
}
|
|
@@ -288,27 +288,20 @@ export default class extends RegistryController {
|
|
|
288
288
|
}
|
|
289
289
|
|
|
290
290
|
#dispatchChange() {
|
|
291
|
-
this.
|
|
292
|
-
bubbles: true,
|
|
291
|
+
this.dispatch("change", {
|
|
293
292
|
detail: {
|
|
294
293
|
value: this.getValue(),
|
|
295
294
|
mentions: this.getMentions()
|
|
296
295
|
}
|
|
297
|
-
})
|
|
296
|
+
})
|
|
298
297
|
}
|
|
299
298
|
|
|
300
299
|
#dispatchSelect(detail) {
|
|
301
|
-
this.
|
|
302
|
-
bubbles: true,
|
|
303
|
-
detail
|
|
304
|
-
}))
|
|
300
|
+
this.dispatch("select", { detail })
|
|
305
301
|
}
|
|
306
302
|
|
|
307
303
|
#dispatchSearch(detail) {
|
|
308
|
-
this.
|
|
309
|
-
bubbles: true,
|
|
310
|
-
detail
|
|
311
|
-
}))
|
|
304
|
+
this.dispatch("search", { detail })
|
|
312
305
|
}
|
|
313
306
|
|
|
314
307
|
#positionDropdown() {
|