@globalbrain/sefirot 4.34.1 → 4.35.1

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.
Files changed (55) hide show
  1. package/config/nuxt.js +44 -1
  2. package/config/vite.js +2 -3
  3. package/lib/blocks/lens/FieldContext.ts +5 -0
  4. package/lib/blocks/lens/FieldData.ts +140 -0
  5. package/lib/blocks/lens/FieldRegistry.ts +23 -0
  6. package/lib/blocks/lens/FileDownloader.ts +1 -0
  7. package/lib/blocks/lens/FilterOperator.ts +33 -0
  8. package/lib/blocks/lens/LensQuery.ts +10 -0
  9. package/lib/blocks/lens/LensResult.ts +20 -0
  10. package/lib/blocks/lens/ResourceFetcher.ts +3 -0
  11. package/lib/blocks/lens/Rule.ts +12 -0
  12. package/lib/blocks/lens/components/LensCatalog.vue +490 -0
  13. package/lib/blocks/lens/components/LensCatalogControl.vue +220 -0
  14. package/lib/blocks/lens/components/LensCatalogFooter.vue +46 -0
  15. package/lib/blocks/lens/components/LensCatalogStateFilter.vue +171 -0
  16. package/lib/blocks/lens/components/LensCatalogStateFilterCondition.vue +86 -0
  17. package/lib/blocks/lens/components/LensCatalogStateFilterGroup.vue +102 -0
  18. package/lib/blocks/lens/components/LensCatalogStateSort.vue +159 -0
  19. package/lib/blocks/lens/components/LensFormFilter.vue +169 -0
  20. package/lib/blocks/lens/components/LensFormFilterCondition.vue +205 -0
  21. package/lib/blocks/lens/components/LensFormFilterGroup.vue +175 -0
  22. package/lib/blocks/lens/components/LensFormOverride.vue +45 -0
  23. package/lib/blocks/lens/components/LensFormOverrideBase.vue +204 -0
  24. package/lib/blocks/lens/components/LensFormView.vue +347 -0
  25. package/lib/blocks/lens/components/LensTable.vue +154 -0
  26. package/lib/blocks/lens/composables/FieldFactory.ts +27 -0
  27. package/lib/blocks/lens/composables/FieldRegistry.ts +16 -0
  28. package/lib/blocks/lens/composables/FileDownloader.ts +10 -0
  29. package/lib/blocks/lens/composables/ResourceFetcher.ts +30 -0
  30. package/lib/blocks/lens/composables/SetupLens.ts +55 -0
  31. package/lib/blocks/lens/fields/ContentField.ts +34 -0
  32. package/lib/blocks/lens/fields/DateField.ts +66 -0
  33. package/lib/blocks/lens/fields/DatetimeField.ts +35 -0
  34. package/lib/blocks/lens/fields/Field.ts +244 -0
  35. package/lib/blocks/lens/fields/FileUploadField.ts +63 -0
  36. package/lib/blocks/lens/fields/IdField.ts +34 -0
  37. package/lib/blocks/lens/fields/LinkField.ts +53 -0
  38. package/lib/blocks/lens/fields/NumberField.ts +32 -0
  39. package/lib/blocks/lens/fields/RelatedManyField.ts +62 -0
  40. package/lib/blocks/lens/fields/SelectField.ts +198 -0
  41. package/lib/blocks/lens/fields/SlackMessageField.ts +34 -0
  42. package/lib/blocks/lens/fields/TextField.ts +46 -0
  43. package/lib/blocks/lens/fields/TextareaField.ts +49 -0
  44. package/lib/blocks/lens/filter-inputs/FilterInput.ts +72 -0
  45. package/lib/blocks/lens/filter-inputs/NumberFilterInput.ts +26 -0
  46. package/lib/blocks/lens/filter-inputs/SelectFilterInput.ts +76 -0
  47. package/lib/blocks/lens/filter-inputs/TextFilterInput.ts +26 -0
  48. package/lib/blocks/lens/validation/RuleMapper.ts +22 -0
  49. package/lib/components/SInputTextarea.vue +28 -10
  50. package/lib/components/STable.vue +230 -61
  51. package/lib/components/STableCell.vue +2 -2
  52. package/lib/composables/TableAnimation.ts +180 -0
  53. package/lib/support/Scroll.ts +263 -0
  54. package/lib/support/Utils.ts +1 -1
  55. package/package.json +7 -15
@@ -0,0 +1,32 @@
1
+ import { type TableCell } from '../../../composables/Table'
2
+ import { type NumberFieldData } from '../FieldData'
3
+ import { type FilterOperator } from '../FilterOperator'
4
+ import { type FilterInput } from '../filter-inputs/FilterInput'
5
+ import { NumberFilterInput } from '../filter-inputs/NumberFilterInput'
6
+ import { Field } from './Field'
7
+
8
+ export class NumberField extends Field<NumberFieldData> {
9
+ override tableCell(v: any, _r: any): TableCell {
10
+ return {
11
+ type: 'number',
12
+ value: v
13
+ }
14
+ }
15
+
16
+ override availableFilters(): Partial<Record<FilterOperator, FilterInput>> {
17
+ const number = new NumberFilterInput()
18
+
19
+ return {
20
+ '=': number,
21
+ '!=': number
22
+ }
23
+ }
24
+
25
+ override dataListItemComponent(): any {
26
+ throw new Error('Not implemented.')
27
+ }
28
+
29
+ override formInputComponent() {
30
+ throw new Error('Not implemented.')
31
+ }
32
+ }
@@ -0,0 +1,62 @@
1
+ import { type TableCell } from '../../../composables/Table'
2
+ import { type RelatedManyFieldData } from '../FieldData'
3
+ import { type FilterOperator } from '../FilterOperator'
4
+ import { type ResourceFetcher } from '../ResourceFetcher'
5
+ import { type FilterInput } from '../filter-inputs/FilterInput'
6
+ import { SelectFilterInput } from '../filter-inputs/SelectFilterInput'
7
+ import { Field } from './Field'
8
+
9
+ export class RelatedManyField extends Field<RelatedManyFieldData> {
10
+ fetcher: ResourceFetcher
11
+
12
+ constructor(ctx: any, data: RelatedManyFieldData, fetcher: ResourceFetcher) {
13
+ super(ctx, data)
14
+ this.fetcher = fetcher
15
+ }
16
+
17
+ override tableCell(v: any, _r: any): TableCell {
18
+ return {
19
+ type: 'pills',
20
+ pills: v.map((item: any) => ({
21
+ label: item[this.data.title],
22
+ value: item[this.data.filterKey]
23
+ }))
24
+ }
25
+ }
26
+
27
+ override availableFilters(): Partial<Record<FilterOperator, FilterInput>> {
28
+ const method = this.data.resourceEndpointMethod
29
+ const url = this.data.resourceEndpointPath
30
+ const key = this.data.resourceEndpointDataKey
31
+
32
+ if (!url) {
33
+ return {}
34
+ }
35
+
36
+ const optionsResolver = async () => {
37
+ const res = await this.fetcher(method, url)
38
+ const data = key ? res[key] : res
39
+ return data.map((item: any) => ({
40
+ value: item[this.data.filterKey],
41
+ label: item[this.data.resourceTitle]
42
+ }))
43
+ }
44
+
45
+ const selectOne = new SelectFilterInput().options(optionsResolver)
46
+ const selectMany = new SelectFilterInput().options(optionsResolver).multiple()
47
+
48
+ return {
49
+ '=': selectOne,
50
+ '!=': selectOne,
51
+ 'in': selectMany
52
+ }
53
+ }
54
+
55
+ override dataListItemComponent(): any {
56
+ throw new Error('Not implemented.')
57
+ }
58
+
59
+ override formInputComponent() {
60
+ throw new Error('Not implemented.')
61
+ }
62
+ }
@@ -0,0 +1,198 @@
1
+ import { xor } from 'lodash-es'
2
+ import { h } from 'vue'
3
+ import SInputCheckboxes from '../../../components/SInputCheckboxes.vue'
4
+ import SInputDropdown from '../../../components/SInputDropdown.vue'
5
+ import SInputRadios from '../../../components/SInputRadios.vue'
6
+ import SState from '../../../components/SState.vue'
7
+ import { type DropdownSection } from '../../../composables/Dropdown'
8
+ import { type TableCell } from '../../../composables/Table'
9
+ import { type SelectFieldData, type SelectFieldDataOption } from '../FieldData'
10
+ import { type FilterOperator } from '../FilterOperator'
11
+ import { type FilterInput } from '../filter-inputs/FilterInput'
12
+ import { SelectFilterInput } from '../filter-inputs/SelectFilterInput'
13
+ import { Field } from './Field'
14
+
15
+ export class SelectField extends Field<SelectFieldData> {
16
+ override async tableFilterMenu(filters: any[], onFilterUpdated: (filters: any[]) => void): Promise<DropdownSection | null> {
17
+ const selected = this.inFilterValueFor(this.data.key, filters)
18
+
19
+ const options = this.data.options.map((o) => ({
20
+ label: this.labelForOption(o),
21
+ value: o.value
22
+ }))
23
+
24
+ return {
25
+ type: 'filter',
26
+ search: false,
27
+ selected,
28
+ options,
29
+ onClick: (v) => { onFilterUpdated?.([this.data.key, 'in', xor(selected, [v])]) }
30
+ }
31
+ }
32
+
33
+ override tableCell(v: any, _r: any): TableCell {
34
+ if (v === null) {
35
+ return { type: 'text', value: null }
36
+ }
37
+
38
+ switch (this.data.displayAs) {
39
+ case 'state':
40
+ return this.tableCellState(v, _r)
41
+ case 'text':
42
+ return this.tableCellText(v, _r)
43
+ default:
44
+ throw new Error(`Unknown displayAs type: ${this.data.displayAs}`)
45
+ }
46
+ }
47
+
48
+ protected tableCellText(v: any, _r: any): TableCell {
49
+ v = Array.isArray(v) ? v : [v]
50
+
51
+ v = this.optionsForValues(v).map((o) => this.labelForOption(o)).join(', ')
52
+
53
+ return {
54
+ type: 'text',
55
+ value: v
56
+ }
57
+ }
58
+
59
+ protected tableCellState(v: any, _r: any): TableCell {
60
+ if (this.data.multiple) {
61
+ throw new Error('Displaying select field as state with multiple option is not supported.')
62
+ }
63
+
64
+ const option = this.optionForValue(v)
65
+
66
+ return {
67
+ type: 'state',
68
+ mode: option.mode,
69
+ label: this.labelForOption(option)
70
+ }
71
+ }
72
+
73
+ override availableFilters(): Partial<Record<FilterOperator, FilterInput>> {
74
+ const options = this.data.options.map((o) => ({
75
+ label: this.labelForOption(o),
76
+ value: o.value
77
+ }))
78
+
79
+ const selectOne = new SelectFilterInput().options(options)
80
+ const selectMany = new SelectFilterInput().options(options).multiple()
81
+
82
+ return {
83
+ '=': selectOne,
84
+ '!=': selectOne,
85
+ 'in': selectMany
86
+ }
87
+ }
88
+
89
+ override dataListItemComponent(): any {
90
+ return this.defineDataListItemComponent((value) => {
91
+ if (value === null) {
92
+ return null
93
+ }
94
+
95
+ switch (this.data.displayAs) {
96
+ case 'state':
97
+ return this.renderDataListItemValueForState(value)
98
+ case 'text':
99
+ return this.renderDataListItemValueForText(value)
100
+ default:
101
+ throw new Error(`Unknown displayAs type: ${this.data.displayAs}`)
102
+ }
103
+ })
104
+ }
105
+
106
+ protected renderDataListItemValueForState(value: any): any {
107
+ if (this.data.multiple) {
108
+ throw new Error('Displaying select field as state with multiple option is not supported.')
109
+ }
110
+
111
+ const option = this.optionForValue(value)
112
+
113
+ return h(SState, {
114
+ mode: option.mode,
115
+ label: this.labelForOption(option)
116
+ })
117
+ }
118
+
119
+ protected renderDataListItemValueForText(value: any): any {
120
+ if (this.data.multiple) {
121
+ value = Array.isArray(value) ? value : [value]
122
+ }
123
+
124
+ return this.optionsForValues(value).map((o) => this.labelForOption(o)).join(', ')
125
+ }
126
+
127
+ override inputEmptyValue(): any {
128
+ return this.data.multiple ? [] : null
129
+ }
130
+
131
+ override formInputComponent(): any {
132
+ switch (this.data.inputAs) {
133
+ case 'dropdown':
134
+ return this.defineDropdownInputComponent()
135
+ case 'radio':
136
+ return this.defineRadioInputComponent()
137
+ }
138
+ }
139
+
140
+ defineDropdownInputComponent(): any {
141
+ return this.defineFormInputComponent((props, { emit }) => {
142
+ return () => h(SInputDropdown, {
143
+ 'size': 'md',
144
+ 'label': this.formInputLabel(),
145
+ 'placeholder': this.placeholder() || undefined,
146
+ 'help': this.help() || undefined,
147
+ 'options': this.data.options.map((o) => ({
148
+ label: this.labelForOption(o),
149
+ value: o.value
150
+ })),
151
+ 'modelValue': props.modelValue,
152
+ 'validation': props.validation,
153
+ 'onUpdate:modelValue': (value: any) => {
154
+ emit('update:modelValue', value)
155
+ }
156
+ })
157
+ })
158
+ }
159
+
160
+ defineRadioInputComponent(): any {
161
+ return this.defineFormInputComponent((props, { emit }) => {
162
+ const Comp = this.data.multiple ? SInputCheckboxes : SInputRadios
163
+
164
+ return () => h(Comp as any, {
165
+ 'size': 'md',
166
+ 'label': this.formInputLabel(),
167
+ 'help': this.help() || undefined,
168
+ 'options': this.data.options.map((o) => ({
169
+ label: this.labelForOption(o),
170
+ value: o.value
171
+ })),
172
+ 'modelValue': props.modelValue,
173
+ 'validation': props.validation,
174
+ 'onUpdate:modelValue': (value: any) => {
175
+ emit('update:modelValue', value)
176
+ }
177
+ })
178
+ })
179
+ }
180
+
181
+ protected labelForOption(option: SelectFieldDataOption): string {
182
+ return this.ctx.lang === 'ja' ? option.labelJa : option.labelEn
183
+ }
184
+
185
+ protected optionForValue(value: string): SelectFieldDataOption {
186
+ const option = this.data.options.find((o) => o.value === value)
187
+
188
+ if (!option) {
189
+ throw new Error(`Option with value "${value}" not found in select field options.`)
190
+ }
191
+
192
+ return option
193
+ }
194
+
195
+ protected optionsForValues(values: string[]): SelectFieldDataOption[] {
196
+ return this.data.options.filter((o) => values.includes(o.value))
197
+ }
198
+ }
@@ -0,0 +1,34 @@
1
+ import { h } from 'vue'
2
+ import SLink from '../../../components/SLink.vue'
3
+ import { type TableCell } from '../../../composables/Table'
4
+ import { type SlackMessageFieldData } from '../FieldData'
5
+ import { type FilterOperator } from '../FilterOperator'
6
+ import { type FilterInput } from '../filter-inputs/FilterInput'
7
+ import { Field } from './Field'
8
+
9
+ export class SlackMessageField extends Field<SlackMessageFieldData> {
10
+ override tableCell(v: any, _r: any): TableCell {
11
+ return {
12
+ type: 'text',
13
+ value: v,
14
+ link: v,
15
+ color: 'info'
16
+ }
17
+ }
18
+
19
+ override availableFilters(): Partial<Record<FilterOperator, FilterInput>> {
20
+ return {}
21
+ }
22
+
23
+ override dataListItemComponent(): any {
24
+ return this.defineDataListItemComponent((value) => {
25
+ return h(SLink, { href: value }, () => value)
26
+ }, {
27
+ lineClamp: 1
28
+ })
29
+ }
30
+
31
+ override formInputComponent(): any {
32
+ throw new Error('Not implemented.')
33
+ }
34
+ }
@@ -0,0 +1,46 @@
1
+ import { h } from 'vue'
2
+ import SInputText from '../../../components/SInputText.vue'
3
+ import { type TableCell } from '../../../composables/Table'
4
+ import { type TextFieldData } from '../FieldData'
5
+ import { type FilterOperator } from '../FilterOperator'
6
+ import { type FilterInput } from '../filter-inputs/FilterInput'
7
+ import { TextFilterInput } from '../filter-inputs/TextFilterInput'
8
+ import { Field } from './Field'
9
+
10
+ export class TextField extends Field<TextFieldData> {
11
+ override tableCell(v: any, _r: any): TableCell {
12
+ return {
13
+ type: 'text',
14
+ value: v
15
+ }
16
+ }
17
+
18
+ override availableFilters(): Partial<Record<FilterOperator, FilterInput>> {
19
+ const text = new TextFilterInput()
20
+
21
+ return {
22
+ '=': text,
23
+ '!=': text
24
+ }
25
+ }
26
+
27
+ override dataListItemComponent(): any {
28
+ return this.defineDataListItemComponent((value) => value)
29
+ }
30
+
31
+ override formInputComponent(): any {
32
+ return this.defineFormInputComponent((props, { emit }) => {
33
+ return () => h(SInputText, {
34
+ 'size': 'md',
35
+ 'label': this.formInputLabel(),
36
+ 'placeholder': this.placeholder() || undefined,
37
+ 'help': this.help() || undefined,
38
+ 'modelValue': props.modelValue,
39
+ 'validation': props.validation,
40
+ 'onUpdate:modelValue': (value: any) => {
41
+ emit('update:modelValue', value)
42
+ }
43
+ })
44
+ })
45
+ }
46
+ }
@@ -0,0 +1,49 @@
1
+ import { h } from 'vue'
2
+ import SInputTextarea from '../../../components/SInputTextarea.vue'
3
+ import { type TableCell } from '../../../composables/Table'
4
+ import { type TextareaFieldData } from '../FieldData'
5
+ import { type FilterOperator } from '../FilterOperator'
6
+ import { type FilterInput } from '../filter-inputs/FilterInput'
7
+ import { TextFilterInput } from '../filter-inputs/TextFilterInput'
8
+ import { Field } from './Field'
9
+
10
+ export class TextareaField extends Field<TextareaFieldData> {
11
+ override tableCell(v: any, _r: any): TableCell {
12
+ return {
13
+ type: 'text',
14
+ value: v
15
+ }
16
+ }
17
+
18
+ override availableFilters(): Partial<Record<FilterOperator, FilterInput>> {
19
+ const text = new TextFilterInput()
20
+
21
+ return {
22
+ '=': text,
23
+ '!=': text
24
+ }
25
+ }
26
+
27
+ override dataListItemComponent(): any {
28
+ return this.defineDataListItemComponent((value) => value, {
29
+ preWrap: true
30
+ })
31
+ }
32
+
33
+ override formInputComponent(): any {
34
+ return this.defineFormInputComponent((props, { emit }) => {
35
+ return () => h(SInputTextarea, {
36
+ 'size': 'md',
37
+ 'label': this.formInputLabel(),
38
+ 'placeholder': this.placeholder() || undefined,
39
+ 'help': this.help() || undefined,
40
+ 'rows': this.data.rows,
41
+ 'modelValue': props.modelValue,
42
+ 'validation': props.validation,
43
+ 'onUpdate:modelValue': (value: any) => {
44
+ emit('update:modelValue', value)
45
+ }
46
+ })
47
+ })
48
+ }
49
+ }
@@ -0,0 +1,72 @@
1
+ import { type ValidationArgs } from '@vuelidate/core'
2
+ import { defineComponent, h } from 'vue'
3
+
4
+ export abstract class FilterInput {
5
+ /**
6
+ * Returns the validation rules for the filter input.
7
+ */
8
+ abstract rules(): Record<string, ValidationArgs>
9
+
10
+ /**
11
+ * Casts the input value to the correct type.
12
+ *
13
+ * This method gets called during rendering filter conditions in
14
+ * `LensFormFilterCondition` when the operator changes.
15
+ *
16
+ * We have to cast the value to the correct type when the input changes.
17
+ *
18
+ * For example, when filtering select input, and at first the user setups
19
+ * single select value filter: ['status', '=', 'open'].
20
+ *
21
+ * Then, user changes the operator to multi select: ['status', 'in', 'open'].
22
+ * Now, the value must be converted to array: ['status', 'in', ['open']].
23
+ */
24
+ abstract castValue(value: any): any
25
+
26
+ /**
27
+ * Helper method to cast value to string.
28
+ */
29
+ protected castValueToStringOrNull(value: any): string | null {
30
+ if (value == null) {
31
+ return null
32
+ }
33
+ return String(value)
34
+ }
35
+
36
+ /**
37
+ * Helper method to cast value to number.
38
+ */
39
+ protected castValueToNumberOrNull(value: any): number | null {
40
+ if (value == null) {
41
+ return null
42
+ }
43
+ const n = Number(value)
44
+ if (Number.isNaN(n)) {
45
+ return null
46
+ }
47
+ return n
48
+ }
49
+
50
+ /**
51
+ * Converts the value to a text representation for display. This value gets
52
+ * displayed in the <LensCatalogStateFilterCondition> component.
53
+ */
54
+ abstract valueToText(value: any): Promise<string>
55
+
56
+ /**
57
+ * Returns the Vue component for the filter input.
58
+ */
59
+ abstract component(): any
60
+
61
+ /**
62
+ * Helper function to define a Vue component with props merging.
63
+ */
64
+ protected defineComponent(component: any, props: any) {
65
+ return defineComponent((baseProps) => {
66
+ return () => h(component, {
67
+ ...baseProps,
68
+ ...props
69
+ })
70
+ })
71
+ }
72
+ }
@@ -0,0 +1,26 @@
1
+ import { type ValidationArgs } from '@vuelidate/core'
2
+ import SInputNumber from '../../../components/SInputNumber.vue'
3
+ import { required } from '../../../validation/rules'
4
+ import { FilterInput } from './FilterInput'
5
+
6
+ export class NumberFilterInput extends FilterInput {
7
+ rules(): Record<string, ValidationArgs> {
8
+ return {
9
+ required: required()
10
+ }
11
+ }
12
+
13
+ castValue(value: any): any {
14
+ return this.castValueToNumberOrNull(value)
15
+ }
16
+
17
+ async valueToText(value: any): Promise<string> {
18
+ return value
19
+ }
20
+
21
+ component(): any {
22
+ return this.defineComponent(SInputNumber, {
23
+ size: 'sm'
24
+ })
25
+ }
26
+ }
@@ -0,0 +1,76 @@
1
+ import { type ValidationArgs } from '@vuelidate/core'
2
+ import { defineAsyncComponent } from 'vue'
3
+ import SInputDropdown, { type Option } from '../../../components/SInputDropdown.vue'
4
+ import { required } from '../../../validation/rules'
5
+ import { FilterInput } from './FilterInput'
6
+
7
+ export class SelectFilterInput extends FilterInput {
8
+ protected _multiple = false
9
+
10
+ protected optionsResolver: Option[] | (() => Promise<Option[]>) = []
11
+
12
+ multiple(): this {
13
+ this._multiple = true
14
+ return this
15
+ }
16
+
17
+ options(resolver: Option[] | (() => Promise<Option[]>)): this {
18
+ this.optionsResolver = resolver
19
+ return this
20
+ }
21
+
22
+ rules(): Record<string, ValidationArgs> {
23
+ return {
24
+ required: required()
25
+ }
26
+ }
27
+
28
+ castValue(value: any): any {
29
+ return this._multiple
30
+ ? this.castValueMany(value)
31
+ : this.castValueOne(value)
32
+ }
33
+
34
+ protected castValueOne(value: any): any {
35
+ if (Array.isArray(value)) {
36
+ return value[0]
37
+ }
38
+ return this.castValueToStringOrNull(value)
39
+ }
40
+
41
+ protected castValueMany(value: any): any {
42
+ if (Array.isArray(value)) {
43
+ return value
44
+ }
45
+ return [value]
46
+ }
47
+
48
+ async valueToText(value: any): Promise<string> {
49
+ const options = await this.resolveOptions()
50
+
51
+ return this.valueAsArray(value)
52
+ .map((v) => options.find((o) => o.value === v)!.label)
53
+ .join(', ')
54
+ }
55
+
56
+ protected valueAsArray(value: any): any[] {
57
+ return Array.isArray(value) ? value : [value]
58
+ }
59
+
60
+ component(): any {
61
+ return defineAsyncComponent(async () => {
62
+ const options = await this.resolveOptions()
63
+ return this.defineComponent(SInputDropdown, {
64
+ size: 'sm',
65
+ options
66
+ })
67
+ })
68
+ }
69
+
70
+ protected async resolveOptions(): Promise<Option[]> {
71
+ if (typeof this.optionsResolver === 'function') {
72
+ return await this.optionsResolver()
73
+ }
74
+ return this.optionsResolver
75
+ }
76
+ }
@@ -0,0 +1,26 @@
1
+ import { type ValidationArgs } from '@vuelidate/core'
2
+ import SInputText from '../../../components/SInputText.vue'
3
+ import { required } from '../../../validation/rules'
4
+ import { FilterInput } from './FilterInput'
5
+
6
+ export class TextFilterInput extends FilterInput {
7
+ rules(): Record<string, ValidationArgs> {
8
+ return {
9
+ required: required()
10
+ }
11
+ }
12
+
13
+ castValue(value: any): any {
14
+ return this.castValueToStringOrNull(value)
15
+ }
16
+
17
+ async valueToText(value: any): Promise<string> {
18
+ return value
19
+ }
20
+
21
+ component(): any {
22
+ return this.defineComponent(SInputText, {
23
+ size: 'sm'
24
+ })
25
+ }
26
+ }
@@ -0,0 +1,22 @@
1
+ import { type ValidationArgs, type ValidationRuleWithParams } from '@vuelidate/core'
2
+ import { maxLength, required } from '../../../validation/rules'
3
+ import { type Rule } from '../Rule'
4
+
5
+ /**
6
+ * Maps field rules to vuelidate validation rules.
7
+ */
8
+ export function map(rules: Rule[]): ValidationArgs {
9
+ return rules.reduce((carry: ValidationArgs<any>, rule: Rule) => {
10
+ carry[rule.type] = mapRule(rule)
11
+ return carry
12
+ }, {})
13
+ }
14
+
15
+ function mapRule(rule: Rule): ValidationRuleWithParams {
16
+ switch (rule.type) {
17
+ case 'max_length':
18
+ return maxLength(rule.length)
19
+ case 'required':
20
+ return required()
21
+ }
22
+ }