@globalbrain/sefirot 4.49.0 → 4.50.0
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.
|
@@ -146,7 +146,7 @@ export interface DecimalFieldData extends FieldDataBase {
|
|
|
146
146
|
|
|
147
147
|
export interface SelectFieldData extends FieldDataBase {
|
|
148
148
|
type: 'select'
|
|
149
|
-
displayAs: 'text' | 'state'
|
|
149
|
+
displayAs: 'text' | 'state' | 'pills'
|
|
150
150
|
inputAs: 'dropdown' | 'radio'
|
|
151
151
|
placeholderEn: string | null
|
|
152
152
|
placeholderJa: string | null
|
|
@@ -3,6 +3,8 @@ import IconPencilSimple from '~icons/ph/pencil-simple'
|
|
|
3
3
|
import { useElementBounding } from '@vueuse/core'
|
|
4
4
|
import { computed, nextTick, onUnmounted, ref, shallowRef, watch } from 'vue'
|
|
5
5
|
import SButton from '../../../components/SButton.vue'
|
|
6
|
+
import SPill, { type Mode as PillMode } from '../../../components/SPill.vue'
|
|
7
|
+
import SState, { type Mode as StateMode } from '../../../components/SState.vue'
|
|
6
8
|
import { useManualDropdownPosition } from '../../../composables/Dropdown'
|
|
7
9
|
import { useTrans } from '../../../composables/Lang'
|
|
8
10
|
import { useValidation } from '../../../composables/Validation'
|
|
@@ -54,21 +56,37 @@ onUnmounted(() => {
|
|
|
54
56
|
|
|
55
57
|
// Reuse the field's own table-cell rendering for the display value so the
|
|
56
58
|
// label mapping (e.g. select option labels) matches the read-only columns.
|
|
57
|
-
|
|
58
|
-
const displayValue = computed(() => {
|
|
59
|
+
const resolvedCell = computed<any>(() => {
|
|
59
60
|
try {
|
|
60
61
|
const cell = props.field.tableColumn().cell
|
|
61
|
-
|
|
62
|
-
if (resolved && (resolved.type === 'text' || resolved.type === 'number')) {
|
|
63
|
-
return resolved.value ?? ''
|
|
64
|
-
}
|
|
65
|
-
// `state` cells (e.g. a select with displayAs: 'state') carry the localized
|
|
66
|
-
// label, not the raw payload — show that rather than falling through.
|
|
67
|
-
if (resolved && resolved.type === 'state') {
|
|
68
|
-
return resolved.label ?? ''
|
|
69
|
-
}
|
|
62
|
+
return typeof cell === 'function' ? cell(props.value, props.record) : cell
|
|
70
63
|
} catch {
|
|
71
|
-
// Field types without a
|
|
64
|
+
// Field types without a usable cell fall through to the generic display.
|
|
65
|
+
return null
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
// A `pills` cell (e.g. a multi-select with displayAs: 'pills') renders as pills
|
|
70
|
+
// rather than text, mirroring the read-only `STableCellPills` column.
|
|
71
|
+
const displayPills = computed<{ label: string; color?: PillMode }[] | null>(() => {
|
|
72
|
+
const cell = resolvedCell.value
|
|
73
|
+
return cell && cell.type === 'pills' ? cell.pills : null
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// A `state` cell (e.g. a select with displayAs: 'state') renders as a status
|
|
77
|
+
// badge rather than its bare label, mirroring the read-only `STableCellState`
|
|
78
|
+
// column.
|
|
79
|
+
const displayState = computed<{ label: string; mode?: StateMode } | null>(() => {
|
|
80
|
+
const cell = resolvedCell.value
|
|
81
|
+
return cell && cell.type === 'state' ? { label: cell.label, mode: cell.mode } : null
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
// Falls back to a plain representation for non-text displays (pills and state
|
|
85
|
+
// cells are rendered as their own components in the template above).
|
|
86
|
+
const displayValue = computed(() => {
|
|
87
|
+
const resolved: any = resolvedCell.value
|
|
88
|
+
if (resolved && (resolved.type === 'text' || resolved.type === 'number')) {
|
|
89
|
+
return resolved.value ?? ''
|
|
72
90
|
}
|
|
73
91
|
|
|
74
92
|
const v = props.value
|
|
@@ -184,7 +202,22 @@ function isTextLikeInput(target: EventTarget | null): boolean {
|
|
|
184
202
|
|
|
185
203
|
<template>
|
|
186
204
|
<div ref="anchor" class="LensTableEditableCell" :class="{ editing }">
|
|
187
|
-
<
|
|
205
|
+
<div v-if="displayPills" class="pills">
|
|
206
|
+
<SPill
|
|
207
|
+
v-for="(pill, i) in displayPills"
|
|
208
|
+
:key="i"
|
|
209
|
+
size="mini"
|
|
210
|
+
:mode="pill.color"
|
|
211
|
+
:label="pill.label"
|
|
212
|
+
/>
|
|
213
|
+
</div>
|
|
214
|
+
<SState
|
|
215
|
+
v-else-if="displayState"
|
|
216
|
+
size="mini"
|
|
217
|
+
:mode="displayState.mode"
|
|
218
|
+
:label="displayState.label"
|
|
219
|
+
/>
|
|
220
|
+
<span v-else class="value">{{ displayValue }}</span>
|
|
188
221
|
<button
|
|
189
222
|
class="edit"
|
|
190
223
|
type="button"
|
|
@@ -233,6 +266,15 @@ function isTextLikeInput(target: EventTarget | null): boolean {
|
|
|
233
266
|
text-overflow: ellipsis;
|
|
234
267
|
}
|
|
235
268
|
|
|
269
|
+
.pills {
|
|
270
|
+
display: flex;
|
|
271
|
+
flex-wrap: nowrap;
|
|
272
|
+
align-items: center;
|
|
273
|
+
gap: 4px;
|
|
274
|
+
min-width: 0;
|
|
275
|
+
overflow: hidden;
|
|
276
|
+
}
|
|
277
|
+
|
|
236
278
|
.edit {
|
|
237
279
|
position: absolute;
|
|
238
280
|
top: 50%;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { xor } from 'lodash-es'
|
|
2
2
|
import { h } from 'vue'
|
|
3
|
+
import SDescPill from '../../../components/SDescPill.vue'
|
|
3
4
|
import SInputCheckboxes from '../../../components/SInputCheckboxes.vue'
|
|
4
5
|
import SInputDropdown from '../../../components/SInputDropdown.vue'
|
|
5
6
|
import SInputRadios from '../../../components/SInputRadios.vue'
|
|
@@ -38,6 +39,8 @@ export class SelectField extends Field<SelectFieldData> {
|
|
|
38
39
|
switch (this.data.displayAs) {
|
|
39
40
|
case 'state':
|
|
40
41
|
return this.tableCellState(v, _r)
|
|
42
|
+
case 'pills':
|
|
43
|
+
return this.tableCellPills(v, _r)
|
|
41
44
|
case 'text':
|
|
42
45
|
return this.tableCellText(v, _r)
|
|
43
46
|
default:
|
|
@@ -45,6 +48,18 @@ export class SelectField extends Field<SelectFieldData> {
|
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
|
|
51
|
+
protected tableCellPills(v: any, _r: any): TableCell {
|
|
52
|
+
v = Array.isArray(v) ? v : [v]
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
type: 'pills',
|
|
56
|
+
pills: this.optionsForValues(v).map((o) => ({
|
|
57
|
+
label: this.labelForOption(o),
|
|
58
|
+
color: o.mode
|
|
59
|
+
}))
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
48
63
|
protected tableCellText(v: any, _r: any): TableCell {
|
|
49
64
|
v = Array.isArray(v) ? v : [v]
|
|
50
65
|
|
|
@@ -95,6 +110,8 @@ export class SelectField extends Field<SelectFieldData> {
|
|
|
95
110
|
switch (this.data.displayAs) {
|
|
96
111
|
case 'state':
|
|
97
112
|
return this.renderDataListItemValueForState(value)
|
|
113
|
+
case 'pills':
|
|
114
|
+
return this.renderDataListItemValueForPills(value)
|
|
98
115
|
case 'text':
|
|
99
116
|
return this.renderDataListItemValueForText(value)
|
|
100
117
|
default:
|
|
@@ -103,6 +120,17 @@ export class SelectField extends Field<SelectFieldData> {
|
|
|
103
120
|
})
|
|
104
121
|
}
|
|
105
122
|
|
|
123
|
+
protected renderDataListItemValueForPills(value: any): any {
|
|
124
|
+
value = Array.isArray(value) ? value : [value]
|
|
125
|
+
|
|
126
|
+
return h(SDescPill, {
|
|
127
|
+
pill: this.optionsForValues(value).map((o) => ({
|
|
128
|
+
label: this.labelForOption(o),
|
|
129
|
+
mode: o.mode
|
|
130
|
+
}))
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
106
134
|
protected renderDataListItemValueForState(value: any): any {
|
|
107
135
|
if (this.data.multiple) {
|
|
108
136
|
throw new Error('Displaying select field as state with multiple option is not supported.')
|