@hakumi-dev/hakumi-components 0.1.16-pre → 0.1.18-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 +196 -28
- package/app/javascript/hakumi_components/controllers/base/registry_controller.js +83 -3
- package/app/javascript/hakumi_components/controllers/hakumi/affix_controller.js +0 -23
- package/app/javascript/hakumi_components/controllers/hakumi/alert_controller.js +2 -1
- package/app/javascript/hakumi_components/controllers/hakumi/button_controller.js +0 -7
- package/app/javascript/hakumi_components/controllers/hakumi/calendar_controller.js +0 -2
- package/app/javascript/hakumi_components/controllers/hakumi/color_picker_controller.js +1 -6
- package/app/javascript/hakumi_components/controllers/hakumi/date_picker_controller.js +28 -34
- package/app/javascript/hakumi_components/controllers/hakumi/drawer_controller.js +2 -1
- package/app/javascript/hakumi_components/controllers/hakumi/form_item_controller.js +9 -63
- package/app/javascript/hakumi_components/controllers/hakumi/mentions_controller.js +4 -11
- package/app/javascript/hakumi_components/controllers/hakumi/message_controller.js +1 -1
- package/app/javascript/hakumi_components/controllers/hakumi/modal_controller.js +4 -20
- package/app/javascript/hakumi_components/controllers/hakumi/notification_controller.js +1 -1
- package/app/javascript/hakumi_components/controllers/hakumi/popconfirm_controller.js +33 -27
- package/app/javascript/hakumi_components/controllers/hakumi/popover_controller.js +2 -23
- package/app/javascript/hakumi_components/controllers/hakumi/qr_code_controller.js +0 -20
- package/app/javascript/hakumi_components/controllers/hakumi/segmented_controller.js +0 -2
- package/app/javascript/hakumi_components/controllers/hakumi/spin_controller.js +1 -19
- package/app/javascript/hakumi_components/controllers/hakumi/statistic_controller.js +0 -2
- package/app/javascript/hakumi_components/controllers/hakumi/table_controller.js +48 -74
- package/app/javascript/hakumi_components/controllers/hakumi/tag_controller.js +15 -14
- package/app/javascript/hakumi_components/controllers/hakumi/tag_group_controller.js +14 -13
- package/app/javascript/hakumi_components/controllers/hakumi/theme_controller.js +24 -1
- package/app/javascript/hakumi_components/controllers/hakumi/time_picker_controller.js +3 -7
- package/app/javascript/hakumi_components/controllers/hakumi/timeline_controller.js +0 -16
- package/app/javascript/hakumi_components/controllers/hakumi/transfer_controller.js +2 -2
- package/app/javascript/hakumi_components/controllers/hakumi/tree_controller.js +0 -2
- package/app/javascript/hakumi_components/controllers/hakumi/tree_select_controller.js +3 -3
- package/app/javascript/hakumi_components/controllers/hakumi/upload_controller.js +12 -26
- package/app/javascript/hakumi_components/core/persistence.js +3 -3
- package/app/javascript/hakumi_components/core/render_component.js +3 -1
- package/app/javascript/lib/validation_manager.js +101 -0
- package/app/javascript/stylesheets/_theme-tokens.scss +2 -1
- package/app/javascript/stylesheets/components/_modal.scss +13 -0
- package/package.json +1 -1
|
@@ -301,7 +301,7 @@ export default class extends RegistryController {
|
|
|
301
301
|
direction
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
-
this.
|
|
304
|
+
this.dispatch("change", { detail })
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
#dispatchSelectChange() {
|
|
@@ -310,6 +310,6 @@ export default class extends RegistryController {
|
|
|
310
310
|
targetSelectedKeys: this.#selectedKeys("target")
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
this.
|
|
313
|
+
this.dispatch("selectChange", { detail })
|
|
314
314
|
}
|
|
315
315
|
}
|
|
@@ -51,7 +51,6 @@ export default class extends RegistryController {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
disconnect() {
|
|
54
|
-
delete this.element.hakumiTree
|
|
55
54
|
super.disconnect()
|
|
56
55
|
}
|
|
57
56
|
|
|
@@ -81,7 +80,6 @@ export default class extends RegistryController {
|
|
|
81
80
|
api
|
|
82
81
|
}
|
|
83
82
|
|
|
84
|
-
this.element.hakumiTree = api
|
|
85
83
|
}
|
|
86
84
|
|
|
87
85
|
buildNodeMap() {
|
|
@@ -268,14 +268,14 @@ export default class extends RegistryController {
|
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
treeApi() {
|
|
271
|
-
return this.treeTarget?.
|
|
271
|
+
return this.treeTarget?.hakumiComponent?.api
|
|
272
272
|
}
|
|
273
273
|
|
|
274
274
|
dispatchSelect(detail) {
|
|
275
|
-
this.
|
|
275
|
+
this.dispatch("select", { detail })
|
|
276
276
|
}
|
|
277
277
|
|
|
278
278
|
dispatchChange(detail) {
|
|
279
|
-
this.
|
|
279
|
+
this.dispatch("change", { detail })
|
|
280
280
|
}
|
|
281
281
|
}
|
|
@@ -742,31 +742,21 @@ export default class extends RegistryController {
|
|
|
742
742
|
}
|
|
743
743
|
|
|
744
744
|
dispatchChange(file) {
|
|
745
|
-
this.
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
detail: { file, fileList: this.getFileList() }
|
|
749
|
-
})
|
|
750
|
-
)
|
|
745
|
+
this.dispatch("change", {
|
|
746
|
+
detail: { file, fileList: this.getFileList() }
|
|
747
|
+
})
|
|
751
748
|
}
|
|
752
749
|
|
|
753
750
|
dispatchProgress(file, event) {
|
|
754
|
-
this.
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
detail: { file: file ? { ...file } : null, fileList: this.getFileList(), event }
|
|
758
|
-
})
|
|
759
|
-
)
|
|
751
|
+
this.dispatch("progress", {
|
|
752
|
+
detail: { file: file ? { ...file } : null, fileList: this.getFileList(), event }
|
|
753
|
+
})
|
|
760
754
|
}
|
|
761
755
|
|
|
762
756
|
dispatchSuccess(file) {
|
|
763
|
-
this.
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
detail: { file: file ? { ...file } : null, fileList: this.getFileList() }
|
|
767
|
-
})
|
|
768
|
-
)
|
|
769
|
-
|
|
757
|
+
this.dispatch("success", {
|
|
758
|
+
detail: { file: file ? { ...file } : null, fileList: this.getFileList() }
|
|
759
|
+
})
|
|
770
760
|
|
|
771
761
|
if (window.HakumiComponents?.renderComponent && file?.name) {
|
|
772
762
|
window.HakumiComponents.renderComponent("message", {
|
|
@@ -781,13 +771,9 @@ export default class extends RegistryController {
|
|
|
781
771
|
}
|
|
782
772
|
|
|
783
773
|
dispatchError(file, error) {
|
|
784
|
-
this.
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
detail: { file: file ? { ...file } : null, fileList: this.getFileList(), error }
|
|
788
|
-
})
|
|
789
|
-
)
|
|
790
|
-
|
|
774
|
+
this.dispatch("error", {
|
|
775
|
+
detail: { file: file ? { ...file } : null, fileList: this.getFileList(), error }
|
|
776
|
+
})
|
|
791
777
|
|
|
792
778
|
if (window.HakumiComponents?.renderComponent) {
|
|
793
779
|
const errorMsg = error || file?.error || "Upload failed"
|
|
@@ -21,7 +21,7 @@ const json = {
|
|
|
21
21
|
},
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
//
|
|
24
|
+
// Navigation events (Turbo + fallbacks), single global binding per module
|
|
25
25
|
let navBound = false
|
|
26
26
|
const navCallbacks = new Set()
|
|
27
27
|
|
|
@@ -51,7 +51,7 @@ const bindNavigationOnce = () => {
|
|
|
51
51
|
window.addEventListener("hashchange", scheduleNavCallbacks)
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
//
|
|
54
|
+
// Dedupe by key: prevents multiple active restores if mount() runs more than once
|
|
55
55
|
const bootstrapsByKey = new Map() // key -> unsubscribe
|
|
56
56
|
|
|
57
57
|
export const Persistence = {
|
|
@@ -80,7 +80,7 @@ export const Persistence = {
|
|
|
80
80
|
throw new Error("[HakumiComponents] Persistence.bootstrap(key, fn) expects a function")
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
//
|
|
83
|
+
// Dedupe: return existing unsubscribe if already bootstrapped
|
|
84
84
|
const existing = bootstrapsByKey.get(key)
|
|
85
85
|
if (existing) return existing
|
|
86
86
|
|
|
@@ -149,7 +149,9 @@ export const renderComponent = async (
|
|
|
149
149
|
element.remove()
|
|
150
150
|
unregister(id)
|
|
151
151
|
}
|
|
152
|
-
|
|
152
|
+
// Listen for component-specific hidden event (e.g., hakumi--modal:hidden)
|
|
153
|
+
const controllerName = name.replace(/_/g, "-")
|
|
154
|
+
element.addEventListener(`hakumi--${controllerName}:hidden`, handleHidden, { once: true })
|
|
153
155
|
}
|
|
154
156
|
|
|
155
157
|
await nextFrame()
|
|
@@ -106,6 +106,107 @@ export default class ValidationManager {
|
|
|
106
106
|
}
|
|
107
107
|
},
|
|
108
108
|
|
|
109
|
+
comparison: (value, rule, formData = {}) => {
|
|
110
|
+
if (value === null || value === undefined || value === '') return { valid: true }
|
|
111
|
+
|
|
112
|
+
const config = rule.value || rule.comparison || {}
|
|
113
|
+
const operator = config.operator
|
|
114
|
+
if (!operator) return { valid: true }
|
|
115
|
+
|
|
116
|
+
let targetValue
|
|
117
|
+
if (config.field) {
|
|
118
|
+
targetValue = formData[config.field]
|
|
119
|
+
} else if (config.value !== undefined) {
|
|
120
|
+
targetValue = config.value
|
|
121
|
+
} else {
|
|
122
|
+
return { valid: true }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (targetValue === null || targetValue === undefined || targetValue === '') {
|
|
126
|
+
return { valid: true }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const normalize = (raw) => {
|
|
130
|
+
if (raw instanceof Date) {
|
|
131
|
+
return { type: 'date', value: raw.getTime() }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (typeof raw === 'number') {
|
|
135
|
+
return { type: 'number', value: raw }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const text = String(raw).trim()
|
|
139
|
+
if (text === '') return { type: 'empty', value: text }
|
|
140
|
+
|
|
141
|
+
const numeric = Number(text)
|
|
142
|
+
if (!Number.isNaN(numeric) && /^-?\d+(\.\d+)?$/.test(text)) {
|
|
143
|
+
return { type: 'number', value: numeric }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const parsedDate = Date.parse(text)
|
|
147
|
+
if (!Number.isNaN(parsedDate) && /[-/T:]/.test(text)) {
|
|
148
|
+
return { type: 'date', value: parsedDate }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return { type: 'string', value: text }
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const left = normalize(value)
|
|
155
|
+
const right = normalize(targetValue)
|
|
156
|
+
|
|
157
|
+
if (left.type === 'empty' || right.type === 'empty') return { valid: true }
|
|
158
|
+
|
|
159
|
+
const compareValues = (a, b) => {
|
|
160
|
+
if (a.type === 'number' && b.type === 'number') {
|
|
161
|
+
return a.value === b.value ? 0 : (a.value > b.value ? 1 : -1)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (a.type === 'date' && b.type === 'date') {
|
|
165
|
+
return a.value === b.value ? 0 : (a.value > b.value ? 1 : -1)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (a.value === b.value) return 0
|
|
169
|
+
return a.value > b.value ? 1 : -1
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const result = compareValues(left, right)
|
|
173
|
+
let valid
|
|
174
|
+
|
|
175
|
+
switch (operator) {
|
|
176
|
+
case 'greater_than':
|
|
177
|
+
valid = result > 0
|
|
178
|
+
break
|
|
179
|
+
case 'greater_than_or_equal_to':
|
|
180
|
+
valid = result >= 0
|
|
181
|
+
break
|
|
182
|
+
case 'less_than':
|
|
183
|
+
valid = result < 0
|
|
184
|
+
break
|
|
185
|
+
case 'less_than_or_equal_to':
|
|
186
|
+
valid = result <= 0
|
|
187
|
+
break
|
|
188
|
+
case 'equal_to':
|
|
189
|
+
valid = result === 0
|
|
190
|
+
break
|
|
191
|
+
case 'other_than':
|
|
192
|
+
valid = result !== 0
|
|
193
|
+
break
|
|
194
|
+
default:
|
|
195
|
+
valid = true
|
|
196
|
+
break
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const label = config.label || config.field || config.value
|
|
200
|
+
const fallbackMessage = label
|
|
201
|
+
? `Must be ${operator.replace(/_/g, ' ')} ${label}`
|
|
202
|
+
: 'Comparison failed'
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
valid,
|
|
206
|
+
message: rule.message || fallbackMessage
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
|
|
109
210
|
custom: (value, rule) => {
|
|
110
211
|
if (typeof rule.validator !== 'function') {
|
|
111
212
|
console.error('Custom validator must be a function')
|
|
@@ -252,8 +252,9 @@
|
|
|
252
252
|
|
|
253
253
|
|
|
254
254
|
|
|
255
|
+
// Auto-detect theme from OS preference (opt-in with data-theme="auto")
|
|
255
256
|
@media (prefers-color-scheme: dark) {
|
|
256
|
-
:root
|
|
257
|
+
:root[data-theme="auto"] {
|
|
257
258
|
|
|
258
259
|
--color-primary: #6366f1;
|
|
259
260
|
--color-primary-hover: #818cf8;
|
|
@@ -207,4 +207,17 @@
|
|
|
207
207
|
transform: scale(0.2);
|
|
208
208
|
transition: opacity 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
|
|
209
209
|
transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Modal Confirm variant
|
|
213
|
+
.hakumi-modal-confirm-body-wrapper {
|
|
214
|
+
display: flex;
|
|
215
|
+
align-items: flex-start;
|
|
216
|
+
gap: 12px;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.hakumi-modal-confirm-content {
|
|
220
|
+
p {
|
|
221
|
+
margin: 0;
|
|
222
|
+
}
|
|
210
223
|
}
|