frappe-ui 0.1.7 → 0.1.8

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": "frappe-ui",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "A set of components and utilities for rapid UI development",
5
5
  "main": "./src/index.js",
6
6
  "scripts": {
@@ -196,7 +196,7 @@ export default {
196
196
  this.dialogActions = actions.map((action) => {
197
197
  let _action = {
198
198
  ...action,
199
- loading: false,
199
+ loading: action.loading || false,
200
200
  _onClick: action.onClick,
201
201
  onClick: () => this.handleAction(_action),
202
202
  }
@@ -0,0 +1,31 @@
1
+ <template>
2
+ <svg
3
+ width="16"
4
+ height="17"
5
+ viewBox="0 0 16 17"
6
+ fill="none"
7
+ xmlns="http://www.w3.org/2000/svg"
8
+ >
9
+ <path
10
+ d="M2 4.5H14"
11
+ stroke="currentColor"
12
+ stroke-miterlimit="10"
13
+ stroke-linecap="round"
14
+ stroke-linejoin="round"
15
+ />
16
+ <path
17
+ d="M4 8.5H12"
18
+ stroke="currentColor"
19
+ stroke-miterlimit="10"
20
+ stroke-linecap="round"
21
+ stroke-linejoin="round"
22
+ />
23
+ <path
24
+ d="M6.5 12.5H9.5"
25
+ stroke="currentColor"
26
+ stroke-miterlimit="10"
27
+ stroke-linecap="round"
28
+ stroke-linejoin="round"
29
+ />
30
+ </svg>
31
+ </template>
@@ -0,0 +1,292 @@
1
+ <template>
2
+ <NestedPopover>
3
+ <template #target>
4
+ <Button label="Filter">
5
+ <template #prefix><FilterIcon class="h-4" /></template>
6
+ <template v-if="filters.size" #suffix>
7
+ <div
8
+ class="flex h-5 w-5 items-center justify-center rounded bg-gray-900 pt-[1px] text-2xs font-medium text-white"
9
+ >
10
+ {{ filters.size }}
11
+ </div>
12
+ </template>
13
+ </Button>
14
+ </template>
15
+ <template #body="{ close }">
16
+ <div class="my-2 rounded-lg border border-gray-100 bg-white shadow-xl">
17
+ <div class="min-w-[400px] p-2">
18
+ <div
19
+ v-if="filters.length"
20
+ v-for="(filter, i) in filters"
21
+ :key="i"
22
+ id="filter-list"
23
+ class="mb-3 flex items-center justify-between gap-2"
24
+ >
25
+ <div class="flex flex-1 items-center gap-2">
26
+ <div
27
+ class="w-13 flex-shrink-0 pl-2 text-end text-base text-gray-600"
28
+ >
29
+ {{ i == 0 ? 'Where' : 'And' }}
30
+ </div>
31
+ <div id="fieldname" class="!min-w-[140px] flex-1">
32
+ <Autocomplete
33
+ :value="filter.fieldname"
34
+ :options="fields"
35
+ @change="filter.fieldname = $event.value"
36
+ placeholder="Filter by..."
37
+ />
38
+ </div>
39
+ <div id="operator" class="!min-w-[140px] flex-shrink-0">
40
+ <FormControl
41
+ type="select"
42
+ :modelValue="filter.operator"
43
+ @update:modelValue="filter.operator = $event.value"
44
+ :options="getOperators(filter.field.fieldtype)"
45
+ placeholder="Operator"
46
+ />
47
+ </div>
48
+ <div id="value" class="!min-w-[140px] flex-1">
49
+ <SearchComplete
50
+ v-if="
51
+ typeLink.includes(filter.field.fieldtype) &&
52
+ ['=', '!='].includes(filter.operator)
53
+ "
54
+ :doctype="filter.field.options"
55
+ :value="filter.value"
56
+ @change="filter.value = $event.value"
57
+ placeholder="Value"
58
+ />
59
+ <component
60
+ v-else
61
+ :is="
62
+ getValueSelector(
63
+ filter.field.fieldtype,
64
+ filter.field.options
65
+ )
66
+ "
67
+ v-model="filter.value"
68
+ placeholder="Value"
69
+ />
70
+ </div>
71
+ </div>
72
+ <div class="flex-shrink-0">
73
+ <Button variant="ghost" icon="x" @click="removeFilter(i)" />
74
+ </div>
75
+ </div>
76
+ <div
77
+ v-else
78
+ class="mb-3 flex h-7 items-center px-3 text-sm text-gray-600"
79
+ >
80
+ Empty - Choose a field to filter by
81
+ </div>
82
+ <div class="flex items-center justify-between gap-2">
83
+ <Autocomplete
84
+ value=""
85
+ :options="fields"
86
+ @change="(field) => addFilter(field.value)"
87
+ placeholder="Filter by..."
88
+ >
89
+ <template #target="{ togglePopover }">
90
+ <Button
91
+ class="!text-gray-600"
92
+ variant="ghost"
93
+ @click="togglePopover()"
94
+ label="Add filter"
95
+ >
96
+ <template #prefix>
97
+ <FeatherIcon name="plus" class="h-4" />
98
+ </template>
99
+ </Button>
100
+ </template>
101
+ </Autocomplete>
102
+ <Button
103
+ v-if="filters.length"
104
+ class="!text-gray-600"
105
+ variant="ghost"
106
+ label="Clear all filter"
107
+ @click="filters = []"
108
+ />
109
+ </div>
110
+ </div>
111
+ </div>
112
+ </template>
113
+ </NestedPopover>
114
+ </template>
115
+
116
+ <script setup>
117
+ import { Autocomplete, FeatherIcon, FormControl } from 'frappe-ui'
118
+ import { computed, h } from 'vue'
119
+ import FilterIcon from './FilterIcon.vue'
120
+ import NestedPopover from './NestedPopover.vue'
121
+ import SearchComplete from './SearchComplete.vue'
122
+
123
+ const typeCheck = ['Check']
124
+ const typeLink = ['Link']
125
+ const typeNumber = ['Float', 'Int']
126
+ const typeSelect = ['Select']
127
+ const typeString = [
128
+ 'Data',
129
+ 'Long Text',
130
+ 'Small Text',
131
+ 'Text Editor',
132
+ 'Text',
133
+ 'JSON',
134
+ 'Code',
135
+ ]
136
+
137
+ const emits = defineEmits(['update:modelValue'])
138
+ const props = defineProps({
139
+ modelValue: {
140
+ type: Object,
141
+ default: () => ({}),
142
+ },
143
+ docfields: {
144
+ type: Array,
145
+ default: () => [],
146
+ },
147
+ })
148
+
149
+ const fields = computed(() => {
150
+ const fields = props.docfields
151
+ .filter((field) => {
152
+ return (
153
+ !field.is_virtual &&
154
+ (typeCheck.includes(field.fieldtype) ||
155
+ typeLink.includes(field.fieldtype) ||
156
+ typeNumber.includes(field.fieldtype) ||
157
+ typeSelect.includes(field.fieldtype) ||
158
+ typeString.includes(field.fieldtype))
159
+ )
160
+ })
161
+ .map((field) => {
162
+ return {
163
+ label: field.label,
164
+ value: field.fieldname,
165
+ description: field.fieldtype,
166
+ ...field,
167
+ }
168
+ })
169
+ return fields
170
+ })
171
+
172
+ const filters = computed({
173
+ get: () => makeFiltersList(props.modelValue),
174
+ set: (value) => emits('update:modelValue', makeFiltersDict(value)),
175
+ })
176
+
177
+ function makeFiltersList(filtersDict) {
178
+ return Object.entries(filtersDict).map(([fieldname, [operator, value]]) => {
179
+ const field = getField(fieldname)
180
+ return {
181
+ fieldname,
182
+ operator,
183
+ value,
184
+ field,
185
+ }
186
+ })
187
+ }
188
+
189
+ function getField(fieldname) {
190
+ return fields.value.find((f) => f.fieldname === fieldname)
191
+ }
192
+
193
+ function makeFiltersDict(filtersList) {
194
+ return filtersList.reduce((acc, filter) => {
195
+ const { fieldname, operator, value } = filter
196
+ acc[fieldname] = [operator, value]
197
+ return acc
198
+ }, {})
199
+ }
200
+
201
+ function getOperators(fieldtype) {
202
+ let options = []
203
+ if (typeString.includes(fieldtype) || typeLink.includes(fieldtype)) {
204
+ options.push(
205
+ ...[
206
+ { label: 'Equals', value: '=' },
207
+ { label: 'Not Equals', value: '!=' },
208
+ { label: 'Like', value: 'like' },
209
+ { label: 'Not Like', value: 'not like' },
210
+ ]
211
+ )
212
+ }
213
+ if (typeNumber.includes(fieldtype)) {
214
+ options.push(
215
+ ...[
216
+ { label: '<', value: '<' },
217
+ { label: '>', value: '>' },
218
+ { label: '<=', value: '<=' },
219
+ { label: '>=', value: '>=' },
220
+ { label: 'Equals', value: '=' },
221
+ { label: 'Not Equals', value: '!=' },
222
+ ]
223
+ )
224
+ }
225
+ if (typeSelect.includes(fieldtype)) {
226
+ options.push(
227
+ ...[
228
+ { label: 'Equals', value: '=' },
229
+ { label: 'Not Equals', value: '!=' },
230
+ ]
231
+ )
232
+ }
233
+ if (typeCheck.includes(fieldtype)) {
234
+ options.push(...[{ label: 'Equals', value: '=' }])
235
+ }
236
+ return options
237
+ }
238
+
239
+ function getDefaultOperator(fieldtype) {
240
+ if (
241
+ typeSelect.includes(fieldtype) ||
242
+ typeLink.includes(fieldtype) ||
243
+ typeCheck.includes(fieldtype) ||
244
+ typeNumber.includes(fieldtype)
245
+ ) {
246
+ return '='
247
+ }
248
+ return 'like'
249
+ }
250
+
251
+ function getValueSelector(fieldtype, options) {
252
+ if (typeSelect.includes(fieldtype) || typeCheck.includes(fieldtype)) {
253
+ const _options =
254
+ fieldtype == 'Check' ? ['Yes', 'No'] : getSelectOptions(options)
255
+ return h(FormControl, {
256
+ type: 'select',
257
+ options: _options,
258
+ })
259
+ } else {
260
+ return h(FormControl, { type: 'text' })
261
+ }
262
+ }
263
+
264
+ function getDefaultValue(field) {
265
+ if (typeSelect.includes(field.fieldtype)) {
266
+ return getSelectOptions(field.options)[0]
267
+ }
268
+ if (typeCheck.includes(field.fieldtype)) {
269
+ return 'Yes'
270
+ }
271
+ return ''
272
+ }
273
+
274
+ function getSelectOptions(options) {
275
+ return options.split('\n')
276
+ }
277
+
278
+ function addFilter(fieldname) {
279
+ const field = getField(fieldname)
280
+ const filter = {
281
+ fieldname,
282
+ operator: getDefaultOperator(field.fieldtype),
283
+ value: getDefaultValue(field),
284
+ field,
285
+ }
286
+ filters.value = [...filters.value, filter]
287
+ }
288
+
289
+ function removeFilter(index) {
290
+ filters.value = filters.value.filter((_, i) => i !== index)
291
+ }
292
+ </script>
@@ -0,0 +1,60 @@
1
+ <template>
2
+ <Popover v-slot="{ open }">
3
+ <PopoverButton
4
+ as="div"
5
+ ref="reference"
6
+ @click="updatePosition"
7
+ @focusin="updatePosition"
8
+ @keydown="updatePosition"
9
+ v-slot="{ open }"
10
+ >
11
+ <slot name="target" v-bind="{ open }" />
12
+ </PopoverButton>
13
+ <div v-show="open">
14
+ <PopoverPanel
15
+ v-slot="{ open, close }"
16
+ ref="popover"
17
+ static
18
+ class="z-[100]"
19
+ >
20
+ <slot name="body" v-bind="{ open, close }" />
21
+ </PopoverPanel>
22
+ </div>
23
+ </Popover>
24
+ </template>
25
+
26
+ <script setup>
27
+ import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
28
+ import { createPopper } from '@popperjs/core'
29
+ import { nextTick, ref, onBeforeUnmount } from 'vue'
30
+
31
+ const props = defineProps({
32
+ placement: {
33
+ type: String,
34
+ default: 'bottom-start',
35
+ },
36
+ })
37
+
38
+ const reference = ref(null)
39
+ const popover = ref(null)
40
+
41
+ let popper = ref(null)
42
+
43
+ function setupPopper() {
44
+ if (!popper.value) {
45
+ popper.value = createPopper(reference.value.el, popover.value.el, {
46
+ placement: props.placement,
47
+ })
48
+ } else {
49
+ popper.value.update()
50
+ }
51
+ }
52
+
53
+ function updatePosition() {
54
+ nextTick(() => setupPopper())
55
+ }
56
+
57
+ onBeforeUnmount(() => {
58
+ popper.value?.destroy()
59
+ })
60
+ </script>
@@ -0,0 +1,85 @@
1
+ <template>
2
+ <Autocomplete
3
+ placeholder="Select an option"
4
+ :options="options"
5
+ :value="selection"
6
+ @update:query="(q) => onUpdateQuery(q)"
7
+ @change="(v) => (selection = v)"
8
+ />
9
+ </template>
10
+
11
+ <script setup>
12
+ import { Autocomplete, createListResource } from 'frappe-ui'
13
+ import { computed, ref, watch } from 'vue'
14
+
15
+ const props = defineProps({
16
+ value: {
17
+ type: String,
18
+ required: false,
19
+ default: '',
20
+ },
21
+ doctype: {
22
+ type: String,
23
+ required: true,
24
+ },
25
+ searchField: {
26
+ type: String,
27
+ required: false,
28
+ default: 'name',
29
+ },
30
+ labelField: {
31
+ type: String,
32
+ required: false,
33
+ default: 'name',
34
+ },
35
+ valueField: {
36
+ type: String,
37
+ required: false,
38
+ default: 'name',
39
+ },
40
+ pageLength: {
41
+ type: Number,
42
+ required: false,
43
+ default: 10,
44
+ },
45
+ })
46
+
47
+ watch(
48
+ () => props.doctype,
49
+ (value) => {
50
+ r.doctype = value
51
+ r.reload()
52
+ }
53
+ )
54
+
55
+ const r = createListResource({
56
+ doctype: props.doctype,
57
+ pageLength: props.pageLength,
58
+ cache: ['link_doctype', props.doctype],
59
+ auto: true,
60
+ fields: [props.labelField, props.searchField, props.valueField],
61
+ onSuccess: () => {
62
+ selection.value = props.value
63
+ ? options.value.find((o) => o.value === props.value)
64
+ : null
65
+ },
66
+ })
67
+ const options = computed(
68
+ () =>
69
+ r.data?.map((result) => ({
70
+ label: result[props.labelField],
71
+ value: result[props.valueField],
72
+ })) || []
73
+ )
74
+ const selection = ref(null)
75
+
76
+ function onUpdateQuery(query) {
77
+ r.update({
78
+ filters: {
79
+ [props.searchField]: ['like', `%${query}%`],
80
+ },
81
+ })
82
+
83
+ r.reload()
84
+ }
85
+ </script>
@@ -1,9 +1,12 @@
1
1
  <template>
2
2
  <div
3
- class="mx-5 mb-2 grid items-center space-x-4 rounded bg-gray-100 p-2"
4
- :style="{ gridTemplateColumns: getGridTemplateColumns(columns) }"
3
+ class="mb-2 grid items-center space-x-4 rounded bg-gray-100 p-2"
4
+ :style="{
5
+ gridTemplateColumns: getGridTemplateColumns(columns, options.selectable),
6
+ }"
5
7
  >
6
8
  <Checkbox
9
+ v-if="options.selectable"
7
10
  class="cursor-pointer duration-300"
8
11
  :modelValue="allRowsSelected"
9
12
  @click.stop="toggleAllRows"
@@ -12,7 +15,7 @@
12
15
  <ListHeaderItem
13
16
  v-for="column in columns"
14
17
  :key="column.key"
15
- :column="column"
18
+ :item="column"
16
19
  />
17
20
  </slot>
18
21
  </div>
@@ -24,5 +27,5 @@ import ListHeaderItem from './ListHeaderItem.vue'
24
27
  import { getGridTemplateColumns } from './utils'
25
28
  import { inject } from 'vue'
26
29
 
27
- const { columns, allRowsSelected, toggleAllRows } = inject('list')
30
+ const { columns, options, allRowsSelected, toggleAllRows } = inject('list')
28
31
  </script>
@@ -1,20 +1,20 @@
1
1
  <template>
2
2
  <div
3
3
  class="flex items-center space-x-2 text-base text-gray-600"
4
- :class="alignmentMap[column.align]"
4
+ :class="alignmentMap[item.align]"
5
5
  >
6
- <slot name="prefix" v-bind="{ column }" />
6
+ <slot name="prefix" v-bind="{ item }" />
7
7
  <div>
8
- {{ column.label }}
8
+ {{ item.label }}
9
9
  </div>
10
- <slot name="suffix" v-bind="{ column }" />
10
+ <slot name="suffix" v-bind="{ item }" />
11
11
  </div>
12
12
  </template>
13
13
 
14
14
  <script setup>
15
15
  import { alignmentMap } from './utils'
16
16
  const props = defineProps({
17
- column: {
17
+ item: {
18
18
  type: Object,
19
19
  required: true,
20
20
  },
@@ -1,11 +1,15 @@
1
1
  <template>
2
2
  <component
3
- :is="row.route ? 'router-link' : 'div'"
4
- class="mx-5 flex cursor-pointer flex-col transition-all duration-300 ease-in-out"
5
- v-bind="row.route ? { to: row.route } : { onClick: row.onClick }"
3
+ :is="options.getRowRoute ? 'router-link' : 'div'"
4
+ class="flex cursor-pointer flex-col transition-all duration-300 ease-in-out"
5
+ v-bind="
6
+ options.getRowRoute
7
+ ? { to: options.getRowRoute(row) }
8
+ : { onClick: () => options.onRowClick(row) }
9
+ "
6
10
  >
7
11
  <component
8
- :is="row.route ? 'template' : 'button'"
12
+ :is="options.getRowRoute ? 'template' : 'button'"
9
13
  class="[all:unset] hover:[all:unset]"
10
14
  >
11
15
  <div
@@ -15,9 +19,15 @@
15
19
  ? 'bg-gray-100 hover:bg-gray-200'
16
20
  : 'hover:bg-gray-50'
17
21
  "
18
- :style="{ gridTemplateColumns: getGridTemplateColumns(columns) }"
22
+ :style="{
23
+ gridTemplateColumns: getGridTemplateColumns(
24
+ columns,
25
+ options.selectable
26
+ ),
27
+ }"
19
28
  >
20
29
  <Checkbox
30
+ v-if="options.selectable"
21
31
  :modelValue="selections.has(row[rowKey])"
22
32
  @click.stop="toggleRow(row[rowKey])"
23
33
  class="cursor-pointer duration-300"
@@ -27,19 +37,16 @@
27
37
  :key="column.key"
28
38
  :class="alignmentMap[column.align]"
29
39
  >
30
- <slot v-bind="{ column, item: _row(row)[column.key] }">
40
+ <slot v-bind="{ column, item: row[column.key] }">
31
41
  <ListRowItem
32
- :item="_row(row)[column.key]"
42
+ :item="row[column.key]"
33
43
  :type="column.type"
34
44
  :align="column.align"
35
45
  />
36
46
  </slot>
37
47
  </div>
38
48
  </div>
39
- <div
40
- v-if="idx < rows.length - 1"
41
- class="mx-2 h-px border-t border-gray-200"
42
- />
49
+ <div v-if="!isLastRow" class="mx-2 h-px border-t border-gray-200" />
43
50
  </component>
44
51
  </component>
45
52
  </template>
@@ -48,25 +55,18 @@
48
55
  import Checkbox from '../Checkbox.vue'
49
56
  import ListRowItem from './ListRowItem.vue'
50
57
  import { alignmentMap, getGridTemplateColumns } from './utils'
51
- import { inject } from 'vue'
58
+ import { computed, inject } from 'vue'
52
59
 
53
60
  const props = defineProps({
54
61
  row: {
55
62
  type: Object,
56
63
  required: true,
57
64
  },
58
- idx: {
59
- type: Number,
60
- required: true,
61
- },
62
65
  })
63
66
 
64
- function _row(row) {
65
- if (row.row && typeof row.row === 'object' && (row.onClick || row.route)) {
66
- return row.row
67
- }
68
- return row
69
- }
67
+ const isLastRow = computed(() => {
68
+ return rows[rows.length - 1][rowKey] === props.row[rowKey]
69
+ })
70
70
 
71
- const { rows, columns, rowKey, selections, toggleRow } = inject('list')
71
+ const { rows, columns, rowKey, options, selections, toggleRow } = inject('list')
72
72
  </script>
@@ -1,6 +1,7 @@
1
1
  <template>
2
- <Tooltip
3
- :text="label"
2
+ <component
3
+ :is="options.showTooltip ? Tooltip : 'div'"
4
+ v-bind="options.showTooltip ? { text: label } : {}"
4
5
  class="flex items-center space-x-2"
5
6
  :class="alignmentMap[align]"
6
7
  >
@@ -11,12 +12,12 @@
11
12
  </div>
12
13
  </slot>
13
14
  <slot name="suffix" />
14
- </Tooltip>
15
+ </component>
15
16
  </template>
16
17
  <script setup>
17
18
  import { alignmentMap } from './utils'
18
19
  import Tooltip from '../Tooltip.vue'
19
- import { computed } from 'vue'
20
+ import { computed, inject } from 'vue'
20
21
 
21
22
  const props = defineProps({
22
23
  item: {
@@ -39,4 +40,6 @@ function getValue(value) {
39
40
  }
40
41
  return { label: value }
41
42
  }
43
+
44
+ const { options } = inject('list')
42
45
  </script>
@@ -1,12 +1,7 @@
1
1
  <template>
2
2
  <div class="h-full overflow-y-auto">
3
3
  <slot>
4
- <ListRow
5
- v-for="(row, i) in rows"
6
- :key="row[rowKey]"
7
- :row="row"
8
- :idx="i"
9
- />
4
+ <ListRow v-for="(row, i) in rows" :key="row[rowKey]" :row="row" />
10
5
  </slot>
11
6
  </div>
12
7
  </template>
@@ -78,5 +78,5 @@ let selectedText = computed(() => {
78
78
  return `${selections.size} ${title} selected`
79
79
  })
80
80
 
81
- const { list, selections, allRowsSelected, toggleAllRows } = inject('list')
81
+ const { selections, allRowsSelected, toggleAllRows } = inject('list')
82
82
  </script>
@@ -1,13 +1,13 @@
1
1
  <template>
2
2
  <div class="relative flex w-full flex-1 flex-col overflow-x-auto">
3
3
  <div
4
- class="mt-3 flex w-max min-w-full flex-col overflow-y-hidden"
4
+ class="flex w-max min-w-full flex-col overflow-y-hidden"
5
5
  :class="$attrs.class"
6
6
  >
7
7
  <slot>
8
8
  <ListHeader />
9
9
  <ListRows />
10
- <ListSelectBanner />
10
+ <ListSelectBanner v-if="_options.selectable" />
11
11
  </slot>
12
12
  </div>
13
13
  </div>
@@ -35,10 +35,32 @@ const props = defineProps({
35
35
  type: String,
36
36
  required: true,
37
37
  },
38
+ options: {
39
+ type: Object,
40
+ default: {
41
+ getRowRoute: null,
42
+ onRowClick: null,
43
+ showTooltip: true,
44
+ selectable: true,
45
+ },
46
+ },
38
47
  })
39
48
 
40
49
  let selections = reactive(new Set())
41
50
 
51
+ let _options = computed(() => {
52
+ function defaultTrue(value) {
53
+ return value === undefined ? true : value
54
+ }
55
+
56
+ return {
57
+ getRowRoute: props.options.getRowRoute || null,
58
+ onRowClick: props.options.onRowClick || null,
59
+ showTooltip: defaultTrue(props.options.showTooltip),
60
+ selectable: defaultTrue(props.options.selectable),
61
+ }
62
+ })
63
+
42
64
  const allRowsSelected = computed(() => {
43
65
  if (!props.rows.length) return false
44
66
  return selections.size === props.rows.length
@@ -62,6 +84,7 @@ provide('list', {
62
84
  rowKey: props.rowKey,
63
85
  rows: props.rows,
64
86
  columns: props.columns,
87
+ options: _options.value,
65
88
  selections,
66
89
  allRowsSelected,
67
90
  toggleRow,
@@ -1,16 +1,15 @@
1
- export function getGridTemplateColumns(columns) {
2
- return (
3
- '14px ' +
4
- columns
5
- .map((col) => {
6
- let width = col.width || 1
7
- if (typeof width === 'number') {
8
- return width + 'fr'
9
- }
10
- return width
11
- })
12
- .join(' ')
13
- )
1
+ export function getGridTemplateColumns(columns, withCheckbox = true) {
2
+ let checkBoxWidth = withCheckbox ? '14px ' : ''
3
+ let columnsWidth = columns
4
+ .map((col) => {
5
+ let width = col.width || 1
6
+ if (typeof width === 'number') {
7
+ return width + 'fr'
8
+ }
9
+ return width
10
+ })
11
+ .join(' ')
12
+ return checkBoxWidth + columnsWidth
14
13
  }
15
14
 
16
15
  export const alignmentMap = {
package/src/index.js CHANGED
@@ -50,6 +50,7 @@ export { toast, Toasts } from './components/toast.js'
50
50
  export { default as Tooltip } from './components/Tooltip.vue'
51
51
  export { default as CommandPalette } from './components/CommandPalette/CommandPalette.vue'
52
52
  export { default as CommandPaletteItem } from './components/CommandPalette/CommandPaletteItem.vue'
53
+ export { default as ListFilter } from './components/ListFilter/ListFilter.vue'
53
54
 
54
55
  // directives
55
56
  export { default as onOutsideClickDirective } from './directives/onOutsideClick.js'