webcake-ui-kit 1.1.26 → 1.1.28

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webcake-ui-kit",
3
- "version": "1.1.26",
3
+ "version": "1.1.28",
4
4
  "description": "UI Kit for Vue 2 && 3 - Pure Options API",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
@@ -17,6 +17,7 @@
17
17
  "./styles/*": "./src/styles/*",
18
18
  "./WkAccordion": "./src/components/accordion/Accordion.vue",
19
19
  "./WkAccordionItem": "./src/components/accordion-item/AccordionItem.vue",
20
+ "./WkAlert": "./src/components/alert/Alert.vue",
20
21
  "./WkAlertDialog": "./src/components/alert-dialog/AlertDialog.vue",
21
22
  "./WkAvatar": "./src/components/avatar/Avatar.vue",
22
23
  "./WkAvatarStack": "./src/components/avatar-stack/AvatarStack.vue",
@@ -0,0 +1,77 @@
1
+ <template>
2
+ <div class="ui-alert" :class="`ui-alert--${type}`" role="alert">
3
+ <div class="ui-alert__body">
4
+ <span class="ui-alert__icon">
5
+ <slot name="icon"><component :is="iconComponent" :size="20" /></slot>
6
+ </span>
7
+ <div class="ui-alert__content">
8
+ <div v-if="hasTitle" class="ui-alert__title">
9
+ <slot>{{ title }}</slot>
10
+ </div>
11
+ <div v-if="hasDescription" class="ui-alert__description">
12
+ <slot name="description">{{ description }}</slot>
13
+ </div>
14
+ </div>
15
+ </div>
16
+ <div v-if="hasAction" class="ui-alert__action">
17
+ <slot name="action" />
18
+ </div>
19
+ <button v-if="closable" class="ui-alert__close" type="button" aria-label="Close" @click.stop="$emit('close')">
20
+ <WkiX :size="16" class="ui-alert__close-icon" />
21
+ </button>
22
+ </div>
23
+ </template>
24
+
25
+ <script>
26
+ import { WkiCircleCheck, WkiCircleAlert, WkiTriangleAlert, WkiInfo, WkiX } from '../../icons'
27
+
28
+ const ICON_BY_TYPE = {
29
+ neutral: 'WkiCircleCheck',
30
+ error: 'WkiCircleAlert',
31
+ warning: 'WkiTriangleAlert',
32
+ info: 'WkiInfo'
33
+ }
34
+
35
+ export default {
36
+ name: 'Alert',
37
+ components: { WkiCircleCheck, WkiCircleAlert, WkiTriangleAlert, WkiInfo, WkiX },
38
+ props: {
39
+ type: {
40
+ type: String,
41
+ default: 'neutral',
42
+ validator: function (v) {
43
+ return ['neutral', 'error', 'warning', 'info'].includes(v)
44
+ }
45
+ },
46
+ title: {
47
+ type: String,
48
+ default: ''
49
+ },
50
+ description: {
51
+ type: String,
52
+ default: ''
53
+ },
54
+ closable: {
55
+ type: Boolean,
56
+ default: false
57
+ }
58
+ },
59
+ emits: ['close'],
60
+ computed: {
61
+ iconComponent: function () {
62
+ return ICON_BY_TYPE[this.type] || ICON_BY_TYPE.neutral
63
+ },
64
+ hasTitle: function () {
65
+ return !!(this.title || this.$slots.default)
66
+ },
67
+ hasDescription: function () {
68
+ return !!(this.description || this.$slots.description)
69
+ },
70
+ hasAction: function () {
71
+ return !!this.$slots.action
72
+ }
73
+ }
74
+ }
75
+ </script>
76
+
77
+ <style src="./alert.css" scoped></style>
@@ -0,0 +1,117 @@
1
+ .ui-alert {
2
+ display: flex;
3
+ align-items: center;
4
+ gap: var(--spacing-md);
5
+ padding: var(--spacing-10) var(--spacing-sm);
6
+ border: 1px solid transparent;
7
+ border-radius: var(--rounded-xl);
8
+ overflow: hidden;
9
+ box-sizing: border-box;
10
+ font-family: var(--font-family-body);
11
+ }
12
+
13
+ /* — layout — */
14
+ .ui-alert__body {
15
+ display: flex;
16
+ flex: 1 1 0;
17
+ min-width: 0;
18
+ gap: var(--spacing-sm);
19
+ align-items: flex-start;
20
+ }
21
+
22
+ .ui-alert__icon {
23
+ display: inline-flex;
24
+ align-items: center;
25
+ justify-content: center;
26
+ flex-shrink: 0;
27
+ width: 20px;
28
+ height: 20px;
29
+ }
30
+
31
+ .ui-alert__content {
32
+ display: flex;
33
+ flex: 1 1 0;
34
+ min-width: 0;
35
+ flex-direction: column;
36
+ gap: var(--spacing-3xs);
37
+ }
38
+
39
+ .ui-alert__title {
40
+ font-size: var(--paragraph-small-font-size);
41
+ line-height: var(--paragraph-small-line-height);
42
+ letter-spacing: var(--paragraph-small-letter-spacing);
43
+ font-weight: var(--paragraph-medium-font-weight);
44
+ word-break: break-word;
45
+ }
46
+
47
+ .ui-alert__description {
48
+ font-size: var(--paragraph-small-font-size);
49
+ line-height: var(--paragraph-small-line-height);
50
+ letter-spacing: var(--paragraph-small-letter-spacing);
51
+ font-weight: var(--paragraph-font-weight);
52
+ color: var(--muted-fg);
53
+ word-break: break-word;
54
+ }
55
+
56
+ .ui-alert__action {
57
+ display: inline-flex;
58
+ align-items: center;
59
+ flex-shrink: 0;
60
+ }
61
+
62
+ .ui-alert__close {
63
+ display: inline-flex;
64
+ align-items: center;
65
+ justify-content: center;
66
+ flex-shrink: 0;
67
+ width: 20px;
68
+ height: 20px;
69
+ padding: 0;
70
+ border: none;
71
+ background: none;
72
+ color: inherit;
73
+ cursor: pointer;
74
+ border-radius: var(--rounded-md);
75
+ outline: none;
76
+ box-sizing: border-box;
77
+ }
78
+
79
+ .ui-alert__close:hover {
80
+ background: rgba(0, 0, 0, 0.06);
81
+ }
82
+
83
+ .ui-alert__close:focus-visible {
84
+ box-shadow: 0 0 0 3px var(--focus-ring);
85
+ }
86
+
87
+ .ui-alert__close-icon {
88
+ display: block;
89
+ width: 16px;
90
+ height: 16px;
91
+ flex-shrink: 0;
92
+ }
93
+
94
+ /* — types — */
95
+ .ui-alert--neutral {
96
+ background: var(--card);
97
+ border-color: var(--border-primary);
98
+ color: var(--primary-fg);
99
+ }
100
+
101
+ .ui-alert--error {
102
+ background: var(--destructive-subtle);
103
+ border-color: var(--destructive-border-subtle);
104
+ color: var(--destructive-text);
105
+ }
106
+
107
+ .ui-alert--warning {
108
+ background: var(--warning-subtle);
109
+ border-color: var(--warning-border-subtle);
110
+ color: var(--warning-text);
111
+ }
112
+
113
+ .ui-alert--info {
114
+ background: var(--infor-subtle);
115
+ border-color: var(--infor-border-subtle);
116
+ color: var(--infor-text);
117
+ }
@@ -50,6 +50,10 @@
50
50
  color: var(--destructive-text);
51
51
  }
52
52
 
53
+ .ui-dropdown-items__item--destructive:hover {
54
+ background: var(--destructive-subtle) !important;
55
+ }
56
+
53
57
  .ui-dropdown-items__item--sub {
54
58
  padding-left: calc(var(--spacing-sm) + var(--spacing-md));
55
59
  }
@@ -75,6 +75,7 @@
75
75
  {{ selectedLabel || placeholder }}
76
76
  </span>
77
77
  </span>
78
+ <span v-if="append" class="ui-select__append">{{ append }}</span>
78
79
  <Spinner v-if="loading" type="mirrored" size="sm" />
79
80
  <span v-else class="ui-select__chevron">
80
81
  <WkiChevronDown :size="16" />
@@ -152,6 +153,10 @@ export default {
152
153
  type: String,
153
154
  default: ''
154
155
  },
156
+ append: {
157
+ type: String,
158
+ default: ''
159
+ },
155
160
  disabled: {
156
161
  type: Boolean,
157
162
  default: false
@@ -399,14 +404,41 @@ export default {
399
404
  // Vue 3: $slots.default is a function; Vue 2: it's an array
400
405
  return typeof this.$slots.default === 'function' ? this.$slots.default() : this.$slots.default || []
401
406
  },
407
+ _slotText(vnode) {
408
+ // Vue 2: text lives in componentOptions.children as text-vnodes
409
+ if (vnode.componentOptions && Array.isArray(vnode.componentOptions.children)) {
410
+ return vnode.componentOptions.children
411
+ .map(function (c) {
412
+ return c.text || ''
413
+ })
414
+ .join('')
415
+ .trim()
416
+ }
417
+ // Vue 3: text lives in children.default() as vnodes with string .children
418
+ if (vnode.children && typeof vnode.children.default === 'function') {
419
+ try {
420
+ return vnode.children
421
+ .default()
422
+ .map(function (c) {
423
+ return typeof c.children === 'string' ? c.children : ''
424
+ })
425
+ .join('')
426
+ .trim()
427
+ } catch (e) {
428
+ return ''
429
+ }
430
+ }
431
+ return ''
432
+ },
402
433
  _syncSlotLabels() {
403
434
  const collect = nodes => {
404
435
  if (!Array.isArray(nodes)) return
405
436
  nodes.forEach(vnode => {
406
437
  if (!vnode) return
407
438
  const props = vnode.props || (vnode.componentOptions && vnode.componentOptions.propsData)
408
- if (props && props.value != null && props.label) {
409
- this.registerOption(String(props.value), String(props.label))
439
+ if (props && props.value != null) {
440
+ const label = props.label || this._slotText(vnode)
441
+ if (label) this.registerOption(String(props.value), String(label))
410
442
  }
411
443
  if (Array.isArray(vnode.children)) collect(vnode.children)
412
444
  })
@@ -419,8 +451,9 @@ export default {
419
451
  for (const vnode of nodes) {
420
452
  if (!vnode) continue
421
453
  const props = vnode.props || (vnode.componentOptions && vnode.componentOptions.propsData)
422
- if (props && String(props.value) === String(value) && props.label) {
423
- return String(props.label)
454
+ if (props && String(props.value) === String(value)) {
455
+ const label = props.label || this._slotText(vnode)
456
+ if (label) return String(label)
424
457
  }
425
458
  if (Array.isArray(vnode.children)) {
426
459
  const found = scan(vnode.children)
@@ -70,7 +70,8 @@
70
70
  color: var(--muted-fg);
71
71
  }
72
72
 
73
- .ui-select__prepend {
73
+ .ui-select__prepend,
74
+ .ui-select__append {
74
75
  font-family: var(--font-family-body);
75
76
  font-size: var(--paragraph-small-font-size);
76
77
  line-height: var(--paragraph-small-line-height);
@@ -145,7 +146,8 @@
145
146
  }
146
147
 
147
148
  .ui-select--xs .ui-select__value,
148
- .ui-select--xs .ui-select__prepend {
149
+ .ui-select--xs .ui-select__prepend,
150
+ .ui-select--xs .ui-select__append {
149
151
  font-size: var(--paragraph-mini-font-size);
150
152
  line-height: var(--paragraph-mini-line-height);
151
153
  }
@@ -13,6 +13,7 @@
13
13
  transition: background 0.15s ease;
14
14
  box-shadow: var(--shadow-xs);
15
15
  outline: none;
16
+ background: var(--color-neutral-300);
16
17
  }
17
18
 
18
19
  .ui-switch--on {
@@ -6,7 +6,7 @@
6
6
  <table class="ui-table__table" :style="tableStyle">
7
7
  <colgroup>
8
8
  <col v-if="hasSelection" class="ui-table__col ui-table__col--selection" />
9
- <col v-for="col in columns" :key="colKey(col)" :style="colStyle(col)" />
9
+ <col v-for="col in normalizedColumns" :key="col._key" :style="col._style" />
10
10
  </colgroup>
11
11
  <thead class="ui-table__head">
12
12
  <tr
@@ -21,17 +21,17 @@
21
21
  <WkCheckbox :checked="allSelected" :disabled="!rows.length" @change="onToggleAll" />
22
22
  </th>
23
23
  <th
24
- v-for="(col, i) in columns"
25
- :key="colKey(col)"
24
+ v-for="(col, i) in normalizedColumns"
25
+ :key="col._key"
26
26
  :class="[
27
27
  'ui-table__cell',
28
28
  'ui-table__th',
29
- `ui-table__cell--${alignOf(col)}`,
30
- isSortable(col) && 'ui-table__th--sortable'
29
+ `ui-table__cell--${col._align}`,
30
+ col._isSortable && 'ui-table__th--sortable'
31
31
  ]"
32
32
  :style="headCellStyle(hasSelection ? i + 1 : i, col)"
33
- :tabindex="isSortable(col) ? 0 : undefined"
34
- :aria-sort="ariaSort(col)"
33
+ :tabindex="col._isSortable ? 0 : undefined"
34
+ :aria-sort="col._ariaSort"
35
35
  @click="onHeaderClick(col)"
36
36
  @keydown.enter.prevent="onHeaderClick(col)"
37
37
  @keydown.space.prevent="onHeaderClick(col)"
@@ -41,12 +41,12 @@
41
41
  <span class="ui-table__th-inner">
42
42
  <slot name="headerCell" :column="col">{{ col.title }}</slot>
43
43
  <span
44
- v-if="isSortable(col)"
45
- :class="['ui-table__sort', sortStateOf(col) ? 'ui-table__sort--active' : 'ui-table__sort--inactive']"
44
+ v-if="col._isSortable"
45
+ :class="['ui-table__sort', col._sortState ? 'ui-table__sort--active' : 'ui-table__sort--inactive']"
46
46
  aria-hidden="true"
47
47
  >
48
- <WkiArrowUp v-if="sortStateOf(col) === 'ascend'" :size="16" />
49
- <WkiArrowDown v-else-if="sortStateOf(col) === 'descend'" :size="16" />
48
+ <WkiArrowUp v-if="col._sortState === 'ascend'" :size="16" />
49
+ <WkiArrowDown v-else-if="col._sortState === 'descend'" :size="16" />
50
50
  <WkiArrowDownUp v-else :size="16" />
51
51
  </span>
52
52
  </span>
@@ -93,15 +93,15 @@
93
93
  <WkCheckbox :checked="isSelected(record, rowIndex(i))" />
94
94
  </td>
95
95
  <td
96
- v-for="col in columns"
97
- :key="colKey(col)"
96
+ v-for="col in normalizedColumns"
97
+ :key="col._key"
98
98
  :class="[
99
99
  'ui-table__cell',
100
100
  'ui-table__td',
101
- `ui-table__cell--${alignOf(col)}`,
101
+ `ui-table__cell--${col._align}`,
102
102
  col.ellipsis && 'ui-table__cell--ellipsis'
103
103
  ]"
104
- :style="colStyle(col)"
104
+ :style="col._style"
105
105
  >
106
106
  <slot name="bodyCell" :column="col" :record="record" :text="cellText(col, record)" :index="rowIndex(i)">{{
107
107
  cellText(col, record)
@@ -189,6 +189,41 @@ export default {
189
189
  }
190
190
  },
191
191
  computed: {
192
+ selectedKeysSet() {
193
+ return new Set(this.internalSelected)
194
+ },
195
+ normalizedColumns() {
196
+ return this.columns.map(col => {
197
+ const _key = col.key != null ? col.key : col.dataIndex
198
+ const _align = ['left', 'right', 'center'].includes(col.align) ? col.align : 'left'
199
+ let _style = {}
200
+ if (col.width != null) {
201
+ _style = { width: typeof col.width === 'number' ? `${col.width}px` : col.width }
202
+ }
203
+ const _isSortable = !!col.sorter
204
+ const _field = col.dataIndex != null ? col.dataIndex : col.key
205
+
206
+ let _sortState = null
207
+ if (this.sortField === _field) {
208
+ _sortState = this.sortOrder
209
+ }
210
+
211
+ let _ariaSort = undefined
212
+ if (_sortState === 'ascend') _ariaSort = 'ascending'
213
+ else if (_sortState === 'descend') _ariaSort = 'descending'
214
+
215
+ return {
216
+ ...col,
217
+ _key,
218
+ _align,
219
+ _style,
220
+ _isSortable,
221
+ _field,
222
+ _sortState,
223
+ _ariaSort
224
+ }
225
+ })
226
+ },
192
227
  hasSelection() {
193
228
  return !!this.rowSelection
194
229
  },
@@ -198,7 +233,7 @@ export default {
198
233
  rows() {
199
234
  const data = this.dataSource.slice()
200
235
  if (!this.sortField || !this.sortOrder) return data
201
- const col = this.columns.find(c => this.fieldOf(c) === this.sortField)
236
+ const col = this.normalizedColumns.find(c => c._field === this.sortField)
202
237
  if (!col || !col.sorter) return data
203
238
  const cmp =
204
239
  typeof col.sorter === 'function' ? col.sorter : (a, b) => defaultCompare(a[this.sortField], b[this.sortField])
@@ -208,7 +243,7 @@ export default {
208
243
  },
209
244
  allSelected() {
210
245
  if (!this.rows.length) return false
211
- return this.rows.every((record, index) => this.internalSelected.includes(this.getRowKey(record, index)))
246
+ return this.rows.every((record, index) => this.selectedKeysSet.has(this.getRowKey(record, index)))
212
247
  },
213
248
  hasVerticalScroll() {
214
249
  return this.height != null || !!(this.scroll && this.scroll.y != null)
@@ -365,40 +400,15 @@ export default {
365
400
  if (this.hasVerticalScroll && this.colWidths[index] !== undefined) {
366
401
  return { width: `${this.colWidths[index]}px` }
367
402
  }
368
- return col ? this.colStyle(col) : {}
369
- },
370
- fieldOf(col) {
371
- return col.dataIndex != null ? col.dataIndex : col.key
372
- },
373
- colKey(col) {
374
- return col.key != null ? col.key : col.dataIndex
375
- },
376
- colStyle(col) {
377
- if (col.width == null) return {}
378
- return { width: typeof col.width === 'number' ? `${col.width}px` : col.width }
379
- },
380
- alignOf(col) {
381
- return ['left', 'right', 'center'].includes(col.align) ? col.align : 'left'
403
+ return col ? col._style : {}
382
404
  },
383
405
  cellText(col, record) {
384
- const field = this.fieldOf(col)
406
+ const field = col._field
385
407
  return field != null ? record[field] : ''
386
408
  },
387
- isSortable(col) {
388
- return !!col.sorter
389
- },
390
- sortStateOf(col) {
391
- return this.sortField === this.fieldOf(col) ? this.sortOrder : null
392
- },
393
- ariaSort(col) {
394
- const state = this.sortStateOf(col)
395
- if (state === 'ascend') return 'ascending'
396
- if (state === 'descend') return 'descending'
397
- return undefined
398
- },
399
409
  onHeaderClick(col) {
400
- if (!this.isSortable(col)) return
401
- const field = this.fieldOf(col)
410
+ if (!col._isSortable) return
411
+ const field = col._field
402
412
  let order
403
413
  if (this.sortField !== field) {
404
414
  order = 'ascend'
@@ -417,7 +427,7 @@ export default {
417
427
  return key != null ? key : index
418
428
  },
419
429
  isSelected(record, index) {
420
- return this.internalSelected.includes(this.getRowKey(record, index))
430
+ return this.selectedKeysSet.has(this.getRowKey(record, index))
421
431
  },
422
432
  emitSelection() {
423
433
  const keys = this.internalSelected.slice()
@@ -6,7 +6,7 @@
6
6
  box-shadow: var(--shadow-xs);
7
7
  font-family: var(--font-family-body);
8
8
  color: var(--primary-fg);
9
- box-sizing: border-box;
9
+ overflow: hidden;
10
10
  transition:
11
11
  border-color 0.12s ease,
12
12
  box-shadow 0.12s ease;
@@ -119,3 +119,19 @@
119
119
  .ui-textarea__field:disabled {
120
120
  cursor: not-allowed;
121
121
  }
122
+
123
+ .ui-textarea__field::-webkit-scrollbar {
124
+ width: 8px;
125
+ }
126
+
127
+ .ui-textarea__field::-webkit-scrollbar-track {
128
+ border-left: 1px solid var(--border-primary);
129
+ }
130
+
131
+ .ui-textarea__field::-webkit-scrollbar-thumb {
132
+ background: var(--border-primary);
133
+ border-radius: 3px;
134
+ border-left: 2px solid transparent;
135
+ border-right: 1px solid transparent;
136
+ background-clip: content-box;
137
+ }
package/src/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export { default as WkAccordion } from './components/accordion/Accordion.vue'
2
2
  export { default as WkAccordionItem } from './components/accordion-item/AccordionItem.vue'
3
+ export { default as WkAlert } from './components/alert/Alert.vue'
3
4
  export { default as WkAlertDialog } from './components/alert-dialog/AlertDialog.vue'
4
5
  export { default as WkAvatar } from './components/avatar/Avatar.vue'
5
6
  export { default as WkAvatarStack } from './components/avatar-stack/AvatarStack.vue'
@@ -31,13 +31,16 @@
31
31
  --destructive-hover: var(--color-red-500);
32
32
  --destructive-subtle: var(--color-red-50);
33
33
  --destructive-border: var(--color-red-500);
34
+ --destructive-border-subtle: var(--color-red-200);
34
35
  --destructive-text: var(--color-red-600);
35
- --warning-subtle: var(--color-orange-50);
36
- --warning-border: var(--color-orange-400);
37
- --warning-text: var(--color-orange-500);
38
- --info-subtle: var(--color-indigo-50);
39
- --info-border: var(--color-indigo-500);
40
- --info-text: var(--color-indigo-600);
36
+ --warning-subtle: var(--color-yellow-50);
37
+ --warning-border: var(--color-yellow-500);
38
+ --warning-border-subtle: var(--color-yellow-200);
39
+ --warning-text: var(--color-orange-800);
40
+ --infor-subtle: var(--color-blue-50);
41
+ --infor-border: var(--color-blue-500);
42
+ --infor-border-subtle: var(--color-blue-200);
43
+ --infor-text: var(--color-blue-800);
41
44
  --ghost: var(--color-white-alpha-001);
42
45
  --ghost-foreground: var(--color-brand-neutrals-700);
43
46
  --ghost-hover: var(--color-black-alpha-5);
@@ -143,13 +146,16 @@
143
146
  --destructive-hover: var(--color-red-500);
144
147
  --destructive-subtle: var(--color-red-950);
145
148
  --destructive-border: var(--color-red-500);
149
+ --destructive-border-subtle: var(--color-red-800);
146
150
  --destructive-text: var(--color-red-400);
147
151
  --warning-subtle: var(--color-yellow-950);
148
- --warning-border: var(--color-orange-600);
149
- --warning-text: var(--color-orange-500);
150
- --info-subtle: var(--color-indigo-950);
151
- --info-border: var(--color-indigo-500);
152
- --info-text: var(--color-indigo-600);
152
+ --warning-border: var(--color-yellow-500);
153
+ --warning-border-subtle: var(--color-yellow-800);
154
+ --warning-text: var(--color-orange-300);
155
+ --infor-subtle: var(--color-blue-950);
156
+ --infor-border: var(--color-blue-500);
157
+ --infor-border-subtle: var(--color-blue-800);
158
+ --infor-text: var(--color-blue-300);
153
159
  --ghost: var(--color-white-alpha-001);
154
160
  --ghost-foreground: var(--color-brand-neutrals-200);
155
161
  --ghost-hover: var(--color-black-alpha-10);
@@ -29,4 +29,6 @@
29
29
  --spacing-10: 10px;
30
30
  --spacing-14: 14px;
31
31
  --spacing-15p5: 15.5px;
32
+ --spacing-28: 28px;
33
+ --spacing-36: 36px;
32
34
  }