@hakumi-dev/hakumi-components 0.1.18-pre → 0.1.19-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.
- package/README.md +208 -381
- package/app/javascript/hakumi_components/controllers/hakumi/admin_panel_controller.js +5 -7
- package/app/javascript/hakumi_components/controllers/hakumi/back_top_controller.js +1 -1
- package/app/javascript/hakumi_components/controllers/hakumi/button_controller.js +108 -2
- package/app/javascript/hakumi_components/controllers/hakumi/calendar_controller.js +183 -95
- package/app/javascript/hakumi_components/controllers/hakumi/color_picker_controller.js +23 -285
- package/app/javascript/hakumi_components/controllers/hakumi/date_picker_controller.js +274 -262
- package/app/javascript/hakumi_components/controllers/hakumi/float_button_group_controller.js +2 -2
- package/app/javascript/hakumi_components/controllers/hakumi/message_controller.js +4 -2
- package/app/javascript/hakumi_components/controllers/hakumi/modal_controller.js +119 -125
- package/app/javascript/hakumi_components/controllers/hakumi/table/editable.js +291 -0
- package/app/javascript/hakumi_components/controllers/hakumi/table_controller.js +166 -366
- package/app/javascript/hakumi_components/controllers/hakumi/tabs_controller.js +8 -2
- package/app/javascript/hakumi_components/controllers/hakumi/tag_controller.js +27 -25
- package/app/javascript/hakumi_components/controllers/hakumi/tag_group_controller.js +19 -18
- package/app/javascript/hakumi_components/controllers/hakumi/theme_controller.js +116 -117
- package/app/javascript/hakumi_components/controllers/hakumi/transfer_controller.js +17 -1
- package/app/javascript/hakumi_components/controllers/hakumi/tree_controller.js +363 -78
- package/app/javascript/hakumi_components/controllers/hakumi/typography_controller.js +3 -3
- package/app/javascript/hakumi_components/controllers/hakumi/upload_controller.js +320 -204
- package/app/javascript/hakumi_components/core/render_component.js +37 -11
- package/app/javascript/hakumi_components/utils/color_helper.js +262 -0
- package/app/javascript/stylesheets/_base.scss +9 -0
- package/app/javascript/stylesheets/_hakumi_components.scss +1 -0
- package/app/javascript/stylesheets/components/_breadcrumb.scss +2 -2
- package/app/javascript/stylesheets/components/_calendar.scss +13 -13
- package/app/javascript/stylesheets/components/_cascader.scss +5 -5
- package/app/javascript/stylesheets/components/_checkbox.scss +9 -11
- package/app/javascript/stylesheets/components/_color_picker.scss +11 -11
- package/app/javascript/stylesheets/components/_date_picker.scss +4 -4
- package/app/javascript/stylesheets/components/_descriptions.scss +2 -2
- package/app/javascript/stylesheets/components/_drawer.scss +3 -3
- package/app/javascript/stylesheets/components/_dropdown.scss +2 -2
- package/app/javascript/stylesheets/components/_flex.scss +1 -1
- package/app/javascript/stylesheets/components/_float_button.scss +5 -5
- package/app/javascript/stylesheets/components/_form_item.scss +92 -0
- package/app/javascript/stylesheets/components/_image.scss +15 -15
- package/app/javascript/stylesheets/components/_input.scss +23 -113
- package/app/javascript/stylesheets/components/_layout.scss +27 -26
- package/app/javascript/stylesheets/components/_menu.scss +15 -15
- package/app/javascript/stylesheets/components/_modal.scss +13 -13
- package/app/javascript/stylesheets/components/_notification.scss +3 -3
- package/app/javascript/stylesheets/components/_popover.scss +1 -1
- package/app/javascript/stylesheets/components/_segmented.scss +3 -3
- package/app/javascript/stylesheets/components/_select.scss +6 -6
- package/app/javascript/stylesheets/components/_slider.scss +1 -1
- package/app/javascript/stylesheets/components/_spin.scss +2 -2
- package/app/javascript/stylesheets/components/_steps.scss +10 -10
- package/app/javascript/stylesheets/components/_switch.scss +11 -10
- package/app/javascript/stylesheets/components/_table.scss +6 -6
- package/app/javascript/stylesheets/components/_tag.scss +2 -2
- package/app/javascript/stylesheets/components/_tooltip.scss +4 -4
- package/app/javascript/stylesheets/components/_tree_select.scss +3 -3
- package/app/javascript/stylesheets/components/_typography.scss +3 -3
- package/app/javascript/stylesheets/components/_upload.scss +4 -4
- package/package.json +2 -2
|
@@ -13,13 +13,6 @@ const STORAGE_KEY = "hakumi:admin-panel"
|
|
|
13
13
|
export default class extends RegistryController {
|
|
14
14
|
static targets = ["body", "empty", "summary", "search", "typeFilter", "stateFilter"]
|
|
15
15
|
|
|
16
|
-
static mount() {
|
|
17
|
-
Persistence.bootstrap(STORAGE_KEY, ({state}) => {
|
|
18
|
-
if(!state.visible) return
|
|
19
|
-
window.HakumiComponents.renderComponent("admin_panel")
|
|
20
|
-
})
|
|
21
|
-
}
|
|
22
|
-
|
|
23
16
|
setup() {
|
|
24
17
|
Persistence.save(STORAGE_KEY, { version: 1, state: { visible: true } })
|
|
25
18
|
this.refresh()
|
|
@@ -66,6 +59,11 @@ export default class extends RegistryController {
|
|
|
66
59
|
this.applyFilters()
|
|
67
60
|
}
|
|
68
61
|
|
|
62
|
+
toggle(event) {
|
|
63
|
+
if (event) event.preventDefault()
|
|
64
|
+
this.element.classList.toggle("hakumi-admin-panel-collapsed")
|
|
65
|
+
}
|
|
66
|
+
|
|
69
67
|
close(event) {
|
|
70
68
|
if (event) event.preventDefault()
|
|
71
69
|
Persistence.save(STORAGE_KEY, { version: 1, state: { visible: false } })
|
|
@@ -51,7 +51,7 @@ export default class extends RegistryController {
|
|
|
51
51
|
const scrollTop = this.getScrollTop()
|
|
52
52
|
const isVisible = scrollTop >= this.visibilityHeightValue
|
|
53
53
|
this._isVisible = isVisible
|
|
54
|
-
this.element.classList.toggle("
|
|
54
|
+
this.element.classList.toggle("hakumi-float-button-back-top-visible", isVisible)
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
attachScrollListener() {
|
|
@@ -6,15 +6,21 @@ export default class extends RegistryController {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
setup() {
|
|
9
|
+
this.originalHref = this.element.getAttribute("href")
|
|
10
|
+
this.initiallyDisabled = this.element.hasAttribute("disabled")
|
|
11
|
+
this.initialAriaDisabled = this.element.getAttribute("aria-disabled")
|
|
9
12
|
this.boundRipple = this.createRipple.bind(this)
|
|
10
|
-
this.element.addEventListener(
|
|
13
|
+
this.element.addEventListener("click", this.boundRipple)
|
|
14
|
+
this.applyLoadingState()
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
teardown() {
|
|
14
|
-
this.element.removeEventListener(
|
|
18
|
+
this.element.removeEventListener("click", this.boundRipple)
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
createRipple(event) {
|
|
22
|
+
if (this.interactionDisabled()) return
|
|
23
|
+
|
|
18
24
|
const button = event.currentTarget
|
|
19
25
|
const circle = document.createElement("span")
|
|
20
26
|
const diameter = Math.max(button.clientWidth, button.clientHeight)
|
|
@@ -43,6 +49,7 @@ export default class extends RegistryController {
|
|
|
43
49
|
const api = {
|
|
44
50
|
setLoading: (loading) => {
|
|
45
51
|
this.loadingValue = Boolean(loading)
|
|
52
|
+
this.applyLoadingState()
|
|
46
53
|
},
|
|
47
54
|
isLoading: () => this.loadingValue,
|
|
48
55
|
ripple: (event) => this.createRipple(event)
|
|
@@ -55,4 +62,103 @@ export default class extends RegistryController {
|
|
|
55
62
|
api
|
|
56
63
|
}
|
|
57
64
|
}
|
|
65
|
+
|
|
66
|
+
loadingValueChanged() {
|
|
67
|
+
this.applyLoadingState()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
applyLoadingState() {
|
|
71
|
+
if (!this.element) return
|
|
72
|
+
|
|
73
|
+
this.originalHref ??= this.element.getAttribute("href")
|
|
74
|
+
this.initiallyDisabled ??= this.element.hasAttribute("disabled")
|
|
75
|
+
this.initialAriaDisabled ??= this.element.getAttribute("aria-disabled")
|
|
76
|
+
|
|
77
|
+
const loading = this.loadingValue
|
|
78
|
+
this.element.classList.toggle("hakumi-button-loading", loading)
|
|
79
|
+
this.element.setAttribute("aria-busy", String(loading))
|
|
80
|
+
|
|
81
|
+
if (this.element.tagName === "BUTTON") {
|
|
82
|
+
this.element.disabled = this.initiallyDisabled || loading
|
|
83
|
+
} else if (this.element.tagName === "A") {
|
|
84
|
+
this.applyAnchorLoadingState(loading)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.syncLoadingIcon(loading)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
applyAnchorLoadingState(loading) {
|
|
91
|
+
if (loading) {
|
|
92
|
+
if (this.element.hasAttribute("href")) {
|
|
93
|
+
this.originalHref = this.element.getAttribute("href")
|
|
94
|
+
}
|
|
95
|
+
this.element.removeAttribute("href")
|
|
96
|
+
this.element.setAttribute("role", "button")
|
|
97
|
+
this.element.setAttribute("aria-disabled", "true")
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (this.originalHref && !this.initialAriaDisabled) {
|
|
102
|
+
this.element.setAttribute("href", this.originalHref)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (this.initialAriaDisabled) {
|
|
106
|
+
this.element.setAttribute("aria-disabled", this.initialAriaDisabled)
|
|
107
|
+
this.element.setAttribute("role", "button")
|
|
108
|
+
} else {
|
|
109
|
+
this.element.removeAttribute("aria-disabled")
|
|
110
|
+
this.element.removeAttribute("role")
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
syncLoadingIcon(loading) {
|
|
115
|
+
if (loading) {
|
|
116
|
+
this.hideOriginalIcons()
|
|
117
|
+
if (!this.loadingIconElement()) {
|
|
118
|
+
this.element.prepend(this.buildLoadingIcon())
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
this.loadingIconElement()?.remove()
|
|
122
|
+
this.restoreOriginalIcons()
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
loadingIconElement() {
|
|
127
|
+
return this.element.querySelector(".hakumi-button-loading-icon")
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
buildLoadingIcon() {
|
|
131
|
+
const span = document.createElement("span")
|
|
132
|
+
span.className = "hakumi-icon hakumi-icon-spin hakumi-button-loading-icon"
|
|
133
|
+
span.setAttribute("aria-hidden", "true")
|
|
134
|
+
span.style.color = "currentColor"
|
|
135
|
+
span.innerHTML = `
|
|
136
|
+
<svg viewBox="0 0 1024 1024" focusable="false" width="1em" height="1em" fill="currentColor">
|
|
137
|
+
<path d="M512 64a448 448 0 1 0 448 448h-80a368 368 0 1 1-368-368V64z"></path>
|
|
138
|
+
</svg>
|
|
139
|
+
`
|
|
140
|
+
return span
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
hideOriginalIcons() {
|
|
144
|
+
Array.from(this.element.children).forEach((child) => {
|
|
145
|
+
if (!child.classList.contains("hakumi-icon") || child.classList.contains("hakumi-button-loading-icon")) return
|
|
146
|
+
|
|
147
|
+
child.dataset.hakumiButtonHiddenIcon = "true"
|
|
148
|
+
child.hidden = true
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
restoreOriginalIcons() {
|
|
153
|
+
Array.from(this.element.children).forEach((child) => {
|
|
154
|
+
if (child.dataset.hakumiButtonHiddenIcon !== "true") return
|
|
155
|
+
|
|
156
|
+
child.hidden = false
|
|
157
|
+
delete child.dataset.hakumiButtonHiddenIcon
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
interactionDisabled() {
|
|
162
|
+
return this.loadingValue || this.element.disabled || this.element.getAttribute("aria-disabled") === "true"
|
|
163
|
+
}
|
|
58
164
|
}
|
|
@@ -13,6 +13,7 @@ export default class extends RegistryController {
|
|
|
13
13
|
selectedDate: { type: String, default: "" },
|
|
14
14
|
static: { type: Boolean, default: false },
|
|
15
15
|
events: { type: Object, default: {} },
|
|
16
|
+
validRange: { type: Array, default: [] },
|
|
16
17
|
// In week mode, tracks the user's preferred display month
|
|
17
18
|
// This is set when user clicks a day, and preserved when reopening
|
|
18
19
|
userPreferredMonth: { type: Number, default: 0 },
|
|
@@ -30,36 +31,52 @@ export default class extends RegistryController {
|
|
|
30
31
|
monthsFull: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
|
|
31
32
|
},
|
|
32
33
|
es: {
|
|
33
|
-
daysShort: ["Lu", "Ma", "Mi", "Ju", "Vi", "Sá", "Do"], // ISO week:
|
|
34
|
+
daysShort: ["Lu", "Ma", "Mi", "Ju", "Vi", "Sá", "Do"], // ISO week: Monday first
|
|
34
35
|
monthsShort: ["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"],
|
|
35
36
|
monthsFull: ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"]
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
setup() {
|
|
40
|
-
this
|
|
41
|
+
this.bindNestedEvents()
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
teardown() {
|
|
45
|
+
this.unbindNestedEvents()
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
|
|
48
|
+
bindNestedEvents() {
|
|
47
49
|
this.boundChangeYear = this.changeYear.bind(this)
|
|
48
50
|
this.boundChangeMonth = this.changeMonth.bind(this)
|
|
49
51
|
this.boundChangeMode = this.changeMode.bind(this)
|
|
52
|
+
this.boundHandleSelectChange = this.handleSelectChange.bind(this)
|
|
50
53
|
|
|
51
|
-
this.element.addEventListener("hakumi--select:change",
|
|
52
|
-
const target = event.target
|
|
53
|
-
if (target.matches("[data-hakumi--calendar-target='yearSelect']")) {
|
|
54
|
-
this.boundChangeYear(event)
|
|
55
|
-
} else if (target.matches("[data-hakumi--calendar-target='monthSelect']")) {
|
|
56
|
-
this.boundChangeMonth(event)
|
|
57
|
-
}
|
|
58
|
-
})
|
|
59
|
-
|
|
54
|
+
this.element.addEventListener("hakumi--select:change", this.boundHandleSelectChange)
|
|
60
55
|
this.element.addEventListener("hakumi--segmented:change", this.boundChangeMode)
|
|
61
56
|
}
|
|
62
57
|
|
|
58
|
+
unbindNestedEvents() {
|
|
59
|
+
if (this.boundHandleSelectChange) {
|
|
60
|
+
this.element.removeEventListener("hakumi--select:change", this.boundHandleSelectChange)
|
|
61
|
+
this.boundHandleSelectChange = null
|
|
62
|
+
}
|
|
63
|
+
if (this.boundChangeMode) {
|
|
64
|
+
this.element.removeEventListener("hakumi--segmented:change", this.boundChangeMode)
|
|
65
|
+
this.boundChangeMode = null
|
|
66
|
+
}
|
|
67
|
+
this.boundChangeYear = null
|
|
68
|
+
this.boundChangeMonth = null
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
handleSelectChange(event) {
|
|
72
|
+
const target = event.target
|
|
73
|
+
if (target.matches("[data-hakumi--calendar-target='yearSelect']")) {
|
|
74
|
+
this.boundChangeYear(event)
|
|
75
|
+
} else if (target.matches("[data-hakumi--calendar-target='monthSelect']")) {
|
|
76
|
+
this.boundChangeMonth(event)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
63
80
|
yearValueChanged() {
|
|
64
81
|
this.#renderCalendar()
|
|
65
82
|
this.#updateYearSelect()
|
|
@@ -447,12 +464,7 @@ export default class extends RegistryController {
|
|
|
447
464
|
if (!events[dateStr]) {
|
|
448
465
|
events[dateStr] = []
|
|
449
466
|
}
|
|
450
|
-
events[dateStr].push(
|
|
451
|
-
id: event.id || null,
|
|
452
|
-
type: event.type || 'default',
|
|
453
|
-
content: event.content || '',
|
|
454
|
-
html: event.html || null
|
|
455
|
-
})
|
|
467
|
+
events[dateStr].push(this.#normalizeEvent(event))
|
|
456
468
|
|
|
457
469
|
this.eventsValue = events
|
|
458
470
|
if (!this.staticValue) {
|
|
@@ -559,6 +571,10 @@ export default class extends RegistryController {
|
|
|
559
571
|
}
|
|
560
572
|
|
|
561
573
|
#formatDateKey(date) {
|
|
574
|
+
if (date === null || date === undefined || date === "") {
|
|
575
|
+
return ""
|
|
576
|
+
}
|
|
577
|
+
|
|
562
578
|
if (date instanceof Date) {
|
|
563
579
|
return DateHelper.formatDataDate(date)
|
|
564
580
|
}
|
|
@@ -571,16 +587,20 @@ export default class extends RegistryController {
|
|
|
571
587
|
const normalized = {}
|
|
572
588
|
Object.keys(events).forEach(key => {
|
|
573
589
|
const dateStr = this.#formatDateKey(key)
|
|
574
|
-
normalized[dateStr] = events[key].map(e => (
|
|
575
|
-
id: e.id || null,
|
|
576
|
-
type: e.type || 'default',
|
|
577
|
-
content: e.content || '',
|
|
578
|
-
html: e.html || null
|
|
579
|
-
}))
|
|
590
|
+
normalized[dateStr] = events[key].map(e => this.#normalizeEvent(e))
|
|
580
591
|
})
|
|
581
592
|
return normalized
|
|
582
593
|
}
|
|
583
594
|
|
|
595
|
+
#normalizeEvent(event) {
|
|
596
|
+
return {
|
|
597
|
+
id: event.id || null,
|
|
598
|
+
type: event.type || 'default',
|
|
599
|
+
content: event.content || '',
|
|
600
|
+
html: event.html || null
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
584
604
|
#dispatchEventsChange() {
|
|
585
605
|
this.dispatch("eventsChange", {
|
|
586
606
|
detail: { events: this.getEvents() }
|
|
@@ -623,7 +643,14 @@ export default class extends RegistryController {
|
|
|
623
643
|
const today = DateHelper.formatDataDate(new Date())
|
|
624
644
|
|
|
625
645
|
let html = `<table class="hakumi-calendar-table">
|
|
626
|
-
|
|
646
|
+
${this.#renderMonthTableHead(locale, showWeek)}
|
|
647
|
+
${this.#renderMonthTableBody(weeks, showWeek, today)}
|
|
648
|
+
</table>`
|
|
649
|
+
return html
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
#renderMonthTableHead(locale, showWeek) {
|
|
653
|
+
let html = `<thead class="hakumi-calendar-thead">
|
|
627
654
|
<tr>`
|
|
628
655
|
|
|
629
656
|
if (showWeek) {
|
|
@@ -636,37 +663,62 @@ export default class extends RegistryController {
|
|
|
636
663
|
</th>`
|
|
637
664
|
})
|
|
638
665
|
|
|
639
|
-
html += `</tr></thead
|
|
666
|
+
html += `</tr></thead>`
|
|
667
|
+
return html
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
#renderMonthTableBody(weeks, showWeek, today) {
|
|
671
|
+
let html = `<tbody class="hakumi-calendar-tbody">`
|
|
640
672
|
|
|
641
673
|
weeks.forEach(week => {
|
|
642
|
-
|
|
643
|
-
|
|
674
|
+
html += this.#renderMonthWeekRow(week, showWeek, today)
|
|
675
|
+
})
|
|
644
676
|
|
|
645
|
-
|
|
646
|
-
|
|
677
|
+
html += `</tbody>`
|
|
678
|
+
return html
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
#renderMonthWeekRow(week, showWeek, today) {
|
|
682
|
+
const rowClass = week.weekSelected ? "hakumi-calendar-row hakumi-calendar-row-selected" : "hakumi-calendar-row"
|
|
683
|
+
let html = `<tr class="${rowClass}">`
|
|
684
|
+
|
|
685
|
+
if (showWeek) {
|
|
686
|
+
html += this.#renderWeekNumberCell(week)
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
week.days.forEach(day => {
|
|
690
|
+
html += this.#renderDateCell(day, today)
|
|
691
|
+
})
|
|
692
|
+
|
|
693
|
+
html += `</tr>`
|
|
694
|
+
return html
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
#renderWeekNumberCell(week) {
|
|
698
|
+
return `<td class="hakumi-calendar-cell hakumi-calendar-cell-week" data-action="click->hakumi--calendar#selectWeek">
|
|
647
699
|
<div class="hakumi-calendar-week-number">${week.weekNumber}</div>
|
|
648
700
|
</td>`
|
|
649
|
-
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
#renderDateCell(day, today) {
|
|
704
|
+
const classes = this.#getCellClasses(day, today)
|
|
705
|
+
const eventsHtml = this.#renderEventsForDate(day.dateStr)
|
|
706
|
+
const actions = this.#dateCellActions()
|
|
650
707
|
|
|
651
|
-
|
|
652
|
-
const classes = this.#getCellClasses(day, today)
|
|
653
|
-
const eventsHtml = this.#renderEventsForDate(day.dateStr)
|
|
654
|
-
const actions = this.rangeStartDateValue && !this.rangeEndDateValue
|
|
655
|
-
? "click->hakumi--calendar#selectDate mouseenter->hakumi--calendar#handleCellHover"
|
|
656
|
-
: "click->hakumi--calendar#selectDate"
|
|
657
|
-
html += `<td class="${classes}" data-date="${day.dateStr}" data-action="${actions}">
|
|
708
|
+
return `<td class="${classes}" data-date="${day.dateStr}" data-action="${actions}">
|
|
658
709
|
<div class="hakumi-calendar-date">
|
|
659
710
|
<div class="hakumi-calendar-date-value">${day.day}</div>
|
|
660
711
|
${eventsHtml ? `<div class="hakumi-calendar-date-content">${eventsHtml}</div>` : ''}
|
|
661
712
|
</div>
|
|
662
713
|
</td>`
|
|
663
|
-
|
|
714
|
+
}
|
|
664
715
|
|
|
665
|
-
|
|
666
|
-
|
|
716
|
+
#dateCellActions() {
|
|
717
|
+
if (this.rangeStartDateValue && !this.rangeEndDateValue) {
|
|
718
|
+
return "click->hakumi--calendar#selectDate mouseenter->hakumi--calendar#handleCellHover"
|
|
719
|
+
}
|
|
667
720
|
|
|
668
|
-
|
|
669
|
-
return html
|
|
721
|
+
return "click->hakumi--calendar#selectDate"
|
|
670
722
|
}
|
|
671
723
|
|
|
672
724
|
#renderYearView() {
|
|
@@ -710,30 +762,34 @@ export default class extends RegistryController {
|
|
|
710
762
|
}
|
|
711
763
|
|
|
712
764
|
#getMonthEventIndicators(year, month) {
|
|
713
|
-
const
|
|
714
|
-
|
|
765
|
+
const eventTypes = this.#getMonthEventTypes(year, month)
|
|
766
|
+
|
|
767
|
+
if (eventTypes.size === 0) return ''
|
|
768
|
+
|
|
769
|
+
let html = '<div class="hakumi-calendar-month-events">'
|
|
770
|
+
eventTypes.forEach(type => {
|
|
771
|
+
html += `<span class="hakumi-calendar-month-event-dot hakumi-calendar-month-event-${type}"></span>`
|
|
772
|
+
})
|
|
773
|
+
html += '</div>'
|
|
715
774
|
|
|
775
|
+
return html
|
|
776
|
+
}
|
|
716
777
|
|
|
778
|
+
#getMonthEventTypes(year, month) {
|
|
779
|
+
const events = this.eventsValue
|
|
717
780
|
const eventTypes = new Set()
|
|
718
|
-
|
|
781
|
+
if (!events || Object.keys(events).length === 0) return eventTypes
|
|
782
|
+
|
|
719
783
|
Object.keys(events).forEach(dateStr => {
|
|
720
784
|
const date = DateHelper.parseDataDate(dateStr)
|
|
721
785
|
if (date.year() === year && date.month() + 1 === month) {
|
|
722
786
|
events[dateStr].forEach(event => {
|
|
723
|
-
eventTypes.add(event
|
|
787
|
+
eventTypes.add(this.#eventType(event))
|
|
724
788
|
})
|
|
725
789
|
}
|
|
726
790
|
})
|
|
727
791
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
let html = '<div class="hakumi-calendar-month-events">'
|
|
731
|
-
eventTypes.forEach(type => {
|
|
732
|
-
html += `<span class="hakumi-calendar-month-event-dot hakumi-calendar-month-event-${type}"></span>`
|
|
733
|
-
})
|
|
734
|
-
html += '</div>'
|
|
735
|
-
|
|
736
|
-
return html
|
|
792
|
+
return eventTypes
|
|
737
793
|
}
|
|
738
794
|
|
|
739
795
|
#renderQuarterView() {
|
|
@@ -848,7 +904,8 @@ export default class extends RegistryController {
|
|
|
848
904
|
dateStr: DateHelper.formatDataDate(current),
|
|
849
905
|
day: current.date(),
|
|
850
906
|
inMonth: current.month() + 1 === month,
|
|
851
|
-
weekend: current.day() === 0 || current.day() === 6
|
|
907
|
+
weekend: current.day() === 0 || current.day() === 6,
|
|
908
|
+
disabled: !this.#isDateInValidRange(current)
|
|
852
909
|
})
|
|
853
910
|
current = current.add(1, "day")
|
|
854
911
|
}
|
|
@@ -893,59 +950,90 @@ export default class extends RegistryController {
|
|
|
893
950
|
classes.push("hakumi-calendar-cell-selected")
|
|
894
951
|
}
|
|
895
952
|
|
|
953
|
+
if (day.disabled) {
|
|
954
|
+
classes.push("hakumi-calendar-cell-disabled")
|
|
955
|
+
}
|
|
956
|
+
|
|
896
957
|
if (day.weekend && day.dateStr !== this.selectedDateValue) {
|
|
897
958
|
classes.push("hakumi-calendar-cell-weekend")
|
|
898
959
|
}
|
|
899
960
|
|
|
900
|
-
|
|
901
|
-
if (this.rangeStartDateValue || this.rangeEndDateValue) {
|
|
902
|
-
const rangeStart = this.rangeStartDateValue ? new Date(this.rangeStartDateValue) : null
|
|
903
|
-
const rangeEnd = this.rangeEndDateValue ? new Date(this.rangeEndDateValue) : null
|
|
904
|
-
const hovered = this.hoveredDateValue ? new Date(this.hoveredDateValue) : null
|
|
905
|
-
const currentDate = new Date(day.dateStr)
|
|
906
|
-
|
|
907
|
-
// Mark range start
|
|
908
|
-
if (rangeStart && day.dateStr === this.rangeStartDateValue) {
|
|
909
|
-
classes.push("hakumi-calendar-cell-range-start")
|
|
910
|
-
}
|
|
961
|
+
this.#appendRangeClasses(classes, day.dateStr)
|
|
911
962
|
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
classes.push("hakumi-calendar-cell-range-end")
|
|
915
|
-
}
|
|
963
|
+
return classes.join(" ")
|
|
964
|
+
}
|
|
916
965
|
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
if ((currentDate > rangeStart && currentDate <= hovered) ||
|
|
926
|
-
(currentDate < rangeStart && currentDate >= hovered)) {
|
|
927
|
-
classes.push("hakumi-calendar-cell-in-range")
|
|
928
|
-
classes.push("hakumi-calendar-cell-range-hover")
|
|
929
|
-
}
|
|
930
|
-
}
|
|
966
|
+
#appendRangeClasses(classes, dateStr) {
|
|
967
|
+
if (!this.rangeStartDateValue && !this.rangeEndDateValue) return
|
|
968
|
+
|
|
969
|
+
const range = this.#rangeDates()
|
|
970
|
+
const currentDate = new Date(dateStr)
|
|
971
|
+
|
|
972
|
+
if (range.start && dateStr === this.rangeStartDateValue) {
|
|
973
|
+
classes.push("hakumi-calendar-cell-range-start")
|
|
931
974
|
}
|
|
932
975
|
|
|
933
|
-
|
|
976
|
+
if (range.end && dateStr === this.rangeEndDateValue) {
|
|
977
|
+
classes.push("hakumi-calendar-cell-range-end")
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
if (this.#isDateInSelectedRange(currentDate, range)) {
|
|
981
|
+
classes.push("hakumi-calendar-cell-in-range")
|
|
982
|
+
} else if (this.#isDateInHoverRange(currentDate, range)) {
|
|
983
|
+
classes.push("hakumi-calendar-cell-in-range")
|
|
984
|
+
classes.push("hakumi-calendar-cell-range-hover")
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
#rangeDates() {
|
|
989
|
+
return {
|
|
990
|
+
start: this.rangeStartDateValue ? new Date(this.rangeStartDateValue) : null,
|
|
991
|
+
end: this.rangeEndDateValue ? new Date(this.rangeEndDateValue) : null,
|
|
992
|
+
hovered: this.hoveredDateValue ? new Date(this.hoveredDateValue) : null
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
#isDateInSelectedRange(currentDate, range) {
|
|
997
|
+
return range.start && range.end && currentDate > range.start && currentDate < range.end
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
#isDateInHoverRange(currentDate, range) {
|
|
1001
|
+
if (!range.start || !range.hovered || range.end) return false
|
|
1002
|
+
|
|
1003
|
+
return (currentDate > range.start && currentDate <= range.hovered) ||
|
|
1004
|
+
(currentDate < range.start && currentDate >= range.hovered)
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
#isDateInValidRange(date) {
|
|
1008
|
+
if (!Array.isArray(this.validRangeValue) || this.validRangeValue.length !== 2) {
|
|
1009
|
+
return true
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
const [startDate, endDate] = this.validRangeValue
|
|
1013
|
+
const current = DateHelper.parseDataDate(DateHelper.formatDataDate(date))
|
|
1014
|
+
const start = startDate ? DateHelper.parseDataDate(startDate) : null
|
|
1015
|
+
const end = endDate ? DateHelper.parseDataDate(endDate) : null
|
|
1016
|
+
|
|
1017
|
+
return (!start || !current.isBefore(start, "day")) && (!end || !current.isAfter(end, "day"))
|
|
934
1018
|
}
|
|
935
1019
|
|
|
936
1020
|
#renderEventsForDate(dateStr) {
|
|
937
1021
|
const events = this.eventsValue[dateStr]
|
|
938
1022
|
if (!events || events.length === 0) return ''
|
|
939
1023
|
|
|
940
|
-
return events.map(event =>
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1024
|
+
return events.map(event => this.#renderEvent(event)).join('')
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
#renderEvent(event) {
|
|
1028
|
+
const type = this.#eventType(event)
|
|
1029
|
+
const content = this.#escapeHtml(event.content)
|
|
1030
|
+
const badgeHtml = event.html || this.#renderBadgeHtml(type, content)
|
|
1031
|
+
|
|
1032
|
+
return `<div class="hakumi-calendar-event hakumi-calendar-event-${type}" title="${content}">${badgeHtml}</div>`
|
|
1033
|
+
}
|
|
944
1034
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
return `<div class="hakumi-calendar-event hakumi-calendar-event-${type}" title="${content}">${badgeHtml}</div>`
|
|
948
|
-
}).join('')
|
|
1035
|
+
#eventType(event) {
|
|
1036
|
+
return event.type || 'default'
|
|
949
1037
|
}
|
|
950
1038
|
|
|
951
1039
|
#renderBadgeHtml(type, content) {
|