webcake-ui-kit 1.3.13 → 1.3.15

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.3.13",
3
+ "version": "1.3.15",
4
4
  "description": "UI Kit for Vue 2 && 3 - Pure Options API",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
@@ -106,15 +106,13 @@
106
106
  />
107
107
  </slot>
108
108
  <slot v-if="showEmpty" name="empty">
109
- <Empty description="No data">
110
- <template #media>
111
- <div class="select-empty">
112
- <img
113
- src="https://content.pancake.vn/web-media-262/2e/2a/0c/96/c2b58ae9f06c4dc2a3b83016e56d5b4132ff40637862ba23435e43a0-w:224-h:224-l:3927-t:image/png.png"
114
- />
115
- </div>
116
- </template>
117
- </Empty>
109
+ <div class="ui-select__empty">
110
+ <img
111
+ src="https://content.pancake.vn/web-media-262/2e/2a/0c/96/c2b58ae9f06c4dc2a3b83016e56d5b4132ff40637862ba23435e43a0-w:224-h:224-l:3927-t:image/png.png"
112
+ alt=""
113
+ />
114
+ <span>{{ emptyText }}</span>
115
+ </div>
118
116
  </slot>
119
117
  <div v-if="hasContentFooter()">
120
118
  <slot name="contentFooter"></slot>
@@ -142,12 +140,11 @@ import Menu from '../menu/Menu.vue'
142
140
  import SelectOption from '../select-option/SelectOption.vue'
143
141
  import Spinner from '../spinner/Spinner.vue'
144
142
  import Tag from '../tag/Tag.vue'
145
- import Empty from '../empty/Empty.vue'
146
143
 
147
144
  export default {
148
145
  name: 'Select',
149
146
 
150
- components: { Menu, SelectOption, Spinner, WkiChevronDown, Tag, Empty },
147
+ components: { Menu, SelectOption, Spinner, WkiChevronDown, Tag },
151
148
 
152
149
  provide() {
153
150
  return { select: this }
@@ -242,6 +239,10 @@ export default {
242
239
  placement: {
243
240
  type: String,
244
241
  default: 'bottom-start'
242
+ },
243
+ emptyText: {
244
+ type: String,
245
+ default: 'Chưa có dữ liệu'
245
246
  }
246
247
  },
247
248
 
@@ -264,13 +264,23 @@
264
264
  border-left: 1px solid var(--border-primary);
265
265
  }
266
266
 
267
- .ui-select__list ::v-deep .select-empty {
268
- height: 100px;
269
- width: 100px;
267
+ .ui-select__empty {
268
+ display: flex;
269
+ flex-direction: column;
270
+ align-items: center;
271
+ gap: 2px;
272
+ padding: 12px;
270
273
  }
271
274
 
272
- .ui-select__list ::v-deep .select-empty img {
273
- width: 100%;
274
- height: 100%;
275
- object-fit: cover;
275
+ .ui-select__empty img {
276
+ width: 56px;
277
+ height: 56px;
278
+ object-fit: contain;
279
+ }
280
+
281
+ .ui-select__empty span {
282
+ font-family: var(--font-family-body);
283
+ font-size: 12px;
284
+ line-height: 1.4;
285
+ color: var(--muted-fg);
276
286
  }
@@ -9,6 +9,9 @@
9
9
  <template #empty>
10
10
  <slot name="empty"></slot>
11
11
  </template>
12
+ <template #selectionHeader>
13
+ <slot name="selectionHeader"></slot>
14
+ </template>
12
15
  </TableWrapper>
13
16
  </TableResizeObserver>
14
17
  </TableLoading>
@@ -58,7 +61,7 @@ export default {
58
61
  rowDraggable: { type: Object, default: null },
59
62
  enableFixedLeft: { type: Boolean, default: false },
60
63
  rowHeight: { type: Number, default: 50 },
61
- headerHeight: { type: Number, default: 36 },
64
+ headerHeight: { type: Number, default: 40 },
62
65
  emptyText: { type: String, default: '' },
63
66
  height: { type: [Number, String], default: 0 }
64
67
  },
@@ -88,13 +91,18 @@ export default {
88
91
  },
89
92
 
90
93
  layoutInput: function () {
94
+ const hasSlotSelectionHeader = !!(
95
+ (this.$scopedSlots && this.$scopedSlots['selectionHeader']) ||
96
+ this.$slots['selectionHeader']
97
+ )
91
98
  return {
92
99
  bordered: this.bordered,
93
100
  size: this.size,
94
101
  scroll: this.scroll,
95
102
  rowHeight: this.rowHeight,
96
103
  headerHeight: this.headerHeight,
97
- height: this.height
104
+ height: this.height,
105
+ hasSlotSelectionHeader: hasSlotSelectionHeader
98
106
  }
99
107
  },
100
108
 
@@ -178,6 +186,7 @@ export default {
178
186
  this.tableCtx.layout.height = v.height
179
187
  this.tableCtx.layout.headerHeight = v.headerHeight
180
188
  this.tableCtx.layout.rowHeight = v.rowHeight
189
+ this.tableCtx.layout.hasSlotSelectionHeader = v.hasSlotSelectionHeader
181
190
  }
182
191
  },
183
192
 
@@ -208,7 +217,7 @@ export default {
208
217
  handler: function (v) {
209
218
  if (!v) return
210
219
  this.tableCtx.selection.selectedRowKeys = v.selectedRowKeys || []
211
- this.tableCtx.selection.hideSelectAll = v.hideSelectAll
220
+ this.tableCtx.selection.hideSelectAll = !!v.hideSelectAll
212
221
  this.tableCtx.selection.columnWidth = v.columnWidth
213
222
  this.tableCtx.selection.type = v.type
214
223
  this.tableCtx.selection.onChange = v.onChange
@@ -247,13 +256,11 @@ export default {
247
256
  },
248
257
 
249
258
  handleSelect: function (keys, meta) {
250
- var prevKeys = this.tableCtx.selection.selectedRowKeys
259
+ var prevKeys = this.tableCtx.selection.selectedRowKeys.slice()
251
260
  var display = this.tableCtx.data.display
252
261
  var rowKey = this.tableCtx.layout.rowKey
253
262
  var rs = this.rowSelection || {}
254
263
 
255
- this.tableCtx.selection.checkAll = keys.length === display.length && display.length > 0
256
-
257
264
  var selectedRows = display.filter(function (r) {
258
265
  return keys.indexOf(r[rowKey]) !== -1
259
266
  })
@@ -265,17 +272,15 @@ export default {
265
272
  if (meta.type === 'row') {
266
273
  if (rs.onSelect) rs.onSelect(meta.record, meta.selected, selectedRows, meta.nativeEvent)
267
274
  } else if (meta.type === 'all') {
268
- // changeRows = rows mới được check (chưa có trong prevKeys)
269
- var changeRows = display.filter(function (r) {
275
+ var addedRows = display.filter(function (r) {
270
276
  return prevKeys.indexOf(r[rowKey]) === -1
271
277
  })
272
- if (rs.onSelectAll) rs.onSelectAll(true, selectedRows, changeRows)
278
+ if (rs.onSelectAll) rs.onSelectAll(true, selectedRows, addedRows)
273
279
  } else if (meta.type === 'none') {
274
- // changeRows = rows bị uncheck (đang có trong prevKeys)
275
- changeRows = display.filter(function (r) {
280
+ var removedRows = display.filter(function (r) {
276
281
  return prevKeys.indexOf(r[rowKey]) !== -1
277
282
  })
278
- if (rs.onSelectAll) rs.onSelectAll(false, [], changeRows)
283
+ if (rs.onSelectAll) rs.onSelectAll(false, [], removedRows)
279
284
  if (rs.onSelectNone) rs.onSelectNone()
280
285
  }
281
286
  },
@@ -17,7 +17,11 @@
17
17
  <div ref="headerRef" class="ui-table__header-hold" style="overflow: hidden">
18
18
  <table :class="tableClass" :style="fixedTableStyle">
19
19
  <TableColGroup :is-header="true" />
20
- <TableHeader />
20
+ <TableHeader>
21
+ <template #selectionHeader>
22
+ <slot name="selectionHeader"></slot>
23
+ </template>
24
+ </TableHeader>
21
25
  </table>
22
26
  </div>
23
27
  <div ref="bodyRef" class="ui-table__body-hold" :style="bodyHoldStyle" @scroll="handleBodyScroll">
@@ -39,7 +43,11 @@
39
43
  <div v-else class="ui-table__content" ref="bodyRef" :style="contentStyle" @scroll="handleBodyScroll">
40
44
  <table :class="tableClass" :style="tableStyle">
41
45
  <TableColGroup />
42
- <TableHeader />
46
+ <TableHeader>
47
+ <template #selectionHeader>
48
+ <slot name="selectionHeader"></slot>
49
+ </template>
50
+ </TableHeader>
43
51
  <TableBody :measureColumnWidth="hasScrollY || hasScrollX || hasSticky" :is-empty="isEmpty">
44
52
  <template #bodyCell="slotData">
45
53
  <slot name="bodyCell" v-bind="slotData"></slot>
@@ -166,14 +174,22 @@ export default {
166
174
  style.overflowX = 'auto'
167
175
  }
168
176
  return style
177
+ },
178
+
179
+ hasSelectionHeader: function () {
180
+ const hasKeys = this.tableContext.selection.selectedRowKeys.length > 0
181
+ const hasSlot = this.tableContext.layout.hasSlotSelectionHeader
182
+
183
+ return hasKeys && hasSlot
169
184
  }
170
185
  },
171
186
  methods: {
172
187
  handleBodyScroll(e) {
173
188
  const { scrollLeft, scrollWidth, clientWidth } = e.currentTarget
174
- if (this.$refs.headerRef) {
189
+ if (this.$refs.headerRef && !this.hasSelectionHeader) {
175
190
  this.$refs.headerRef.scrollLeft = scrollLeft
176
191
  }
192
+ if (!this.hasScrollX) return
177
193
 
178
194
  this.isPingedLeft = scrollLeft > 0
179
195
  this.isPingedRight = scrollLeft < scrollWidth - clientWidth
@@ -183,6 +199,8 @@ export default {
183
199
  const { width } = getTargetScrollBarSize(this.$refs.bodyRef)
184
200
  this.tableContext.layout.scrollBarWidth = width == 'auto' ? '0px' : width
185
201
 
202
+ if (!this.hasScrollX) return
203
+
186
204
  const { scrollWidth, clientWidth, scrollLeft } = this.$refs.bodyRef
187
205
  this.isPingedLeft = scrollLeft > 0
188
206
  this.isPingedRight = scrollLeft < scrollWidth - clientWidth
@@ -200,6 +218,24 @@ export default {
200
218
  this.$nextTick(() => {
201
219
  this.updateScrollState()
202
220
  })
221
+ },
222
+ 'tableContext.layout.columnWidths': function () {
223
+ if (this.hasScrollY) {
224
+ clearTimeout(this._columnWidthsTimer)
225
+ this._columnWidthsTimer = setTimeout(() => {
226
+ this.$nextTick(() => {
227
+ this.updateScrollState()
228
+ })
229
+ })
230
+ }
231
+ },
232
+ hasSelectionHeader: function (val) {
233
+ if (!this.$refs.headerRef) return
234
+ if (!val && this.$refs.bodyRef) {
235
+ this.$refs.headerRef.scrollLeft = this.$refs.bodyRef.scrollLeft
236
+ } else {
237
+ this.$refs.headerRef.scrollLeft = 0
238
+ }
203
239
  }
204
240
  },
205
241
 
@@ -27,8 +27,9 @@ export function createTableContext() {
27
27
  containerWidth: 0,
28
28
  columnWidths: new Map(),
29
29
  rowHeight: 50,
30
- headerHeight: 36,
31
- height: 0
30
+ headerHeight: 40,
31
+ height: 0,
32
+ hasSlotSelectionHeader: false
32
33
  },
33
34
 
34
35
  sort: {
@@ -1,6 +1,10 @@
1
1
  <template>
2
2
  <thead class="ui-table__header">
3
- <TableHeaderRow />
3
+ <TableHeaderRow>
4
+ <template #selectionHeader>
5
+ <slot name="selectionHeader"></slot>
6
+ </template>
7
+ </TableHeaderRow>
4
8
  </thead>
5
9
  </template>
6
10
 
@@ -4,7 +4,6 @@
4
4
  'ui-table__header-cell',
5
5
  isSelectionCol && 'ui-table__header-cell--selection',
6
6
  isLastItem && 'ui-table__header-cell--last',
7
- isSelectionCol && !isHideSelectAll && 'ui-table__header-cell--selection',
8
7
  column.fixed && `ui-table__cell--fixed-${column.fixed}`,
9
8
  isLastFixedLeft && 'ui-table__cell--fixed-left__last',
10
9
  isFirstFixedRight && 'ui-table__cell--fixed-right__first'
@@ -12,7 +11,7 @@
12
11
  :style="cellStyle"
13
12
  >
14
13
  <div v-if="isSelectionCol && !isHideSelectAll" class="wrapper-option">
15
- <Checkbox :checked="tableContext.selection.checkAll" @change="onCheckAll" />
14
+ <Checkbox :checked="hasChecked" @change="onCheckAll" :iconType="isCheckAll ? 'default' : 'minus'" />
16
15
  </div>
17
16
 
18
17
  <span v-else :class="[column.ellipsis && 'ui-table__header-cell--ellipsis']">{{ column.title }}</span>
@@ -36,12 +35,20 @@ export default {
36
35
  offset: { type: [Number, String], default: 0 }
37
36
  },
38
37
  computed: {
39
- isHideSelectAll() {
40
- return this.tableContext.selection.hideSelectAll
38
+ isHideSelectAll: function () {
39
+ return !!this.tableContext.selection.hideSelectAll
41
40
  },
42
41
  isSelectionCol: function () {
43
42
  return this.column.type === SELECTION_COLUMN
44
43
  },
44
+ hasChecked: function () {
45
+ return this.tableContext.selection.selectedRowKeys.length > 0
46
+ },
47
+ isCheckAll: function () {
48
+ var keys = this.tableContext.selection.selectedRowKeys
49
+ var display = this.tableContext.data.display
50
+ return display.length > 0 && keys.length === display.length
51
+ },
45
52
  isLastFixedLeft: function () {
46
53
  if (this.column.fixed !== 'left') return false
47
54
  const cols = this.tableContext.columns.flat
@@ -73,16 +80,17 @@ export default {
73
80
  }
74
81
  },
75
82
  methods: {
76
- onCheckAll: function (checked) {
83
+ onCheckAll: function () {
77
84
  const key = this.tableContext.layout.rowKey
78
85
  const allKeys = this.tableContext.data.display.map(function (r) {
79
86
  return r[key]
80
87
  })
81
88
 
82
- this.tableContext.actions.select(checked ? allKeys : [], {
83
- type: checked ? 'all' : 'none',
84
- selected: checked
85
- })
89
+ if (this.isCheckAll) {
90
+ this.tableContext.actions.select([], { type: 'none', selected: false })
91
+ } else {
92
+ this.tableContext.actions.select(allKeys, { type: 'all', selected: true })
93
+ }
86
94
  }
87
95
  }
88
96
  }
@@ -5,24 +5,41 @@
5
5
  :class="['ui-table__header-row', customAttrs.class]"
6
6
  :style="rowStyle"
7
7
  >
8
- <TableHeaderCell
9
- v-for="(column, index) in tableContext.columns.flat"
10
- :key="column.key"
11
- :column="column"
12
- :is-last-item="index === tableContext.columns.flat.length - 1"
13
- :offset="column.fixed === 'left' ? leftStickyOffsets[column.key] : rightStickyOffsets[column.key]"
14
- >
15
- <template v-if="column.resizable && !isEmpty" #drag-handle>
16
- <ColumnDragHandle
17
- :column="column"
18
- :width="columnWidths.get(column.key)"
19
- :min-width="column.minWidth"
20
- :max-width="column.maxWidth"
21
- @resize="hanldeResize"
22
- />
23
- </template>
24
- </TableHeaderCell>
25
- <th v-if="hasScrollBar" class="ui-table__scrollbar-cell" aria-hidden="true" :style="styleFakeScrollbar" />
8
+ <template v-if="hasSelectionHeader">
9
+ <TableHeaderCell
10
+ v-for="column in systemColumns"
11
+ :key="column.key"
12
+ :column="column"
13
+ :is-last-item="false"
14
+ :offset="column.fixed === 'left' ? leftStickyOffsets[column.key] : rightStickyOffsets[column.key]"
15
+ />
16
+ <th :colspan="selectionHeaderColspan" class="ui-table__header-cell ui-table__header-cell--bulk">
17
+ <div :style="bulkHeaderStyle">
18
+ <slot name="selectionHeader"></slot>
19
+ </div>
20
+ </th>
21
+ <th v-if="hasScrollBar" class="ui-table__scrollbar-cell" aria-hidden="true" :style="styleFakeScrollbar" />
22
+ </template>
23
+ <template v-else>
24
+ <TableHeaderCell
25
+ v-for="(column, index) in tableContext.columns.flat"
26
+ :key="column.key"
27
+ :column="column"
28
+ :is-last-item="index === tableContext.columns.flat.length - 1"
29
+ :offset="column.fixed === 'left' ? leftStickyOffsets[column.key] : rightStickyOffsets[column.key]"
30
+ >
31
+ <template v-if="column.resizable && !isEmpty" #drag-handle>
32
+ <ColumnDragHandle
33
+ :column="column"
34
+ :width="columnWidths.get(column.key)"
35
+ :min-width="column.minWidth"
36
+ :max-width="column.maxWidth"
37
+ @resize="hanldeResize"
38
+ />
39
+ </template>
40
+ </TableHeaderCell>
41
+ <th v-if="hasScrollBar" class="ui-table__scrollbar-cell" aria-hidden="true" :style="styleFakeScrollbar" />
42
+ </template>
26
43
  </tr>
27
44
  </template>
28
45
 
@@ -30,6 +47,7 @@
30
47
  import TableHeaderCell from './TableHeaderCell.vue'
31
48
  import { splitProps } from '../../../utils/common.js'
32
49
  import ColumnDragHandle from '../ColumnDragHandle.vue'
50
+ import { SELECTION_COLUMN, DRAGGABLE_COLUMN } from '../constants.js'
33
51
 
34
52
  export default {
35
53
  name: 'TableHeaderRow',
@@ -95,6 +113,35 @@ export default {
95
113
 
96
114
  isEmpty: function () {
97
115
  return this.tableContext.data.display.length === 0
116
+ },
117
+
118
+ hasSelectionHeader: function () {
119
+ const hasKeys = this.tableContext.selection.selectedRowKeys.length > 0
120
+ const hasSlot = this.tableContext.layout.hasSlotSelectionHeader
121
+
122
+ return hasKeys && hasSlot
123
+ },
124
+
125
+ systemColumns: function () {
126
+ return this.tableContext.columns.flat.filter(function (col) {
127
+ return col.type === SELECTION_COLUMN || col.type === DRAGGABLE_COLUMN
128
+ })
129
+ },
130
+
131
+ selectionHeaderColspan: function () {
132
+ return this.tableContext.columns.flat.length - this.systemColumns.length
133
+ },
134
+
135
+ bulkHeaderStyle: function () {
136
+ var container = this.tableContext.layout.containerWidth
137
+ if (!container) return {}
138
+ var self = this
139
+ var systemWidth = 0
140
+ this.systemColumns.forEach(function (col) {
141
+ var measured = self.columnWidths.get(col.key)
142
+ systemWidth += measured != null ? measured : col.width || 0
143
+ })
144
+ return { width: container - systemWidth - 2 + 'px' } // Trừ đi border của table
98
145
  }
99
146
  },
100
147
 
@@ -1,8 +1,7 @@
1
1
  .ui-table__wrapper {
2
2
  overflow: hidden;
3
3
  border-radius: var(--rounded-2xl);
4
- border-inline-end: 1px solid var(--border-primary);
5
- border-inline-start: 1px solid var(--border-primary);
4
+ border: 1px solid var(--border-primary);
6
5
  }
7
6
 
8
7
  .ui-table__wrapper table {
@@ -13,19 +12,6 @@
13
12
  border-spacing: 0;
14
13
  }
15
14
 
16
- .ui-table__wrapper .ui-table__content {
17
- border-top: 1px solid var(--border-primary);
18
- border-bottom: 1px solid var(--border-primary);
19
- }
20
-
21
- .ui-table__wrapper .ui-table__header-hold {
22
- border-top: 1px solid var(--border-primary);
23
- }
24
-
25
- .ui-table__wrapper .ui-table__body-hold {
26
- border-bottom: 1px solid var(--border-primary);
27
- }
28
-
29
15
  /* Common betwenn Header and Body */
30
16
  .ui-table__wrapper ::v-deep .ui-table__header > tr > th,
31
17
  .ui-table__wrapper ::v-deep .ui-table__body > tr > td {
@@ -300,3 +286,18 @@
300
286
  border-bottom: 1px solid transparent;
301
287
  background-clip: content-box;
302
288
  }
289
+
290
+ .ui-table__wrapper ::v-deep .ui-table__header-cell--bulk.ui-table__header-cell--bulk {
291
+ padding: 0;
292
+ vertical-align: middle;
293
+ }
294
+
295
+ .ui-table__wrapper ::v-deep .ui-table__header-cell--bulk > div {
296
+ position: absolute;
297
+ top: 0;
298
+ left: 0;
299
+ right: 0;
300
+ bottom: 0;
301
+ display: flex;
302
+ align-items: center;
303
+ }