@globalbrain/sefirot 4.56.0 → 4.58.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.
- package/lib/blocks/lens/components/LensFormFilterCondition.vue +2 -1
- package/lib/blocks/lens/components/LensFormFilterGroup.vue +1 -1
- package/lib/blocks/lens/components/{LensAvatarInput.vue → LensInputAvatar.vue} +5 -3
- package/lib/blocks/lens/components/LensInputDropdown.vue +102 -0
- package/lib/blocks/lens/components/LensSheetAvatarField.vue +2 -2
- package/lib/blocks/lens/components/LensTableEditableCell.vue +5 -4
- package/lib/blocks/lens/fields/AvatarField.ts +2 -2
- package/lib/blocks/lens/fields/Field.ts +10 -6
- package/lib/blocks/lens/filter-inputs/SelectFilterInput.ts +2 -1
- package/lib/components/SButton.vue +2 -10
- package/lib/components/SControlInputSearch.vue +6 -3
- package/lib/components/SDescPill.vue +3 -2
- package/lib/components/SDescState.vue +3 -2
- package/lib/components/SInputAddon.vue +4 -2
- package/lib/components/SInputAsyncDropdown.vue +761 -0
- package/lib/components/SInputBase.vue +1 -3
- package/lib/components/SInputCheckbox.vue +10 -18
- package/lib/components/SInputCheckboxes.vue +16 -33
- package/lib/components/SInputDate.vue +10 -20
- package/lib/components/SInputDropdown.vue +22 -43
- package/lib/components/SInputDropdownItem.vue +10 -27
- package/lib/components/SInputFile.vue +10 -18
- package/lib/components/SInputFileUpload.vue +12 -34
- package/lib/components/SInputFileUploadItem.vue +7 -21
- package/lib/components/SInputHMS.vue +6 -7
- package/lib/components/SInputImage.vue +11 -16
- package/lib/components/SInputNumber.vue +1 -3
- package/lib/components/SInputRadio.vue +9 -19
- package/lib/components/SInputRadios.vue +18 -40
- package/lib/components/SInputSegments.vue +15 -26
- package/lib/components/SInputSegmentsOption.vue +7 -7
- package/lib/components/SInputSelect.vue +14 -19
- package/lib/components/SInputSwitch.vue +10 -19
- package/lib/components/SInputSwitches.vue +16 -30
- package/lib/components/SInputText.vue +1 -4
- package/lib/components/SInputTextarea.vue +0 -2
- package/lib/components/SInputYMD.vue +6 -7
- package/lib/components/SPagination.vue +1 -1
- package/lib/components/SPill.vue +2 -2
- package/lib/components/SState.vue +2 -2
- package/lib/components/STableCellPill.vue +3 -2
- package/lib/components/STableCellState.vue +3 -2
- package/lib/composables/Table.ts +8 -15
- package/lib/support/Color.ts +4 -0
- package/lib/support/InputBase.ts +5 -0
- package/lib/support/InputDropdown.ts +15 -0
- package/lib/support/InputFileUpload.ts +22 -0
- package/lib/support/InputOption.ts +8 -0
- package/lib/support/InputText.ts +6 -0
- package/package.json +1 -1
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
import IconTrash from '~icons/ph/trash'
|
|
3
3
|
import { type Ref, computed, markRaw, ref, watch } from 'vue'
|
|
4
4
|
import SButton from '../../../components/SButton.vue'
|
|
5
|
-
import SInputDropdown
|
|
5
|
+
import SInputDropdown from '../../../components/SInputDropdown.vue'
|
|
6
6
|
import SSpinner from '../../../components/SSpinner.vue'
|
|
7
7
|
import { useTrans } from '../../../composables/Lang'
|
|
8
8
|
import { useValidation } from '../../../composables/Validation'
|
|
9
|
+
import { type Option } from '../../../support/InputDropdown'
|
|
9
10
|
import { required } from '../../../validation/rules'
|
|
10
11
|
import { type FieldData } from '../FieldData'
|
|
11
12
|
import { type FilterOperator, FilterOperatorLabelDict } from '../FilterOperator'
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import IconPlusCircle from '~icons/ph/plus-circle'
|
|
3
3
|
import IconTrash from '~icons/ph/trash'
|
|
4
4
|
import SButton from '../../../components/SButton.vue'
|
|
5
|
-
import { type Option } from '../../../components/SInputDropdown.vue'
|
|
6
5
|
import SInputSegments from '../../../components/SInputSegments.vue'
|
|
7
6
|
import { useTrans } from '../../../composables/Lang'
|
|
7
|
+
import { type Option } from '../../../support/InputDropdown'
|
|
8
8
|
import { type FieldData } from '../FieldData'
|
|
9
9
|
import LensFormFilterCondition, { type FilterCondition } from './LensFormFilterCondition.vue'
|
|
10
10
|
|
|
@@ -3,7 +3,7 @@ import SInputImage, { type ImageType, type Size } from '../../../components/SInp
|
|
|
3
3
|
import { useTrans } from '../../../composables/Lang'
|
|
4
4
|
import { type Validatable } from '../../../composables/Validation'
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
export interface Props {
|
|
7
7
|
label?: string
|
|
8
8
|
help?: string
|
|
9
9
|
accept?: string
|
|
@@ -11,7 +11,9 @@ withDefaults(defineProps<{
|
|
|
11
11
|
size?: Size
|
|
12
12
|
disabled?: boolean
|
|
13
13
|
validation?: Validatable
|
|
14
|
-
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
withDefaults(defineProps<Props>(), {
|
|
15
17
|
imageType: 'circle',
|
|
16
18
|
size: 'small'
|
|
17
19
|
})
|
|
@@ -30,7 +32,7 @@ const { t } = useTrans({
|
|
|
30
32
|
<template>
|
|
31
33
|
<SInputImage
|
|
32
34
|
v-model="model"
|
|
33
|
-
class="
|
|
35
|
+
class="LensInputAvatar"
|
|
34
36
|
:label
|
|
35
37
|
:help
|
|
36
38
|
:accept
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<script setup lang="ts" generic="T, Multiple extends boolean = false">
|
|
2
|
+
import SInputAsyncDropdown from '../../../components/SInputAsyncDropdown.vue'
|
|
3
|
+
import { useMutation } from '../../../composables/Api'
|
|
4
|
+
import { useLang } from '../../../composables/Lang'
|
|
5
|
+
import { type Option } from '../../../support/InputDropdown'
|
|
6
|
+
import { type LensQuerySettings, type LensQuerySort } from '../LensQuery'
|
|
7
|
+
import { isAuthError } from '../validation/ServerErrors'
|
|
8
|
+
|
|
9
|
+
// A lens-backed search-select that works entirely in the consumer's own model
|
|
10
|
+
// type. It searches the server as the user types (via SInputAsyncDropdown): each
|
|
11
|
+
// result row is turned into a model by `toModel`, and each model is rendered as
|
|
12
|
+
// its dropdown/chip option by `toOption`. The v-model is those models — a `T[]`
|
|
13
|
+
// when `multiple`, otherwise `T | null`.
|
|
14
|
+
//
|
|
15
|
+
// Remaining selection UI props (nullable, disabled, placeholder, size, label,
|
|
16
|
+
// validation, position, closeOnSelect, debounce, …) fall through as attributes.
|
|
17
|
+
export interface Props<T = any, Multiple extends boolean = false> {
|
|
18
|
+
// The lens search endpoint (e.g. `/api/admin/lens/search`).
|
|
19
|
+
endpoint: string
|
|
20
|
+
|
|
21
|
+
// The entity to search.
|
|
22
|
+
entity: string
|
|
23
|
+
|
|
24
|
+
// Columns to fetch for each row, passed to `toModel`.
|
|
25
|
+
select: string[]
|
|
26
|
+
|
|
27
|
+
// Columns matched against the typed query — OR-ed together with `contains`. An
|
|
28
|
+
// empty query fetches the initial (unfiltered) page. Named to match
|
|
29
|
+
// LensCatalog's `queryKeys`.
|
|
30
|
+
queryKeys: string[]
|
|
31
|
+
|
|
32
|
+
// Sort applied to the results (e.g. `[['name', 'asc']]`). Re-read on each fetch,
|
|
33
|
+
// so a locale-dependent sort stays current.
|
|
34
|
+
sort?: LensQuerySort[]
|
|
35
|
+
|
|
36
|
+
// Page size of each search. The server enforces its own perPage ceiling.
|
|
37
|
+
perPage?: number
|
|
38
|
+
|
|
39
|
+
// Per-request settings (e.g. language). Usually unneeded — the server
|
|
40
|
+
// negotiates language from the request headers.
|
|
41
|
+
settings?: LensQuerySettings
|
|
42
|
+
|
|
43
|
+
// Whether multiple models can be selected. Typed `boolean & Multiple` (equal to
|
|
44
|
+
// `Multiple`) so Vue keeps runtime Boolean casting for a bare `multiple` while
|
|
45
|
+
// `Multiple` still drives the arity-conditional model type. See SInputAsyncDropdown.
|
|
46
|
+
multiple?: boolean & Multiple
|
|
47
|
+
|
|
48
|
+
// Build a model from a raw result row.
|
|
49
|
+
toModel: (row: Record<string, any>) => T
|
|
50
|
+
|
|
51
|
+
// Render a model as its dropdown/chip option (`value` is the identity).
|
|
52
|
+
toOption: (item: T) => Option
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const props = defineProps<Props<T, Multiple>>()
|
|
56
|
+
|
|
57
|
+
// The selected model(s): a `T[]` when `multiple`, otherwise `T | null`.
|
|
58
|
+
const model = defineModel<Multiple extends true ? T[] : T | null>({ required: true })
|
|
59
|
+
|
|
60
|
+
// Don't let lens-config attributes leak onto the child; only the explicitly
|
|
61
|
+
// forwarded selection props (via `$attrs`) should.
|
|
62
|
+
defineOptions({ inheritAttrs: false })
|
|
63
|
+
|
|
64
|
+
// Default the request language to the active app language (like LensCatalog), so
|
|
65
|
+
// server-localized fields come back in the right locale even when HttpConfig /
|
|
66
|
+
// request headers differ; explicit `settings` (incl. `lang`) overrides it.
|
|
67
|
+
const lang = useLang()
|
|
68
|
+
|
|
69
|
+
const { execute } = useMutation((http, query: string) =>
|
|
70
|
+
http.post<{ data: Record<string, any>[] }>(props.endpoint, {
|
|
71
|
+
entity: props.entity,
|
|
72
|
+
select: props.select,
|
|
73
|
+
filters: query
|
|
74
|
+
? [['$or', props.queryKeys.map((key) => [key, 'contains', query])]]
|
|
75
|
+
: [],
|
|
76
|
+
sort: props.sort ?? [],
|
|
77
|
+
page: 1,
|
|
78
|
+
perPage: props.perPage ?? 25,
|
|
79
|
+
settings: { lang, ...props.settings }
|
|
80
|
+
})
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
// Read every config field through `props` at call time so a reactive change (e.g.
|
|
84
|
+
// a locale-dependent sort or label) takes effect on the next search.
|
|
85
|
+
async function fetch(query: string): Promise<T[]> {
|
|
86
|
+
const res = await execute(query)
|
|
87
|
+
return res.data.map(props.toModel)
|
|
88
|
+
}
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<template>
|
|
92
|
+
<SInputAsyncDropdown
|
|
93
|
+
v-model="model"
|
|
94
|
+
:fetch
|
|
95
|
+
:to-option
|
|
96
|
+
:multiple
|
|
97
|
+
:rethrow="isAuthError"
|
|
98
|
+
v-bind="$attrs"
|
|
99
|
+
>
|
|
100
|
+
<template v-if="$slots.info" #info><slot name="info" /></template>
|
|
101
|
+
</SInputAsyncDropdown>
|
|
102
|
+
</template>
|
|
@@ -8,7 +8,7 @@ import { type FieldData } from '../FieldData'
|
|
|
8
8
|
import { useLensEdit } from '../composables/LensEdit'
|
|
9
9
|
import { type AvatarField } from '../fields/AvatarField'
|
|
10
10
|
import { type Field } from '../fields/Field'
|
|
11
|
-
import
|
|
11
|
+
import LensInputAvatar from './LensInputAvatar.vue'
|
|
12
12
|
|
|
13
13
|
const props = defineProps<{
|
|
14
14
|
field: Field<FieldData>
|
|
@@ -116,7 +116,7 @@ async function onChange(value: File | string | null | undefined) {
|
|
|
116
116
|
<SDataListItem v-if="editable">
|
|
117
117
|
<template #label>{{ field.label() }}</template>
|
|
118
118
|
<template #value>
|
|
119
|
-
<
|
|
119
|
+
<LensInputAvatar
|
|
120
120
|
:model-value="model"
|
|
121
121
|
:accept="avatarField.accept()"
|
|
122
122
|
:image-type="avatarField.imageType()"
|
|
@@ -4,11 +4,12 @@ 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
6
|
import SLink from '../../../components/SLink.vue'
|
|
7
|
-
import SPill
|
|
8
|
-
import SState
|
|
7
|
+
import SPill from '../../../components/SPill.vue'
|
|
8
|
+
import SState from '../../../components/SState.vue'
|
|
9
9
|
import { useManualDropdownPosition } from '../../../composables/Dropdown'
|
|
10
10
|
import { useTrans } from '../../../composables/Lang'
|
|
11
11
|
import { useValidation } from '../../../composables/Validation'
|
|
12
|
+
import { type ColorMode } from '../../../support/Color'
|
|
12
13
|
import { day } from '../../../support/Day'
|
|
13
14
|
import { dispatchEditorKeydown, focusFirstEditable } from '../../../support/Dom'
|
|
14
15
|
import { type FieldData } from '../FieldData'
|
|
@@ -75,7 +76,7 @@ const resolvedCell = computed<any>(() => {
|
|
|
75
76
|
|
|
76
77
|
// A `pills` cell (e.g. a multi-select with displayAs: 'pills') renders as pills
|
|
77
78
|
// rather than text, mirroring the read-only `STableCellPills` column.
|
|
78
|
-
const displayPills = computed<{ label: string; color?:
|
|
79
|
+
const displayPills = computed<{ label: string; color?: ColorMode }[] | null>(() => {
|
|
79
80
|
const cell = resolvedCell.value
|
|
80
81
|
return cell && cell.type === 'pills' ? cell.pills : null
|
|
81
82
|
})
|
|
@@ -83,7 +84,7 @@ const displayPills = computed<{ label: string; color?: PillMode }[] | null>(() =
|
|
|
83
84
|
// A `state` cell (e.g. a select with displayAs: 'state') renders as a status
|
|
84
85
|
// badge rather than its bare label, mirroring the read-only `STableCellState`
|
|
85
86
|
// column.
|
|
86
|
-
const displayState = computed<{ label: string; mode?:
|
|
87
|
+
const displayState = computed<{ label: string; mode?: ColorMode } | null>(() => {
|
|
87
88
|
const cell = resolvedCell.value
|
|
88
89
|
return cell && cell.type === 'state' ? { label: cell.label, mode: cell.mode } : null
|
|
89
90
|
})
|
|
@@ -3,7 +3,7 @@ import SAvatar from '../../../components/SAvatar.vue'
|
|
|
3
3
|
import { type TableCell } from '../../../composables/Table'
|
|
4
4
|
import { type AvatarFieldData } from '../FieldData'
|
|
5
5
|
import { type FilterOperator } from '../FilterOperator'
|
|
6
|
-
import
|
|
6
|
+
import LensInputAvatar from '../components/LensInputAvatar.vue'
|
|
7
7
|
import { type FilterInput } from '../filter-inputs/FilterInput'
|
|
8
8
|
import { Field } from './Field'
|
|
9
9
|
|
|
@@ -62,7 +62,7 @@ export class AvatarField extends Field<AvatarFieldData> {
|
|
|
62
62
|
|
|
63
63
|
override formInputComponent(): any {
|
|
64
64
|
return this.defineFormInputComponent((props, { emit }) => {
|
|
65
|
-
return () => h(
|
|
65
|
+
return () => h(LensInputAvatar, {
|
|
66
66
|
'label': this.formInputLabel(),
|
|
67
67
|
'help': this.help() || undefined,
|
|
68
68
|
'accept': this.accept(),
|
|
@@ -50,9 +50,11 @@ export abstract class Field<T extends FieldData> {
|
|
|
50
50
|
* on the current app language.
|
|
51
51
|
*/
|
|
52
52
|
placeholder(): string | null {
|
|
53
|
-
return this.ctx.lang === 'ja'
|
|
54
|
-
?
|
|
55
|
-
:
|
|
53
|
+
return this.ctx.lang === 'ja' && 'placeholderJa' in this.data
|
|
54
|
+
? this.data.placeholderJa ?? null
|
|
55
|
+
: this.ctx.lang === 'en' && 'placeholderEn' in this.data
|
|
56
|
+
? this.data.placeholderEn ?? null
|
|
57
|
+
: null
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
/**
|
|
@@ -60,9 +62,11 @@ export abstract class Field<T extends FieldData> {
|
|
|
60
62
|
* on the current app language.
|
|
61
63
|
*/
|
|
62
64
|
help(): string | null {
|
|
63
|
-
return this.ctx.lang === 'ja'
|
|
64
|
-
?
|
|
65
|
-
:
|
|
65
|
+
return this.ctx.lang === 'ja' && 'helpJa' in this.data
|
|
66
|
+
? this.data.helpJa ?? null
|
|
67
|
+
: this.ctx.lang === 'en' && 'helpEn' in this.data
|
|
68
|
+
? this.data.helpEn ?? null
|
|
69
|
+
: null
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ValidationArgs } from '@vuelidate/core'
|
|
2
2
|
import { defineAsyncComponent } from 'vue'
|
|
3
|
-
import SInputDropdown
|
|
3
|
+
import SInputDropdown from '../../../components/SInputDropdown.vue'
|
|
4
|
+
import { type Option } from '../../../support/InputDropdown'
|
|
4
5
|
import { required } from '../../../validation/rules'
|
|
5
6
|
import { isAuthError } from '../validation/ServerErrors'
|
|
6
7
|
import { FilterInput } from './FilterInput'
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { type Component, type MaybeRef, computed, unref, useSlots } from 'vue'
|
|
3
3
|
import { type Position } from '../composables/Tooltip'
|
|
4
|
+
import { type ColorMode } from '../support/Color'
|
|
4
5
|
import SFragment from './SFragment.vue'
|
|
5
6
|
import SLink from './SLink.vue'
|
|
6
7
|
import SSpinner from './SSpinner.vue'
|
|
@@ -20,16 +21,7 @@ export type Size =
|
|
|
20
21
|
|
|
21
22
|
export type Type = 'fill' | 'outline' | 'text'
|
|
22
23
|
|
|
23
|
-
export type Mode =
|
|
24
|
-
| 'default'
|
|
25
|
-
| 'mute'
|
|
26
|
-
| 'neutral'
|
|
27
|
-
| 'white'
|
|
28
|
-
| 'black'
|
|
29
|
-
| 'info'
|
|
30
|
-
| 'success'
|
|
31
|
-
| 'warning'
|
|
32
|
-
| 'danger'
|
|
24
|
+
export type Mode = ColorMode | 'white' | 'black'
|
|
33
25
|
|
|
34
26
|
export interface Tooltip {
|
|
35
27
|
tag?: Component | string
|
|
@@ -4,9 +4,10 @@ import { type Component, computed } from 'vue'
|
|
|
4
4
|
import { useControlSize } from '../composables/Control'
|
|
5
5
|
import { useTrans } from '../composables/Lang'
|
|
6
6
|
import { type Validatable } from '../composables/Validation'
|
|
7
|
-
import
|
|
7
|
+
import { type Align, type TextColor } from '../support/InputText'
|
|
8
|
+
import SInputText from './SInputText.vue'
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
export interface Props {
|
|
10
11
|
placeholder?: string
|
|
11
12
|
unitAfter?: Component | string
|
|
12
13
|
textColor?: TextColor | ((value: string | null) => TextColor)
|
|
@@ -16,7 +17,9 @@ const props = defineProps<{
|
|
|
16
17
|
modelValue?: string | null
|
|
17
18
|
displayValue?: string | null
|
|
18
19
|
validation?: Validatable
|
|
19
|
-
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const props = defineProps<Props>()
|
|
20
23
|
|
|
21
24
|
defineEmits<{
|
|
22
25
|
'update:model-value': [value: string | null]
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from 'vue'
|
|
3
|
+
import { type ColorMode } from '../support/Color'
|
|
3
4
|
import SDescEmpty from './SDescEmpty.vue'
|
|
4
|
-
import SPill
|
|
5
|
+
import SPill from './SPill.vue'
|
|
5
6
|
|
|
6
7
|
export interface Pill {
|
|
7
|
-
mode?:
|
|
8
|
+
mode?: ColorMode
|
|
8
9
|
label: string
|
|
9
10
|
}
|
|
10
11
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { type ColorMode } from '../support/Color'
|
|
2
3
|
import SDescEmpty from './SDescEmpty.vue'
|
|
3
|
-
import SState
|
|
4
|
+
import SState from './SState.vue'
|
|
4
5
|
|
|
5
6
|
export interface State {
|
|
6
|
-
mode?:
|
|
7
|
+
mode?: ColorMode
|
|
7
8
|
label: string
|
|
8
9
|
}
|
|
9
10
|
|
|
@@ -9,14 +9,16 @@ import {
|
|
|
9
9
|
import { useFlyout } from '../composables/Flyout'
|
|
10
10
|
import SDropdown from './SDropdown.vue'
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
export interface Props {
|
|
13
13
|
label?: Component | string
|
|
14
14
|
clickable?: boolean
|
|
15
15
|
dropdown?: DropdownSection[]
|
|
16
16
|
dropdownCaret?: boolean
|
|
17
17
|
dropdownPosition?: 'top' | 'bottom'
|
|
18
18
|
disabled?: boolean
|
|
19
|
-
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
20
22
|
clickable: true,
|
|
21
23
|
dropdown: () => [],
|
|
22
24
|
dropdownCaret: true
|