webcake-ui-kit 1.1.29 → 1.1.30

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,71 @@
1
+ .ui-cal-header {
2
+ display: flex;
3
+ align-items: center;
4
+ gap: var(--spacing-xs);
5
+ min-height: 32px;
6
+ font-family: var(--font-family-body);
7
+ }
8
+
9
+ .ui-cal-header__nav {
10
+ display: inline-flex;
11
+ align-items: center;
12
+ gap: var(--spacing-3xs);
13
+ flex-shrink: 0;
14
+ }
15
+
16
+ /* One label/select group per visible month, centered over its grid. */
17
+ .ui-cal-header__labels {
18
+ display: flex;
19
+ flex: 1;
20
+ align-items: center;
21
+ gap: var(--spacing-md);
22
+ }
23
+
24
+ .ui-cal-header__group {
25
+ display: flex;
26
+ flex: 1;
27
+ align-items: center;
28
+ justify-content: center;
29
+ gap: var(--spacing-2xs);
30
+ }
31
+
32
+ /* Clickable month/year text label (default when selects are disabled). */
33
+ .ui-cal-header__label {
34
+ border: 0;
35
+ background: transparent;
36
+ cursor: pointer;
37
+ font-family: inherit;
38
+ font-size: var(--paragraph-small-font-size);
39
+ line-height: var(--paragraph-small-line-height);
40
+ font-weight: 400;
41
+ color: var(--primary-fg);
42
+ border-radius: var(--rounded-md);
43
+ }
44
+
45
+ /* .ui-select fills its container (width:100%), so size via these wrappers. */
46
+ .ui-cal-header__select {
47
+ display: block;
48
+ }
49
+
50
+ .ui-cal-header__select--month {
51
+ flex: 1 1 auto;
52
+ max-width: 98px;
53
+ }
54
+
55
+ .ui-cal-header__select--year {
56
+ flex: 0 0 88px;
57
+ width: 88px;
58
+ }
59
+
60
+ .ui-cal-header__label_month_not_select,
61
+ .ui-cal-header__label_year_not_select {
62
+ font-weight: 500;
63
+ }
64
+
65
+ .ui-cal-header__label_year_not_select {
66
+ margin-left: 4px;
67
+ }
68
+
69
+ .ui-cal-header__label_month_not_select {
70
+ margin-right: 4px;
71
+ }
@@ -0,0 +1,63 @@
1
+ .ui-time-panel {
2
+ display: flex;
3
+ gap: var(--spacing-3xs);
4
+ height: 200px;
5
+ font-family: var(--font-family-body);
6
+ }
7
+
8
+ /* Each part (hour/minute/second) scrolls independently. */
9
+ .ui-time-panel__col {
10
+ display: flex;
11
+ flex-direction: column;
12
+ gap: var(--spacing-3xs);
13
+ overflow-y: auto;
14
+ overflow-x: hidden;
15
+ padding: 0 var(--spacing-3xs);
16
+ }
17
+
18
+ .ui-time-panel__col + .ui-time-panel__col {
19
+ border-left: 1px solid var(--border-primary);
20
+ }
21
+
22
+ .ui-time-panel__col::-webkit-scrollbar {
23
+ width: 6px;
24
+ height: 6px;
25
+ }
26
+
27
+ .ui-time-panel__col::-webkit-scrollbar-thumb {
28
+ background: var(--accent-2);
29
+ border-radius: 999px;
30
+
31
+ border: 0 solid transparent;
32
+ background-clip: padding-box;
33
+ }
34
+
35
+ .ui-time-panel__opt {
36
+ flex-shrink: 0;
37
+ width: 44px;
38
+ height: 30px;
39
+ border: 0;
40
+ background: transparent;
41
+ cursor: pointer;
42
+ border-radius: var(--rounded-md);
43
+ font-family: inherit;
44
+ font-size: var(--paragraph-small-font-size);
45
+ line-height: var(--paragraph-small-line-height);
46
+ color: var(--primary-fg);
47
+ text-align: center;
48
+ }
49
+
50
+ .ui-time-panel__opt:hover:not(.is-disabled):not(.is-active) {
51
+ background: var(--accent-1);
52
+ }
53
+
54
+ .ui-time-panel__opt.is-active {
55
+ background: var(--primary-brand-bg);
56
+ color: var(--inverse-fg);
57
+ font-weight: 600;
58
+ }
59
+
60
+ .ui-time-panel__opt.is-disabled {
61
+ color: var(--muted-fg);
62
+ cursor: not-allowed;
63
+ }
@@ -126,6 +126,11 @@
126
126
  cursor: not-allowed;
127
127
  }
128
128
 
129
+ .ui-input__field::-webkit-calendar-picker-indicator {
130
+ display: none;
131
+ -webkit-appearance: none;
132
+ }
133
+
129
134
  /* Decoration slot wrappers — fixed-size icon containers. */
130
135
  .ui-input__decoration {
131
136
  display: inline-flex;
@@ -20,7 +20,6 @@
20
20
  display: flex;
21
21
  flex-direction: column;
22
22
  min-width: 176px;
23
- max-width: 320px;
24
23
  outline: none;
25
24
  pointer-events: auto;
26
25
  box-sizing: border-box;
@@ -0,0 +1,72 @@
1
+ <template>
2
+ <div class="ui-message" :class="`ui-message--${type}`" role="status" aria-live="polite">
3
+ <span class="ui-message__icon">
4
+ <WkSpinner v-if="type === 'loading'" size="sm" />
5
+ <component :is="iconComponent" v-else :size="18" />
6
+ </span>
7
+ <div class="ui-message__wrapper">
8
+ <span class="ui-message__content">
9
+ <slot>{{ content }}</slot>
10
+ </span>
11
+ <span v-if="description" class="ui-message__description">{{ description }}</span>
12
+ </div>
13
+ <WkButton v-if="action" size="mini" :variant="action.variant || 'primary'" @click="handleAction">{{
14
+ action.label
15
+ }}</WkButton>
16
+ </div>
17
+ </template>
18
+
19
+ <script>
20
+ import WkSpinner from '../spinner/Spinner.vue'
21
+ import WkButton from '../button/Button.vue'
22
+ import { WkiCircleCheck, WkiCircleX, WkiTriangleAlert, WkiInfo } from '../../icons'
23
+
24
+ const ICON_BY_TYPE = {
25
+ success: 'WkiCircleCheck',
26
+ error: 'WkiCircleX',
27
+ warning: 'WkiTriangleAlert',
28
+ info: 'WkiInfo'
29
+ }
30
+
31
+ export default {
32
+ name: 'Message',
33
+ components: { WkSpinner, WkButton, WkiCircleCheck, WkiCircleX, WkiTriangleAlert, WkiInfo },
34
+ props: {
35
+ type: {
36
+ type: String,
37
+ default: 'info',
38
+ validator: function (v) {
39
+ return ['info', 'success', 'error', 'warning', 'loading'].includes(v)
40
+ }
41
+ },
42
+ content: {
43
+ type: String,
44
+ default: ''
45
+ },
46
+ description: {
47
+ type: String,
48
+ default: null
49
+ },
50
+ action: {
51
+ type: Object,
52
+ default: null
53
+ }
54
+ },
55
+ emits: ['close'],
56
+ computed: {
57
+ iconComponent: function () {
58
+ return ICON_BY_TYPE[this.type] || ICON_BY_TYPE.info
59
+ }
60
+ },
61
+ methods: {
62
+ handleAction: function () {
63
+ if (typeof this.action.onClick === 'function') {
64
+ this.action.onClick()
65
+ }
66
+ this.$emit('close')
67
+ }
68
+ }
69
+ }
70
+ </script>
71
+
72
+ <style src="./message.css" scoped></style>
@@ -0,0 +1,200 @@
1
+ <template>
2
+ <div
3
+ class="ui-message-container"
4
+ :class="`ui-message-container--${placement}`"
5
+ :style="containerStyle"
6
+ @mouseenter="onMouseEnter"
7
+ @mouseleave="onMouseLeave"
8
+ >
9
+ <div :class="['ui-message-list', { 'ui-message-list--expanded': hovered }]">
10
+ <div v-for="(item, idx) in messages" :key="item.key" class="ui-message-item" :style="itemStyle(idx)">
11
+ <Message
12
+ :type="item.type"
13
+ :content="item.content"
14
+ :description="item.description"
15
+ :action="item.action"
16
+ @close="remove(item.key)"
17
+ />
18
+ </div>
19
+ </div>
20
+ </div>
21
+ </template>
22
+
23
+ <script>
24
+ import Message from './Message.vue'
25
+
26
+ let seed = 0
27
+
28
+ // Center placements ('top'/'bottom') flow messages as a spaced column; the four
29
+ // corner placements anchor to a corner and overlap messages (newest on top).
30
+ const PLACEMENTS = ['top', 'bottom', 'top-left', 'top-right', 'bottom-left', 'bottom-right']
31
+
32
+ export default {
33
+ name: 'MessageContainer',
34
+ components: { Message },
35
+ emits: [],
36
+ data() {
37
+ return {
38
+ messages: [],
39
+ placement: 'top',
40
+ offset: 24,
41
+ maxCount: 0,
42
+ hovered: false,
43
+ itemOffsets: []
44
+ }
45
+ },
46
+ computed: {
47
+ containerStyle() {
48
+ const p = this.placement
49
+ const o = this.offset + 'px'
50
+ const isBottom = p === 'bottom' || p === 'bottom-left' || p === 'bottom-right'
51
+ const style = {}
52
+ style[isBottom ? 'bottom' : 'top'] = o
53
+ // Corner placements pin a horizontal edge and drop the CSS center transform.
54
+ if (p === 'top-left' || p === 'bottom-left') {
55
+ style.left = o
56
+ style.right = 'auto'
57
+ style.transform = 'none'
58
+ } else if (p === 'top-right' || p === 'bottom-right') {
59
+ style.right = o
60
+ style.left = 'auto'
61
+ style.transform = 'none'
62
+ }
63
+ return style
64
+ }
65
+ },
66
+ watch: {
67
+ hovered(val) {
68
+ if (val) {
69
+ this.$nextTick(this.measureOffsets)
70
+ } else {
71
+ this.itemOffsets = []
72
+ }
73
+ },
74
+ 'messages.length'() {
75
+ if (this.hovered) this.$nextTick(this.measureOffsets)
76
+ }
77
+ },
78
+ // Timers live off the reactive data map so mutating them never triggers a render.
79
+ created() {
80
+ this._timers = {}
81
+ this._hoverTimer = null
82
+ },
83
+ // Declare both teardown hooks: Vue 2.7 fires beforeDestroy, Vue 3.4 fires beforeUnmount.
84
+ beforeUnmount() {
85
+ this.clear()
86
+ clearTimeout(this._hoverTimer)
87
+ },
88
+ // eslint-disable-next-line vue/no-deprecated-destroyed-lifecycle, vue/no-dupe-keys
89
+ beforeDestroy() {
90
+ this.clear()
91
+ clearTimeout(this._hoverTimer)
92
+ },
93
+ methods: {
94
+ onMouseEnter() {
95
+ clearTimeout(this._hoverTimer)
96
+ this.hovered = true
97
+ },
98
+ onMouseLeave() {
99
+ this._hoverTimer = setTimeout(() => {
100
+ this.hovered = false
101
+ }, 200)
102
+ },
103
+ // Measure each item's real offsetHeight and compute cumulative Y offsets
104
+ // for the expanded stack. Newest item (last in DOM) anchors at Y=0.
105
+ measureOffsets() {
106
+ if (!this.$el) return
107
+ const items = Array.from(this.$el.querySelectorAll('.ui-message-item'))
108
+ if (!items.length) {
109
+ this.itemOffsets = []
110
+ return
111
+ }
112
+ const GAP = 8
113
+ const n = items.length
114
+ const offsets = new Array(n)
115
+ let cumulative = 0
116
+ for (let reverseIdx = 0; reverseIdx < n; reverseIdx++) {
117
+ offsets[reverseIdx] = cumulative
118
+ cumulative += items[n - 1 - reverseIdx].offsetHeight + GAP
119
+ }
120
+ this.itemOffsets = offsets
121
+ },
122
+ // Returns inline style for item at v-for index idx (0 = oldest, last = newest).
123
+ itemStyle(idx) {
124
+ if (!this.hovered || !this.itemOffsets.length) return {}
125
+ const reverseIdx = this.messages.length - 1 - idx
126
+ const y = this.itemOffsets[reverseIdx]
127
+ if (y === undefined) return {}
128
+ const isBottom =
129
+ this.placement === 'bottom' || this.placement === 'bottom-left' || this.placement === 'bottom-right'
130
+ return { transform: 'translateY(' + (isBottom ? -y : y) + 'px) scaleX(1)', opacity: '1' }
131
+ },
132
+ // Merge global/service config into the container's own reactive state.
133
+ configure(opts) {
134
+ if (!opts) return
135
+ if (opts.placement) this.placement = PLACEMENTS.includes(opts.placement) ? opts.placement : 'top'
136
+ if (opts.offset != null) this.offset = opts.offset
137
+ if (opts.maxCount != null) this.maxCount = opts.maxCount
138
+ },
139
+ // Add (or update, when the key already exists) a message. Returns its key.
140
+ add(config) {
141
+ const key = config.key != null ? config.key : `wk-msg-${++seed}`
142
+ const existing = this.messages.find(m => m.key === key)
143
+ if (existing) {
144
+ existing.type = config.type || 'info'
145
+ existing.content = config.content != null ? config.content : ''
146
+ existing.description = config.description || null
147
+ existing.action = config.action || null
148
+ existing.onClose = config.onClose || null
149
+ this.scheduleRemoval(key, config.duration)
150
+ return key
151
+ }
152
+ this.messages.push({
153
+ key,
154
+ type: config.type || 'info',
155
+ content: config.content != null ? config.content : '',
156
+ description: config.description || null,
157
+ action: config.action || null,
158
+ onClose: config.onClose || null
159
+ })
160
+ // Enforce maxCount by evicting the oldest entries.
161
+ if (this.maxCount > 0 && this.messages.length > this.maxCount) {
162
+ const overflow = this.messages.slice(0, this.messages.length - this.maxCount)
163
+ overflow.forEach(m => this.remove(m.key))
164
+ }
165
+ this.scheduleRemoval(key, config.duration)
166
+ return key
167
+ },
168
+ scheduleRemoval(key, duration) {
169
+ this.clearTimer(key)
170
+ // duration <= 0 (or missing) => sticky; caller removes it manually.
171
+ if (duration && duration > 0) {
172
+ this._timers[key] = setTimeout(() => this.remove(key), duration)
173
+ }
174
+ },
175
+ remove(key) {
176
+ this.clearTimer(key)
177
+ const idx = this.messages.findIndex(m => m.key === key)
178
+ if (idx === -1) return
179
+ const [removed] = this.messages.splice(idx, 1)
180
+ if (removed && typeof removed.onClose === 'function') removed.onClose()
181
+ },
182
+ clear() {
183
+ Object.keys(this._timers).forEach(k => this.clearTimer(k))
184
+ const items = this.messages
185
+ this.messages = []
186
+ items.forEach(m => {
187
+ if (typeof m.onClose === 'function') m.onClose()
188
+ })
189
+ },
190
+ clearTimer(key) {
191
+ if (this._timers[key]) {
192
+ clearTimeout(this._timers[key])
193
+ delete this._timers[key]
194
+ }
195
+ }
196
+ }
197
+ }
198
+ </script>
199
+
200
+ <style src="./message-container.css" scoped></style>
@@ -0,0 +1,108 @@
1
+ .ui-message-container {
2
+ position: fixed;
3
+ left: 50%;
4
+ transform: translateX(-50%);
5
+ z-index: 2000;
6
+ pointer-events: none;
7
+ }
8
+
9
+ .ui-message-list {
10
+ display: grid;
11
+ pointer-events: auto;
12
+ --step: 44px;
13
+ }
14
+
15
+ /* All items share one grid cell; DOM order sets z — last in DOM = newest = on top */
16
+ .ui-message-item {
17
+ grid-area: 1 / 1;
18
+ pointer-events: auto;
19
+ transition:
20
+ transform 0.35s cubic-bezier(0.22, 1, 0.36, 1),
21
+ opacity 0.35s cubic-bezier(0.22, 1, 0.36, 1);
22
+ }
23
+
24
+ /* ── Collapsed stack peek ──────────────────────────────────── */
25
+
26
+ .ui-message-item:nth-last-child(1) {
27
+ z-index: 3;
28
+ }
29
+
30
+ .ui-message-item:nth-last-child(2) {
31
+ z-index: 2;
32
+ transform: translateY(8px) scaleX(0.95);
33
+ }
34
+
35
+ .ui-message-item:nth-last-child(n + 3) {
36
+ z-index: 1;
37
+ transform: translateY(16px) scaleX(0.9);
38
+ }
39
+
40
+ .ui-message-item:nth-last-child(n + 5) {
41
+ opacity: 0;
42
+ pointer-events: none;
43
+ }
44
+
45
+ /* Bottom placements: peek cards appear above the top card */
46
+ .ui-message-container--bottom .ui-message-item:nth-last-child(2),
47
+ .ui-message-container--bottom-left .ui-message-item:nth-last-child(2),
48
+ .ui-message-container--bottom-right .ui-message-item:nth-last-child(2) {
49
+ transform: translateY(-8px) scaleX(0.95);
50
+ }
51
+
52
+ .ui-message-container--bottom .ui-message-item:nth-last-child(n + 3),
53
+ .ui-message-container--bottom-left .ui-message-item:nth-last-child(n + 3),
54
+ .ui-message-container--bottom-right .ui-message-item:nth-last-child(n + 3) {
55
+ transform: translateY(-16px) scaleX(0.9);
56
+ }
57
+
58
+ /* ── Alignment ─────────────────────────────────────────────── */
59
+
60
+ .ui-message-container--top .ui-message-item,
61
+ .ui-message-container--bottom .ui-message-item {
62
+ justify-self: center;
63
+ }
64
+
65
+ .ui-message-container--top-left .ui-message-item,
66
+ .ui-message-container--bottom-left .ui-message-item {
67
+ justify-self: start;
68
+ }
69
+
70
+ .ui-message-container--top-right .ui-message-item,
71
+ .ui-message-container--bottom-right .ui-message-item {
72
+ justify-self: end;
73
+ }
74
+
75
+ /* ── Enter/leave animation ─────────────────────────────────── */
76
+ /*
77
+ * Vue 2 applies `-enter` / `-leave` for the start frame;
78
+ * Vue 3 applies `-enter-from` / `-leave-from`. Both listed for dual compat.
79
+ */
80
+ .ui-message-anim-enter-active,
81
+ .ui-message-anim-leave-active {
82
+ transition:
83
+ opacity 0.3s cubic-bezier(0.22, 1, 0.36, 1),
84
+ transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
85
+ }
86
+
87
+ .ui-message-anim-enter-from,
88
+ .ui-message-anim-enter,
89
+ .ui-message-anim-leave-to {
90
+ opacity: 0;
91
+ transform: translateY(-16px);
92
+ }
93
+
94
+ .ui-message-container--bottom .ui-message-anim-enter-from,
95
+ .ui-message-container--bottom .ui-message-anim-enter,
96
+ .ui-message-container--bottom .ui-message-anim-leave-to,
97
+ .ui-message-container--bottom-left .ui-message-anim-enter-from,
98
+ .ui-message-container--bottom-left .ui-message-anim-enter,
99
+ .ui-message-container--bottom-left .ui-message-anim-leave-to,
100
+ .ui-message-container--bottom-right .ui-message-anim-enter-from,
101
+ .ui-message-container--bottom-right .ui-message-anim-enter,
102
+ .ui-message-container--bottom-right .ui-message-anim-leave-to {
103
+ transform: translateY(16px);
104
+ }
105
+
106
+ .ui-message-anim-move {
107
+ transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
108
+ }
@@ -0,0 +1,69 @@
1
+ .ui-message {
2
+ pointer-events: auto;
3
+ display: inline-flex;
4
+ align-items: center;
5
+ gap: var(--spacing-sm);
6
+ max-width: 480px;
7
+ padding: var(--spacing-sm) var(--spacing-md);
8
+ background: var(--card);
9
+ border: 1px solid var(--border-primary);
10
+ border-radius: var(--rounded-xl);
11
+ box-shadow: var(--shadow-sonner);
12
+ }
13
+
14
+ .ui-message__icon {
15
+ display: inline-flex;
16
+ align-items: center;
17
+ justify-content: center;
18
+ flex-shrink: 0;
19
+ }
20
+
21
+ .ui-message__wrapper {
22
+ flex: 1;
23
+ min-width: 0;
24
+ display: flex;
25
+ flex-direction: column;
26
+ gap: var(--spacing-2xs);
27
+ }
28
+
29
+ .ui-message__content {
30
+ color: var(--primary-fg);
31
+ font-family: var(--font-family-body);
32
+ font-size: var(--paragraph-small-font-size);
33
+ font-style: normal;
34
+ font-weight: 500;
35
+ line-height: var(--paragraph-small-line-height);
36
+ letter-spacing: var(--paragraph-small-letter-spacing, 0);
37
+ word-break: break-word;
38
+ }
39
+
40
+ .ui-message__description {
41
+ color: var(--muted-fg);
42
+ font-size: var(--paragraph-small-font-size);
43
+ font-family: var(--font-family-body);
44
+ font-style: normal;
45
+ font-weight: 400;
46
+ line-height: var(--paragraph-small-line-height);
47
+ letter-spacing: var(--paragraph-small-letter-spacing);
48
+ word-break: break-word;
49
+ }
50
+
51
+ .ui-message--success .ui-message__icon {
52
+ color: var(--positive);
53
+ }
54
+
55
+ .ui-message--error .ui-message__icon {
56
+ color: var(--destructive);
57
+ }
58
+
59
+ .ui-message--warning .ui-message__icon {
60
+ color: var(--warning);
61
+ }
62
+
63
+ .ui-message--info .ui-message__icon {
64
+ color: var(--infor);
65
+ }
66
+
67
+ .ui-message--loading .ui-message__icon {
68
+ color: var(--primary-fg);
69
+ }