webcake-ui-kit 1.1.29 → 1.1.31

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 (29) hide show
  1. package/package.json +7 -2
  2. package/src/components/date-picker/CalendarPanel.vue +154 -0
  3. package/src/components/date-picker/CalendarView.vue +104 -0
  4. package/src/components/date-picker/DatePicker.vue +587 -0
  5. package/src/components/date-picker/PanelFooter.vue +45 -0
  6. package/src/components/date-picker/PanelHeader.vue +102 -0
  7. package/src/components/date-picker/TimePanel.vue +98 -0
  8. package/src/components/date-picker/calendar-panel.css +36 -0
  9. package/src/components/date-picker/calendar-view.css +134 -0
  10. package/src/components/date-picker/calendar.js +116 -0
  11. package/src/components/date-picker/date-picker.css +95 -0
  12. package/src/components/date-picker/dayjs.js +34 -0
  13. package/src/components/date-picker/panel-footer.css +42 -0
  14. package/src/components/date-picker/panel-header.css +71 -0
  15. package/src/components/date-picker/time-panel.css +63 -0
  16. package/src/components/input/input.css +5 -0
  17. package/src/components/menu/menu.css +0 -1
  18. package/src/components/message/Message.vue +72 -0
  19. package/src/components/message/MessageContainer.vue +200 -0
  20. package/src/components/message/message-container.css +108 -0
  21. package/src/components/message/message.css +69 -0
  22. package/src/components/message/message.js +133 -0
  23. package/src/components/progress/Progress.vue +42 -0
  24. package/src/components/progress/progress.css +15 -0
  25. package/src/components/radio/Radio.vue +1 -1
  26. package/src/components/radio-group/RadioGroup.vue +2 -2
  27. package/src/components/table/Table.vue +30 -21
  28. package/src/components/table/table.css +18 -2
  29. package/src/index.js +5 -0
@@ -0,0 +1,133 @@
1
+ // Global message service — an Ant-Design-style singleton.
2
+ //
3
+ // Usage:
4
+ // import { wkMessage } from 'webcake-ui-kit'
5
+ // wkMessage.success('Saved')
6
+ // const hide = wkMessage.loading('Uploading...') // sticky; call hide() to dismiss
7
+ // wkMessage.config({ placement: 'bottom', maxCount: 3, duration: 5000 })
8
+ //
9
+ // A single MessageContainer is lazily mounted to document.body the first time a
10
+ // message is shown; consumers never render it themselves.
11
+ //
12
+ // Dual-compat note: the library ships raw SFCs consumed by both Vue 2.7 and
13
+ // Vue 3.4. We namespace-import `vue` (which never errors on a missing member)
14
+ // and branch at runtime to avoid static import errors in Vue 2.
15
+ // eslint-disable-next-line no-restricted-imports
16
+ import * as VueRuntime from 'vue'
17
+ import MessageContainer from './MessageContainer.vue'
18
+
19
+ const DEFAULTS = { duration: 3000, placement: 'top', offset: 24, maxCount: 0 }
20
+
21
+ let globalConfig = { ...DEFAULTS }
22
+ let app = null // Vue 3 application instance (needed for unmount)
23
+ let containerVm = null // MessageContainer instance we drive imperatively
24
+ let hostEl = null
25
+
26
+ function ensureContainer() {
27
+ if (containerVm) return containerVm
28
+ if (typeof document === 'undefined') return null
29
+
30
+ hostEl = document.createElement('div')
31
+ hostEl.setAttribute('data-wk-message-host', '')
32
+ document.body.appendChild(hostEl)
33
+
34
+ // Bypass strict static analysis in bundlers (e.g. Webpack 5)
35
+ const createAppKey = 'createApp'
36
+ const defaultKey = 'default'
37
+
38
+ if (typeof VueRuntime[createAppKey] === 'function') {
39
+ app = VueRuntime[createAppKey](MessageContainer)
40
+ containerVm = app.mount(hostEl)
41
+ } else {
42
+ const Vue = VueRuntime[defaultKey] || VueRuntime
43
+ const vm = new Vue({
44
+ render: h => h(MessageContainer)
45
+ }).$mount(hostEl)
46
+ containerVm = vm.$children[0]
47
+ }
48
+
49
+ containerVm.configure(globalConfig)
50
+ return containerVm
51
+ }
52
+
53
+ // Accept either a config object, (content, optsObj), or (content, duration?, onClose?).
54
+ function toConfig(content, durationOrOpts, onClose) {
55
+ const cfg = content && typeof content === 'object' ? { ...content } : { content }
56
+ if (durationOrOpts && typeof durationOrOpts === 'object') {
57
+ Object.assign(cfg, durationOrOpts)
58
+ } else if (typeof durationOrOpts === 'function') {
59
+ cfg.onClose = durationOrOpts
60
+ } else {
61
+ if (durationOrOpts !== undefined) cfg.duration = durationOrOpts
62
+ if (onClose !== undefined) cfg.onClose = onClose
63
+ }
64
+ return cfg
65
+ }
66
+
67
+ // Show a message. Returns a function that dismisses it early.
68
+ function open(config) {
69
+ const vm = ensureContainer()
70
+ if (!vm) return function () {}
71
+
72
+ // Per-call placement/offset override (applied before add so the container
73
+ // is positioned correctly when the message enters).
74
+ if (config.placement != null || config.offset != null) {
75
+ vm.configure({
76
+ placement: config.placement,
77
+ offset: config.offset
78
+ })
79
+ }
80
+
81
+ const type = config.type || 'info'
82
+ const duration = config.duration !== undefined ? config.duration : type === 'loading' ? 0 : globalConfig.duration
83
+
84
+ const key = vm.add({
85
+ key: config.key,
86
+ type,
87
+ content: config.content,
88
+ description: config.description,
89
+ action: config.action,
90
+ duration,
91
+ onClose: config.onClose
92
+ })
93
+
94
+ return function close() {
95
+ vm.remove(key)
96
+ }
97
+ }
98
+
99
+ function makeOpener(type) {
100
+ return function (content, duration, onClose) {
101
+ const cfg = toConfig(content, duration, onClose)
102
+ cfg.type = type
103
+ return open(cfg)
104
+ }
105
+ }
106
+
107
+ export const wkMessage = {
108
+ open,
109
+ success: makeOpener('success'),
110
+ error: makeOpener('error'),
111
+ info: makeOpener('info'),
112
+ warning: makeOpener('warning'),
113
+ loading: makeOpener('loading'),
114
+ // destroy(key) removes one message; destroy() with no key removes all.
115
+ destroy(key) {
116
+ if (!containerVm) return
117
+ if (key === undefined) {
118
+ containerVm.clear()
119
+ return
120
+ }
121
+ containerVm.remove(key)
122
+ },
123
+ destroyAll() {
124
+ if (containerVm) containerVm.clear()
125
+ },
126
+ // Update global defaults (placement/offset/maxCount/duration). Applies live.
127
+ config(options) {
128
+ globalConfig = { ...globalConfig, ...options }
129
+ if (containerVm) containerVm.configure(globalConfig)
130
+ }
131
+ }
132
+
133
+ export default wkMessage
@@ -0,0 +1,42 @@
1
+ <template>
2
+ <div class="ui-progress" role="progressbar" :aria-valuemin="0" :aria-valuemax="max" :aria-valuenow="clampedValue">
3
+ <div class="ui-progress__bar" :style="{ width: percent + '%' }" />
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ export default {
9
+ name: 'Progress',
10
+
11
+ props: {
12
+ // Current progress value, clamped to the [0, max] range.
13
+ value: {
14
+ type: Number,
15
+ default: 0
16
+ },
17
+ // Upper bound of the progress scale (value === max renders a full bar).
18
+ max: {
19
+ type: Number,
20
+ default: 100,
21
+ validator: function (v) {
22
+ return v > 0
23
+ }
24
+ }
25
+ },
26
+
27
+ emits: [],
28
+
29
+ computed: {
30
+ clampedValue() {
31
+ if (this.value < 0) return 0
32
+ if (this.value > this.max) return this.max
33
+ return this.value
34
+ },
35
+ percent() {
36
+ return (this.clampedValue / this.max) * 100
37
+ }
38
+ }
39
+ }
40
+ </script>
41
+
42
+ <style src="./progress.css" scoped></style>
@@ -0,0 +1,15 @@
1
+ .ui-progress {
2
+ position: relative;
3
+ width: 100%;
4
+ height: 6px;
5
+ overflow: hidden;
6
+ background: var(--accent-2);
7
+ border-radius: var(--rounded-xl);
8
+ }
9
+
10
+ .ui-progress__bar {
11
+ height: 100%;
12
+ background: var(--primary-fg);
13
+ border-radius: var(--rounded-md) 0 0 var(--rounded-md);
14
+ transition: width 0.2s ease;
15
+ }
@@ -36,7 +36,7 @@ export default {
36
36
  default: ''
37
37
  },
38
38
  value: {
39
- type: String,
39
+ type: [String, Number, Boolean],
40
40
  required: true
41
41
  },
42
42
  checked: {
@@ -29,8 +29,8 @@ export default {
29
29
 
30
30
  props: {
31
31
  value: {
32
- type: String,
33
- default: ''
32
+ type: [String, Number, Boolean],
33
+ default: null
34
34
  },
35
35
  options: {
36
36
  type: Array,
@@ -113,12 +113,7 @@
113
113
  'ui-table__row',
114
114
  'ui-table__row--body',
115
115
  row._selected && 'ui-table__row--selected',
116
- hasDrag && dragState && row._index === dragState.fromIndex && 'ui-table__row--dragging',
117
- hasDrag &&
118
- row._index === dragOverIndex &&
119
- dragState &&
120
- row._index !== dragState.fromIndex &&
121
- 'ui-table__row--drag-over'
116
+ hasDrag && dragState && row._index === dragState.currentIndex && 'ui-table__row--dragging'
122
117
  ]"
123
118
  :style="[{ height: toCssSize(rowHeight) }, rowScrollStyle]"
124
119
  v-bind="row._attrs"
@@ -263,7 +258,8 @@ export default {
263
258
  resizeState: null,
264
259
  columnWidths: {},
265
260
  dragState: null,
266
- dragOverIndex: null
261
+ dragOverIndex: null,
262
+ internalData: this.dataSource.slice()
267
263
  }
268
264
  },
269
265
  computed: {
@@ -380,7 +376,7 @@ export default {
380
376
  return this.columns.length + (this.hasDrag ? 1 : 0) + (this.hasSelection ? 1 : 0)
381
377
  },
382
378
  rows() {
383
- const data = this.dataSource.slice()
379
+ const data = this.internalData.slice()
384
380
  if (!this.sortField || !this.sortOrder) return data
385
381
  const col = this.normalizedColumns.find(c => c._field === this.sortField)
386
382
  if (!col || !col.sorter) return data
@@ -485,6 +481,11 @@ export default {
485
481
  watch: {
486
482
  selectedRowKeys(next) {
487
483
  this.internalSelected = (next || []).slice()
484
+ },
485
+ dataSource(next) {
486
+ if (!this.dragState) {
487
+ this.internalData = (next || []).slice()
488
+ }
488
489
  }
489
490
  },
490
491
  mounted() {
@@ -665,7 +666,7 @@ export default {
665
666
  this.emitSelection()
666
667
  },
667
668
  onDragStart(e, row) {
668
- this.dragState = { fromIndex: row._index, record: row._record }
669
+ this.dragState = { fromIndex: row._index, currentIndex: row._index, record: row._record }
669
670
  e.dataTransfer.effectAllowed = 'move'
670
671
 
671
672
  const tr = e.currentTarget.closest('tr')
@@ -704,7 +705,20 @@ export default {
704
705
  },
705
706
  onDragOver(e, index) {
706
707
  if (!this.dragState) return
707
- this.dragOverIndex = index
708
+ if (index === this.dragState.currentIndex) return
709
+ // Only reorder once the pointer crosses the row's midpoint to prevent oscillation.
710
+ const rect = e.currentTarget.getBoundingClientRect()
711
+ const midY = rect.top + rect.height / 1.5
712
+ if (index > this.dragState.currentIndex && e.clientY < midY) return
713
+ if (index < this.dragState.currentIndex && e.clientY > midY) return
714
+ // FLIP — snapshot "First" positions before the data changes.
715
+ const firstRects = this.snapshotRowRects()
716
+ const data = this.internalData.slice()
717
+ const [moved] = data.splice(this.dragState.currentIndex, 1)
718
+ data.splice(index, 0, moved)
719
+ this.internalData = data
720
+ this.dragState = { ...this.dragState, currentIndex: index }
721
+ this.$nextTick(() => this.flipAnimateRows(firstRects))
708
722
  },
709
723
  onDragLeave(e) {
710
724
  if (!e.currentTarget.contains(e.relatedTarget)) {
@@ -713,21 +727,16 @@ export default {
713
727
  },
714
728
  onDrop(e, toIndex) {
715
729
  if (!this.dragState) return
716
- const { fromIndex, record } = this.dragState
717
- if (fromIndex === toIndex) {
718
- this.dragState = null
719
- this.dragOverIndex = null
720
- return
721
- }
722
- // FLIP — snapshot "First" positions before the data changes.
723
- const firstRects = this.snapshotRowRects()
724
- this.$emit('drag-record', { record, fromIndex, toIndex }, e)
730
+ const { fromIndex, record, currentIndex } = this.dragState
731
+ this.$emit('drag-record', { record, fromIndex, toIndex: currentIndex }, e)
725
732
  this.dragState = null
726
733
  this.dragOverIndex = null
727
- // After Vue re-renders with the new order, run the Invert+Play phase.
728
- this.$nextTick(() => this.flipAnimateRows(firstRects))
729
734
  },
730
735
  onDragEnd() {
736
+ if (this.dragState) {
737
+ // Drag cancelled (no drop) — revert live reorder.
738
+ this.internalData = this.dataSource.slice()
739
+ }
731
740
  this.dragState = null
732
741
  this.dragOverIndex = null
733
742
  },
@@ -161,6 +161,10 @@
161
161
  cursor: col-resize;
162
162
  }
163
163
 
164
+ .ui-table__resize-handle:hover {
165
+ height: 100%;
166
+ }
167
+
164
168
  .ui-table__resize-handle--left {
165
169
  right: auto;
166
170
  left: -4px;
@@ -179,8 +183,8 @@
179
183
 
180
184
  .ui-table__resize-handle:hover::before,
181
185
  .ui-table__resize-handle.resizing::before {
182
- width: 2px;
183
- background: var(--border-focus);
186
+ width: 3px;
187
+ background: #3f8dff;
184
188
  }
185
189
 
186
190
  .ui-table__th-inner {
@@ -275,6 +279,18 @@
275
279
  border-right: none;
276
280
  }
277
281
 
282
+ @keyframes ui-table-sticky-border-hide {
283
+ to {
284
+ border-right-width: 0;
285
+ }
286
+ }
287
+
288
+ .ui-table--bordered .ui-table__cell--fixed-left-last {
289
+ animation: ui-table-sticky-border-hide linear both;
290
+ animation-timeline: scroll(nearest x);
291
+ animation-range: 0px 1px;
292
+ }
293
+
278
294
  /* With a scrollbar spacer present, the last real header column is second-to-last. */
279
295
  .ui-table--bordered .ui-table__row--has-scrollbar .ui-table__cell:nth-last-child(2) {
280
296
  border-right: none;
package/src/index.js CHANGED
@@ -20,6 +20,7 @@ export { default as WkInput } from './components/input/Input.vue'
20
20
  export { default as WkInputNumber } from './components/input-number/InputNumber.vue'
21
21
  export { default as WkMenu } from './components/menu/Menu.vue'
22
22
  export { default as WkPagination } from './components/pagination/Pagination.vue'
23
+ export { default as WkProgress } from './components/progress/Progress.vue'
23
24
  export { default as WkRadio } from './components/radio/Radio.vue'
24
25
  export { default as WkRadioGroup } from './components/radio-group/RadioGroup.vue'
25
26
  export { default as WkRichCheckboxGroup } from './components/rich-checkbox-group/RichCheckboxGroup.vue'
@@ -40,3 +41,7 @@ export { default as WkToggle } from './components/toggle/Toggle.vue'
40
41
  export { default as WkToggleGroup } from './components/toggle-group/ToggleGroup.vue'
41
42
  export { default as WkTooltip } from './components/tooltip/Tooltip.vue'
42
43
  export { default as WkTypography } from './components/typography/Typography.vue'
44
+ export { default as WkDatePicker } from './components/date-picker/DatePicker.vue'
45
+
46
+ // Imperative singleton service (no SFC export — mounts its own container to body).
47
+ export { wkMessage } from './components/message/message.js'