@xy-planning-network/trees 0.15.2-dev.0 → 0.15.3
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/dist/trees.es.js +4165 -3540
- package/dist/trees.umd.js +39 -37
- package/package.json +3 -2
- package/src/index.css +10 -0
- package/src/lib-components/forms/Combobox.vue +240 -0
- package/src/lib-components/forms/FileUpload.vue +596 -0
- package/src/lib-components/forms/FormCell.vue +52 -0
- package/src/lib-components/forms/FormDisplay.vue +96 -0
- package/src/lib-components/forms/FormGrid.vue +23 -0
- package/src/lib-components/forms/FormSection.vue +57 -0
- package/src/lib-components/forms/InputDisplay.vue +36 -0
- package/src/lib-components/forms/InputHelp.vue +9 -2
- package/src/lib-components/forms/MultiSelect.vue +404 -0
- package/src/lib-components/forms/RadioCards.vue +14 -1
- package/src/lib-components/forms/RepeaterDisplay.vue +214 -0
- package/src/lib-components/indicators/DismissableBadge.vue +31 -0
- package/src/lib-components/lists/DataTable.vue +2 -4
- package/src/lib-components/lists/DynamicTable.vue +1 -2
- package/types/composables/forms.d.ts +22 -0
- package/types/composables/index.d.ts +1 -0
- package/types/composables/useBulkActions.d.ts +25 -0
- package/types/composables/useFieldsSchema.d.ts +26 -11
- package/types/composables/useRepeater.d.ts +116 -0
- package/types/composables/useTable.d.ts +1 -0
- package/types/entry.d.ts +2 -1
- package/types/helpers/Bytes.d.ts +14 -0
- package/types/lib-components/forms/BaseInput.vue.d.ts +1 -1
- package/types/lib-components/forms/Checkbox.vue.d.ts +1 -1
- package/types/lib-components/forms/Combobox.vue.d.ts +1 -1
- package/types/lib-components/forms/DateRangePicker.vue.d.ts +1 -1
- package/types/lib-components/forms/DateTimeInput.vue.d.ts +1 -1
- package/types/lib-components/forms/FileUpload.vue.d.ts +19 -0
- package/types/lib-components/forms/MultiCheckboxes.vue.d.ts +1 -1
- package/types/lib-components/forms/MultiSelect.vue.d.ts +1 -1
- package/types/lib-components/forms/NumberInput.vue.d.ts +3 -3
- package/types/lib-components/forms/Radio.vue.d.ts +1 -1
- package/types/lib-components/forms/RepeaterDisplay.vue.d.ts +29 -0
- package/types/lib-components/forms/Select.vue.d.ts +1 -1
- package/types/lib-components/forms/TextArea.vue.d.ts +1 -1
- package/types/lib-components/forms/YesOrNoRadio.vue.d.ts +1 -1
- package/types/lib-components/index.d.ts +1 -0
- package/types/lib-components/lists/DataTable.vue.d.ts +16 -2
- package/types/composables/useActionItems.d.ts +0 -12
- package/types/helpers/Object.d.ts +0 -8
- package/types/lib-components/lists/TableActionButtons.vue.d.ts +0 -8
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, ref, useId, useTemplateRef, watch } from "vue"
|
|
3
|
+
import { PlusCircleIcon, TrashIcon } from "@heroicons/vue/outline"
|
|
4
|
+
import type { RepeaterRow } from "@/composables/useRepeater"
|
|
5
|
+
import FormCell from "@/lib-components/forms/FormCell.vue"
|
|
6
|
+
import InputError from "@/lib-components/forms/InputError.vue"
|
|
7
|
+
import InputHelp from "@/lib-components/forms/InputHelp.vue"
|
|
8
|
+
import InputLabel from "@/lib-components/forms/InputLabel.vue"
|
|
9
|
+
|
|
10
|
+
const props = withDefaults(
|
|
11
|
+
defineProps<{
|
|
12
|
+
type: RepeaterRow["type"]
|
|
13
|
+
title?: string
|
|
14
|
+
help?: string
|
|
15
|
+
count: number
|
|
16
|
+
min?: number
|
|
17
|
+
max?: number
|
|
18
|
+
rows: RepeaterRow[]
|
|
19
|
+
addText?: string
|
|
20
|
+
addDisabled?: boolean
|
|
21
|
+
removeDisabled?: boolean
|
|
22
|
+
}>(),
|
|
23
|
+
{
|
|
24
|
+
title: "",
|
|
25
|
+
help: "",
|
|
26
|
+
min: undefined,
|
|
27
|
+
max: undefined,
|
|
28
|
+
addText: "Add",
|
|
29
|
+
addDisabled: false,
|
|
30
|
+
removeDisabled: false,
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
const emit = defineEmits<{
|
|
35
|
+
add: []
|
|
36
|
+
remove: [index: number]
|
|
37
|
+
}>()
|
|
38
|
+
|
|
39
|
+
const validationID = useId()
|
|
40
|
+
const errorState = ref("")
|
|
41
|
+
const errorInputRef = useTemplateRef<HTMLInputElement>("errorInput")
|
|
42
|
+
|
|
43
|
+
const countError = computed(() => {
|
|
44
|
+
const belowMin = props.min !== undefined && props.count < props.min
|
|
45
|
+
const aboveMax = props.max !== undefined && props.count > props.max
|
|
46
|
+
|
|
47
|
+
if (props.min !== undefined && props.max !== undefined) {
|
|
48
|
+
return belowMin || aboveMax
|
|
49
|
+
? `Please add between ${props.min} and ${props.max} items.`
|
|
50
|
+
: ""
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (belowMin) {
|
|
54
|
+
return `Please add at least ${props.min} items.`
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (aboveMax) {
|
|
58
|
+
return `Please limit this list to ${props.max} items.`
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return ""
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const setValidationError = () => {
|
|
65
|
+
errorState.value = countError.value
|
|
66
|
+
errorInputRef.value?.setCustomValidity(countError.value)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const isFullWidthField = (row: RepeaterRow) => {
|
|
70
|
+
const span = row.fields[0]?.input.span
|
|
71
|
+
return row.type === "field" && (!span || span === "full")
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const isRemoveDisabled = (index: number) => {
|
|
75
|
+
const isModelRow = index < props.count
|
|
76
|
+
return props.removeDisabled || (isModelRow && props.count <= (props.min ?? 0))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
watch(countError, (error) => {
|
|
80
|
+
if (!error) {
|
|
81
|
+
errorInputRef.value?.setCustomValidity("")
|
|
82
|
+
errorState.value = ""
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (errorState.value) {
|
|
87
|
+
errorState.value = error
|
|
88
|
+
errorInputRef.value?.setCustomValidity(error)
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
</script>
|
|
92
|
+
|
|
93
|
+
<template>
|
|
94
|
+
<div
|
|
95
|
+
class="relative space-y-5"
|
|
96
|
+
role="group"
|
|
97
|
+
:aria-labelledby="title ? `${validationID}-label` : undefined"
|
|
98
|
+
:aria-describedby="help ? `${validationID}-help` : undefined"
|
|
99
|
+
:aria-errormessage="errorState ? `${validationID}-error` : undefined"
|
|
100
|
+
>
|
|
101
|
+
<div v-if="title || help || errorState">
|
|
102
|
+
<InputLabel :id="`${validationID}-label`" tag="div" :label="title" />
|
|
103
|
+
<InputHelp
|
|
104
|
+
:id="`${validationID}-help`"
|
|
105
|
+
:class="{ 'mt-1': title }"
|
|
106
|
+
:text="help"
|
|
107
|
+
/>
|
|
108
|
+
<InputError
|
|
109
|
+
:id="`${validationID}-error`"
|
|
110
|
+
:class="{ 'mt-1': title || help }"
|
|
111
|
+
:text="errorState"
|
|
112
|
+
/>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div
|
|
116
|
+
v-for="(row, rowIndex) in rows"
|
|
117
|
+
:key="row.key"
|
|
118
|
+
class="relative"
|
|
119
|
+
:class="{
|
|
120
|
+
'rounded-xy border border-neutral-200 p-6': row.type === 'collection',
|
|
121
|
+
}"
|
|
122
|
+
>
|
|
123
|
+
<div
|
|
124
|
+
v-if="row.type === 'collection'"
|
|
125
|
+
class="mb-6 flex items-center justify-between gap-x-4"
|
|
126
|
+
>
|
|
127
|
+
<InputLabel v-if="row.title" tag="div" :label="row.title" />
|
|
128
|
+
|
|
129
|
+
<button
|
|
130
|
+
type="button"
|
|
131
|
+
class="xy-btn-neutral-sm ml-auto shrink-0 gap-x-1.5"
|
|
132
|
+
:disabled="isRemoveDisabled(rowIndex)"
|
|
133
|
+
:aria-label="`Remove item ${rowIndex + 1}`"
|
|
134
|
+
@click="emit('remove', rowIndex)"
|
|
135
|
+
>
|
|
136
|
+
<TrashIcon class="h-4 w-4 text-red-700" aria-hidden="true" />
|
|
137
|
+
<span>Remove</span>
|
|
138
|
+
</button>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<div
|
|
142
|
+
v-if="row.type === 'field'"
|
|
143
|
+
:class="
|
|
144
|
+
isFullWidthField(row)
|
|
145
|
+
? 'flex items-center gap-x-3'
|
|
146
|
+
: 'grid grid-cols-[minmax(0,1fr)_auto] items-center gap-x-3 sm:grid-cols-12'
|
|
147
|
+
"
|
|
148
|
+
>
|
|
149
|
+
<template v-for="field in row.fields" :key="field.key">
|
|
150
|
+
<FormCell
|
|
151
|
+
v-if="field.input.show"
|
|
152
|
+
:class="{ 'min-w-0 flex-1': isFullWidthField(row) }"
|
|
153
|
+
:span="field.input.span || 'full'"
|
|
154
|
+
:start="field.input.start"
|
|
155
|
+
>
|
|
156
|
+
<component
|
|
157
|
+
:is="field.input.$component"
|
|
158
|
+
v-bind="field.input.$props"
|
|
159
|
+
/>
|
|
160
|
+
</FormCell>
|
|
161
|
+
</template>
|
|
162
|
+
|
|
163
|
+
<button
|
|
164
|
+
type="button"
|
|
165
|
+
class="xy-btn-neutral-sm shrink-0 justify-self-start"
|
|
166
|
+
:disabled="isRemoveDisabled(rowIndex)"
|
|
167
|
+
:aria-label="`Remove item ${rowIndex + 1}`"
|
|
168
|
+
@click="emit('remove', rowIndex)"
|
|
169
|
+
>
|
|
170
|
+
<TrashIcon class="h-5 w-5 text-red-700" aria-hidden="true" />
|
|
171
|
+
</button>
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
<div v-else>
|
|
175
|
+
<div class="grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-12">
|
|
176
|
+
<template v-for="field in row.fields" :key="field.key">
|
|
177
|
+
<FormCell
|
|
178
|
+
v-if="field.input.show"
|
|
179
|
+
:span="field.input.span || 'full'"
|
|
180
|
+
:start="field.input.start"
|
|
181
|
+
>
|
|
182
|
+
<component
|
|
183
|
+
:is="field.input.$component"
|
|
184
|
+
v-bind="field.input.$props"
|
|
185
|
+
/>
|
|
186
|
+
</FormCell>
|
|
187
|
+
</template>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
|
|
192
|
+
<button
|
|
193
|
+
type="button"
|
|
194
|
+
class="xy-btn-neutral-sm"
|
|
195
|
+
:disabled="addDisabled"
|
|
196
|
+
@click="emit('add')"
|
|
197
|
+
>
|
|
198
|
+
<PlusCircleIcon class="mr-1 h-5 w-5 text-xy-blue" aria-hidden="true" />
|
|
199
|
+
{{ addText }}
|
|
200
|
+
</button>
|
|
201
|
+
|
|
202
|
+
<!-- Participates in native form validation when the repeater is outside its limits. -->
|
|
203
|
+
<input
|
|
204
|
+
v-if="countError"
|
|
205
|
+
ref="errorInput"
|
|
206
|
+
required
|
|
207
|
+
class="sr-only top-1 left-1"
|
|
208
|
+
aria-hidden
|
|
209
|
+
:tabindex="errorState ? undefined : -1"
|
|
210
|
+
type="checkbox"
|
|
211
|
+
@invalid="setValidationError"
|
|
212
|
+
/>
|
|
213
|
+
</div>
|
|
214
|
+
</template>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { BadgeKind } from "@/composables/badge"
|
|
3
|
+
import { XMarkIcon } from "@heroicons/vue/solid"
|
|
4
|
+
|
|
5
|
+
withDefaults(defineProps<{ label: string; kind?: BadgeKind }>(), {
|
|
6
|
+
kind: BadgeKind.Neutral,
|
|
7
|
+
})
|
|
8
|
+
defineEmits<{
|
|
9
|
+
dismiss: [label: string]
|
|
10
|
+
}>()
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<span
|
|
15
|
+
:class="[kind, 'flex items-center gap-x-1 group cursor-pointer']"
|
|
16
|
+
@click="$emit('dismiss', label)"
|
|
17
|
+
>
|
|
18
|
+
<span class="flex-1 truncate">{{ label }}</span>
|
|
19
|
+
<button
|
|
20
|
+
type="button"
|
|
21
|
+
class="group relative -mr-1.5 h-3.5 w-3.5 rounded-sm group-hover:bg-gray-500/10 inline-flex items-center justify-center"
|
|
22
|
+
>
|
|
23
|
+
<span class="sr-only">Remove</span>
|
|
24
|
+
<XMarkIcon
|
|
25
|
+
class="h-3 w-3 stroke-gray-500/20 group-hover:stroke-gray-500/75"
|
|
26
|
+
/>
|
|
27
|
+
<!--makes the clickable target larger-->
|
|
28
|
+
<span class="absolute -inset-2" />
|
|
29
|
+
</button>
|
|
30
|
+
</span>
|
|
31
|
+
</template>
|
|
@@ -34,15 +34,13 @@ const {
|
|
|
34
34
|
bulkSelectIndeterminate,
|
|
35
35
|
bulkSelectOnChange,
|
|
36
36
|
hasBulkActions,
|
|
37
|
-
hasSelection,
|
|
38
37
|
publicMethods,
|
|
39
38
|
selectable,
|
|
40
39
|
selectedOnPage,
|
|
41
40
|
} = useBulkActions(
|
|
42
41
|
toRef(props, "tableData"),
|
|
43
42
|
toRef(props, "tableBulkActions"),
|
|
44
|
-
selected
|
|
45
|
-
{ reconcileSelections: true }
|
|
43
|
+
selected
|
|
46
44
|
)
|
|
47
45
|
|
|
48
46
|
const { columns, hasActions, isEmptyCellValue, rows } = useTable(
|
|
@@ -99,7 +97,7 @@ defineExpose(publicMethods)
|
|
|
99
97
|
/>
|
|
100
98
|
</tr>
|
|
101
99
|
|
|
102
|
-
<tr v-if="hasBulkActions &&
|
|
100
|
+
<tr v-if="hasBulkActions && selected.length > 0">
|
|
103
101
|
<td colspan="100%" class="px-6 py-2.5 border-t bg-neutral-50">
|
|
104
102
|
<div class="flex items-center gap-x-3">
|
|
105
103
|
<div class="text-sm shrink-0">
|
|
@@ -153,7 +153,6 @@ const {
|
|
|
153
153
|
bulkSelectOnChange,
|
|
154
154
|
clearSelections,
|
|
155
155
|
hasBulkActions,
|
|
156
|
-
hasSelection,
|
|
157
156
|
publicMethods,
|
|
158
157
|
selectable,
|
|
159
158
|
selectedOnPage,
|
|
@@ -345,7 +344,7 @@ loadAndRender()
|
|
|
345
344
|
/>
|
|
346
345
|
</tr>
|
|
347
346
|
|
|
348
|
-
<tr v-if="hasBulkActions &&
|
|
347
|
+
<tr v-if="hasBulkActions && selected.length > 0">
|
|
349
348
|
<td colspan="100%" class="px-6 py-2.5 border-t bg-neutral-50">
|
|
350
349
|
<div class="flex items-center gap-x-3">
|
|
351
350
|
<div class="text-sm shrink-0">
|
|
@@ -41,6 +41,28 @@ export interface DateRangeInput extends Input {
|
|
|
41
41
|
export interface DateTimeInput extends Input {
|
|
42
42
|
modelValue?: string | null;
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* UploadedFile is the durable value stored in a form model after a file is
|
|
46
|
+
* uploaded.
|
|
47
|
+
*
|
|
48
|
+
* NOTE(spk): Add display metadata like file size, content type, or timestamps
|
|
49
|
+
* here as upload APIs need it. The ID is the authoritative value.
|
|
50
|
+
*/
|
|
51
|
+
export interface UploadedFile {
|
|
52
|
+
id: number;
|
|
53
|
+
filename: string;
|
|
54
|
+
url?: string;
|
|
55
|
+
}
|
|
56
|
+
export type FileUploadValue = UploadedFile | UploadedFile[] | null;
|
|
57
|
+
export interface FileUploadInput extends Input {
|
|
58
|
+
action: string;
|
|
59
|
+
accept?: string[];
|
|
60
|
+
multiple?: boolean;
|
|
61
|
+
fileField?: string;
|
|
62
|
+
maxFileBytes?: number;
|
|
63
|
+
maxFiles?: number;
|
|
64
|
+
modelValue?: FileUploadValue;
|
|
65
|
+
}
|
|
44
66
|
export interface TextLikeInput extends Input {
|
|
45
67
|
modelValue?: string | number | null;
|
|
46
68
|
type: TextInputType;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Ref } from "vue";
|
|
2
|
+
import type { DynamicTableAPI, TableBulkActions, TableRowData } from "./table";
|
|
3
|
+
interface UseBulkActionsOptions {
|
|
4
|
+
refresh?: () => void;
|
|
5
|
+
reset?: () => void;
|
|
6
|
+
}
|
|
7
|
+
export declare const useBulkActions: <T extends TableRowData>(tableData: Ref<T[]>, tableBulkActions: Ref<TableBulkActions<T>>, selected: Ref<number[]>, options?: UseBulkActionsOptions) => {
|
|
8
|
+
bulkActions: import("vue").ComputedRef<{
|
|
9
|
+
disabled: boolean | undefined;
|
|
10
|
+
onClick: (e?: Event) => void;
|
|
11
|
+
label: string;
|
|
12
|
+
icon?: import("vue").RenderFunction | import("vue").FunctionalComponent<{}, {}, any, {}> | undefined;
|
|
13
|
+
show?: boolean | undefined;
|
|
14
|
+
}[]>;
|
|
15
|
+
bulkSelectChecked: import("vue").ComputedRef<boolean>;
|
|
16
|
+
bulkSelectIndeterminate: import("vue").ComputedRef<boolean>;
|
|
17
|
+
bulkSelectOnChange: (e: Event) => void;
|
|
18
|
+
clearSelections: () => void;
|
|
19
|
+
hasBulkActions: import("vue").ComputedRef<boolean>;
|
|
20
|
+
publicMethods: DynamicTableAPI<T>;
|
|
21
|
+
selectable: import("vue").ComputedRef<any[]>;
|
|
22
|
+
selectedData: import("vue").ComputedRef<T[]>;
|
|
23
|
+
selectedOnPage: import("vue").ComputedRef<number[]>;
|
|
24
|
+
};
|
|
25
|
+
export {};
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import { Component, ComputedRef, MaybeRefOrGetter, Ref } from "vue";
|
|
2
|
-
import { type BooleanInput, type DateRangeInput, type Input, type MultiChoiceInput, type OptionsInput, type TextInputType, type TextLikeInput, type TextareaInput, type DateTimeInput, type NumericInput, type NumericInputType } from "../composables/forms";
|
|
2
|
+
import { type BooleanInput, type DateRangeInput, type FileUploadInput, type Input, type MultiChoiceInput, type OptionsInput, type TextInputType, type TextLikeInput, type TextareaInput, type DateTimeInput, type NumericInput, type NumericInputType } from "../composables/forms";
|
|
3
|
+
import { type DisplayRepeater, type Repeater } from "../composables/useRepeater";
|
|
3
4
|
/**
|
|
4
5
|
* FieldsSchema supports an Array of FieldSection(s)
|
|
5
|
-
* or simply an Array of
|
|
6
|
+
* or simply an Array of SchemaField(s)
|
|
6
7
|
*/
|
|
7
|
-
export type FieldsSchema = Array<FieldSection> | Array<
|
|
8
|
+
export type FieldsSchema = Array<FieldSection> | Array<SchemaField>;
|
|
8
9
|
/**
|
|
9
10
|
* FieldsSchemaInput type declares the supported input components in a FieldsSchema
|
|
10
11
|
*/
|
|
11
12
|
export type FieldsSchemaInput = InputField<BooleanInput, "checkbox" | "toggle" | "yes-no-radio"> | InputField<MultiChoiceInput, "multi-checkbox"> | InputField<MultiChoiceInput & {
|
|
12
13
|
customValues?: boolean;
|
|
13
|
-
}, "multi-select"> | InputField<DateRangeInput, "date-range"> | InputField<DateTimeInput, "datetime"> | InputField<NumericInput, NumericInputType> | InputField<OptionsInput, "combobox" | "radio" | "radio-cards" | "select"> | InputField<TextareaInput, "textarea"> | InputField<TextLikeInput, Exclude<TextInputType, "number">>;
|
|
14
|
+
}, "multi-select"> | InputField<DateRangeInput, "date-range"> | InputField<DateTimeInput, "datetime"> | InputField<FileUploadInput, "file-upload"> | InputField<NumericInput, NumericInputType> | InputField<OptionsInput, "combobox" | "radio" | "radio-cards" | "select"> | InputField<TextareaInput, "textarea"> | InputField<TextLikeInput, Exclude<TextInputType, "number">>;
|
|
15
|
+
/** An input with the component properties needed for display. */
|
|
16
|
+
export type DisplayInput = FieldsSchemaInput & {
|
|
17
|
+
$component: Component;
|
|
18
|
+
$props: Record<string, any>;
|
|
19
|
+
show: boolean;
|
|
20
|
+
};
|
|
21
|
+
export type SchemaField = FieldsSchemaInput | Repeater;
|
|
14
22
|
/**
|
|
15
23
|
* isTextInputType is a user defined type guard for
|
|
16
24
|
* narrowing if a FieldsSchemaInput in a FieldsSchema is a text
|
|
@@ -44,10 +52,14 @@ export declare const isInputsOnlySchema: (s: FieldsSchema) => s is FieldsSchemaI
|
|
|
44
52
|
export interface FieldSection {
|
|
45
53
|
title?: string;
|
|
46
54
|
description?: string;
|
|
47
|
-
fields: Array<
|
|
55
|
+
fields: Array<SchemaField>;
|
|
48
56
|
}
|
|
57
|
+
export type DisplayField = DisplayInput | DisplayRepeater;
|
|
58
|
+
export type DisplaySection = Omit<FieldSection, "fields"> & {
|
|
59
|
+
fields: DisplayField[];
|
|
60
|
+
};
|
|
49
61
|
export type InputFieldType = (typeof inputFieldTypes)[number];
|
|
50
|
-
export declare const inputFieldTypes: readonly ["date", "email", "month", "number", "password", "search", "tel", "text", "time", "url", "week", "money", "number", "raw-number", "checkbox", "combobox", "date-range", "datetime", "multi-checkbox", "multi-select", "radio", "radio-cards", "select", "textarea", "toggle", "yes-no-radio"];
|
|
62
|
+
export declare const inputFieldTypes: readonly ["date", "email", "month", "number", "password", "search", "tel", "text", "time", "url", "week", "money", "number", "raw-number", "checkbox", "combobox", "date-range", "datetime", "file-upload", "multi-checkbox", "multi-select", "radio", "radio-cards", "select", "textarea", "toggle", "yes-no-radio"];
|
|
51
63
|
/**
|
|
52
64
|
* InputField is the type used to declare an option of FieldsSchemaInput
|
|
53
65
|
* The Input interface is enforced against the InputFieldType to avoid applying unsupported
|
|
@@ -68,9 +80,11 @@ export type InputField<I extends Input, T extends InputFieldType> = I & {
|
|
|
68
80
|
maxlength?: number;
|
|
69
81
|
pattern?: string;
|
|
70
82
|
required?: boolean;
|
|
71
|
-
$component?: Component;
|
|
72
|
-
$props?: Record<string, any>;
|
|
73
83
|
};
|
|
84
|
+
/**
|
|
85
|
+
* Narrows an unsectioned schema containing ordinary inputs and/or repeaters.
|
|
86
|
+
*/
|
|
87
|
+
export declare const isFieldsOnlySchema: (schema: FieldsSchema) => schema is SchemaField[];
|
|
74
88
|
/**
|
|
75
89
|
* disableSchemaFields is a utility function for disabling all input fields
|
|
76
90
|
* in a FieldSchema. Useful for rendering forms as read-only.
|
|
@@ -80,8 +94,9 @@ export type InputField<I extends Input, T extends InputFieldType> = I & {
|
|
|
80
94
|
*/
|
|
81
95
|
export declare const disableSchemaFields: (schema: FieldsSchema) => FieldsSchema;
|
|
82
96
|
/**
|
|
83
|
-
* extractInputs is a utility function for extracting all
|
|
84
|
-
* entries
|
|
97
|
+
* extractInputs is a utility function for extracting all directly bound
|
|
98
|
+
* FieldsSchemaInput entries out of a FieldSchema. Repeater templates are
|
|
99
|
+
* skipped because their names are incomplete or relative until rendered.
|
|
85
100
|
*
|
|
86
101
|
* @param schema FieldsSchema
|
|
87
102
|
* @returns Array<FieldsSchemaInput>
|
|
@@ -96,5 +111,5 @@ export declare const extractInputs: (schema: FieldsSchema) => Array<FieldsSchema
|
|
|
96
111
|
* @returns { fieldSections: ComputedRef<FieldSection[]> }
|
|
97
112
|
*/
|
|
98
113
|
export declare const useFieldsSchema: (model: Ref<Record<string, any>>, schema: MaybeRefOrGetter<FieldsSchema>) => {
|
|
99
|
-
fieldSections: ComputedRef<
|
|
114
|
+
fieldSections: ComputedRef<DisplaySection[]>;
|
|
100
115
|
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { type Component, type Ref } from "vue";
|
|
2
|
+
import type { DisplayInput, FieldsSchemaInput } from "../composables/useFieldsSchema";
|
|
3
|
+
/**
|
|
4
|
+
* Removes repeater-owned properties from every supported input type while
|
|
5
|
+
* keeping required properties such as `options`.
|
|
6
|
+
*/
|
|
7
|
+
type RepeaterSchemaInput<T, K extends PropertyKey> = T extends unknown ? Omit<T, Extract<keyof T, K>> : never;
|
|
8
|
+
/**
|
|
9
|
+
* The input repeated by a field repeater. Its name and value come from the
|
|
10
|
+
* repeater definition.
|
|
11
|
+
*/
|
|
12
|
+
export type RepeaterInput = RepeaterSchemaInput<FieldsSchemaInput, "name" | "modelValue">;
|
|
13
|
+
/**
|
|
14
|
+
* An input inside a collection row. Names are relative to the row, and initial
|
|
15
|
+
* values come from the repeater model.
|
|
16
|
+
*/
|
|
17
|
+
export type RepeaterCollectionInput = RepeaterSchemaInput<FieldsSchemaInput, "modelValue">;
|
|
18
|
+
export type IndexPosition = "prefix" | "suffix";
|
|
19
|
+
export interface RepeaterBase<T> {
|
|
20
|
+
type: "repeater";
|
|
21
|
+
name: string;
|
|
22
|
+
modelValue?: T[];
|
|
23
|
+
title?: string;
|
|
24
|
+
help?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Adds an visible index to labels at the specified position
|
|
27
|
+
* e.g. Email 1, Email 2 | 1. Contact, 2. Contact
|
|
28
|
+
*/
|
|
29
|
+
indexPosition?: IndexPosition;
|
|
30
|
+
/**
|
|
31
|
+
* Limits Add/Remove actions and validates the number of items. Existing model
|
|
32
|
+
* values are shown as-is, even when they fall outside these limits.
|
|
33
|
+
*/
|
|
34
|
+
min?: number;
|
|
35
|
+
max?: number;
|
|
36
|
+
addText?: string;
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
span?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "full";
|
|
39
|
+
start?: boolean;
|
|
40
|
+
show?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Repeats one input and stores each input's value in the resulting array.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* {
|
|
47
|
+
* type: "repeater",
|
|
48
|
+
* name: "phone_numbers",
|
|
49
|
+
* title: "Phone numbers",
|
|
50
|
+
* help: "Add each number where you can be reached.",
|
|
51
|
+
* indexPosition: "suffix",
|
|
52
|
+
* addText: "Add phone number",
|
|
53
|
+
* field: { type: "tel", label: "Phone number" }
|
|
54
|
+
* }
|
|
55
|
+
*/
|
|
56
|
+
export interface FieldRepeater extends RepeaterBase<unknown> {
|
|
57
|
+
field: RepeaterInput;
|
|
58
|
+
fields?: never;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Repeats a set of named inputs and stores each row as an object. Nested
|
|
62
|
+
* repeaters are not supported.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* {
|
|
66
|
+
* type: "repeater",
|
|
67
|
+
* name: "household_members",
|
|
68
|
+
* title: "Household members",
|
|
69
|
+
* itemTitle: "Household member",
|
|
70
|
+
* help: "Include everyone who lives in your household.",
|
|
71
|
+
* indexPosition: "prefix",
|
|
72
|
+
* min: 1,
|
|
73
|
+
* fields: [
|
|
74
|
+
* { type: "text", name: "name", label: "Name" },
|
|
75
|
+
* { type: "number", name: "age", label: "Age" }
|
|
76
|
+
* ]
|
|
77
|
+
* }
|
|
78
|
+
*/
|
|
79
|
+
export interface CollectionRepeater extends RepeaterBase<Record<string, any>> {
|
|
80
|
+
field?: never;
|
|
81
|
+
itemTitle?: string;
|
|
82
|
+
fields: RepeaterCollectionInput[];
|
|
83
|
+
}
|
|
84
|
+
export type Repeater = FieldRepeater | CollectionRepeater;
|
|
85
|
+
/** A repeater with the component properties needed for display. */
|
|
86
|
+
export type DisplayRepeater = Repeater & {
|
|
87
|
+
$component: Component;
|
|
88
|
+
$props: Record<string, any>;
|
|
89
|
+
show: boolean;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Returns true when a schema field repeats one input.
|
|
93
|
+
*/
|
|
94
|
+
export declare const isFieldRepeater: (field: any) => field is FieldRepeater;
|
|
95
|
+
/**
|
|
96
|
+
* Returns true when a schema field repeats a collection of inputs.
|
|
97
|
+
*/
|
|
98
|
+
export declare const isCollectionRepeater: (field: any) => field is CollectionRepeater;
|
|
99
|
+
export interface RowField {
|
|
100
|
+
key: string;
|
|
101
|
+
input: DisplayInput;
|
|
102
|
+
}
|
|
103
|
+
export interface RepeaterRow {
|
|
104
|
+
key: string;
|
|
105
|
+
type: "field" | "collection";
|
|
106
|
+
title?: string;
|
|
107
|
+
fields: RowField[];
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Builds repeater rows for display and handles adding, updating, and removing
|
|
111
|
+
* their values. Used by useFieldsSchema.
|
|
112
|
+
*/
|
|
113
|
+
export declare const useRepeater: (model: Ref<Record<string, any>>, toDisplayInput: (input: FieldsSchemaInput, onUpdate?: ($val: any) => void) => DisplayInput) => {
|
|
114
|
+
toDisplayRepeater: (repeater: Repeater) => DisplayRepeater;
|
|
115
|
+
};
|
|
116
|
+
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Ref } from "vue";
|
|
2
2
|
import { TableColumns, TableRowsData, TableActions, DynamicTableAPI } from "./table";
|
|
3
3
|
import { ActionItemButton, ActionItemLink } from "../entry";
|
|
4
|
+
export declare const tableAPIStub: DynamicTableAPI;
|
|
4
5
|
export declare const useTable: (rowData: TableRowsData | Ref<TableRowsData>, cols: TableColumns | Ref<TableColumns>, acts: TableActions | Ref<TableActions>, exposedAPI?: DynamicTableAPI) => {
|
|
5
6
|
columns: import("vue").ComputedRef<{
|
|
6
7
|
alignment: string;
|
package/types/entry.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { HttpPromise, HttpError, QueryParams, ReqOptions, ReqPayload, Trail
|
|
|
4
4
|
import { emailPattern, looseToNumber, numericInputTypes, phonePattern, urlPattern, textInputTypes, useInputField } from "./composables/forms";
|
|
5
5
|
import { debounce as debounceFn, debounceLeading } from "./helpers/Debounce";
|
|
6
6
|
import { throttle as throttleFn } from "./helpers/Throttle";
|
|
7
|
+
import bytes from "./helpers/Bytes";
|
|
7
8
|
declare const install: Exclude<Plugin["install"], undefined>;
|
|
8
9
|
export default install;
|
|
9
10
|
export * from "./composables/index";
|
|
@@ -12,4 +13,4 @@ export { BaseAPI, isHttpCancel, isHttpError, setBaseAPIDefaults };
|
|
|
12
13
|
export type { HttpPromise, HttpError, QueryParams, ReqOptions, ReqPayload, TrailsPromise, TrailsPromisePaged, TrailsResp, TrailsRespPaged, };
|
|
13
14
|
export type * from "./composables/forms";
|
|
14
15
|
export { emailPattern, looseToNumber, phonePattern, urlPattern, numericInputTypes, textInputTypes, useInputField, };
|
|
15
|
-
export { debounceFn, debounceLeading, throttleFn };
|
|
16
|
+
export { bytes, debounceFn, debounceLeading, throttleFn };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bytes converts and formats common file sizes.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* bytes.megabytes(5) // 5242880
|
|
6
|
+
* bytes.format(5242880) // "5.0 MB"
|
|
7
|
+
*/
|
|
8
|
+
declare const bytes: {
|
|
9
|
+
kilobytes: (value: number) => number;
|
|
10
|
+
megabytes: (value: number) => number;
|
|
11
|
+
gigabytes: (value: number) => number;
|
|
12
|
+
format: (value: number) => string;
|
|
13
|
+
};
|
|
14
|
+
export default bytes;
|
|
@@ -11,8 +11,8 @@ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
11
11
|
"onUpdate:modelValue"?: ((value: string | number | null | undefined) => any) | undefined;
|
|
12
12
|
}>, {
|
|
13
13
|
label: string;
|
|
14
|
-
modelValue: string | number | null;
|
|
15
14
|
help: string;
|
|
15
|
+
modelValue: string | number | null;
|
|
16
16
|
placeholder: string;
|
|
17
17
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {
|
|
18
18
|
input: HTMLInputElement;
|
|
@@ -9,8 +9,8 @@ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {},
|
|
|
9
9
|
"onUpdate:modelValue"?: ((value: boolean | null | undefined) => any) | undefined;
|
|
10
10
|
}>, {
|
|
11
11
|
label: string;
|
|
12
|
-
modelValue: boolean | null;
|
|
13
12
|
help: string;
|
|
13
|
+
modelValue: boolean | null;
|
|
14
14
|
placeholder: string;
|
|
15
15
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
16
|
export default _default;
|
|
@@ -9,8 +9,8 @@ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {},
|
|
|
9
9
|
"onUpdate:modelValue"?: ((value: string | number | null | undefined) => any) | undefined;
|
|
10
10
|
}>, {
|
|
11
11
|
label: string;
|
|
12
|
-
modelValue: string | number | null;
|
|
13
12
|
help: string;
|
|
13
|
+
modelValue: string | number | null;
|
|
14
14
|
placeholder: string;
|
|
15
15
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
16
|
export default _default;
|
|
@@ -83,11 +83,11 @@ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
83
83
|
label: string;
|
|
84
84
|
actions: import("../../composables/forms").DateRangeAction[];
|
|
85
85
|
position: "bottom" | "bottom-start" | "bottom-end";
|
|
86
|
+
help: string;
|
|
86
87
|
modelValue: {
|
|
87
88
|
minDate: number;
|
|
88
89
|
maxDate: number;
|
|
89
90
|
};
|
|
90
|
-
help: string;
|
|
91
91
|
placeholder: string;
|
|
92
92
|
maxRange: number;
|
|
93
93
|
maxValue: Date | null;
|
|
@@ -9,8 +9,8 @@ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {},
|
|
|
9
9
|
"onUpdate:modelValue"?: ((value: string | null | undefined) => any) | undefined;
|
|
10
10
|
}>, {
|
|
11
11
|
label: string;
|
|
12
|
-
modelValue: string | null;
|
|
13
12
|
help: string;
|
|
13
|
+
modelValue: string | null;
|
|
14
14
|
placeholder: string;
|
|
15
15
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
16
|
export default _default;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type FileUploadInput } from "../../composables/forms";
|
|
2
|
+
type __VLS_Props = FileUploadInput;
|
|
3
|
+
type __VLS_PublicProps = {
|
|
4
|
+
modelValue?: FileUploadInput["modelValue"];
|
|
5
|
+
} & __VLS_Props;
|
|
6
|
+
declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
7
|
+
"update:modelValue": (value: import("../../composables/forms").FileUploadValue | undefined) => any;
|
|
8
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
9
|
+
"onUpdate:modelValue"?: ((value: import("../../composables/forms").FileUploadValue | undefined) => any) | undefined;
|
|
10
|
+
}>, {
|
|
11
|
+
label: string;
|
|
12
|
+
help: string;
|
|
13
|
+
modelValue: import("../../composables/forms").FileUploadValue;
|
|
14
|
+
placeholder: string;
|
|
15
|
+
accept: string[];
|
|
16
|
+
multiple: boolean;
|
|
17
|
+
fileField: string;
|
|
18
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
19
|
+
export default _default;
|