@stonecrop/aform 0.13.14 → 0.15.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/README.md +59 -9
- package/dist/aform.d.ts +24 -5
- package/dist/aform.js +2541 -2490
- package/dist/aform.js.map +1 -1
- package/dist/assets/index.css +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -3
- package/dist/src/types/index.d.ts +21 -2
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/theme/custom_themes.css +6 -0
- package/dist/theme/fields.css +84 -0
- package/dist/theme/login.css +1 -0
- package/dist/theme/purple_theme.css +9 -0
- package/package.json +28 -26
- package/src/components/AForm.vue +19 -8
- package/src/components/form/ACheckbox.vue +7 -3
- package/src/components/form/ADate.vue +12 -2
- package/src/components/form/ADatePicker.vue +85 -69
- package/src/components/form/ADateRange.vue +5 -2
- package/src/components/form/ADropdown.vue +18 -1
- package/src/components/form/ANumericInput.vue +8 -2
- package/src/components/form/ATextInput.vue +6 -3
- package/src/components/form/ATextarea.vue +31 -0
- package/src/components/utilities/Login.vue +0 -1
- package/src/index.ts +3 -3
- package/src/theme/login.css +0 -1
- package/src/types/index.ts +34 -3
- package/dist/directives/mask.js +0 -102
- package/dist/index.js +0 -31
- package/dist/types/index.js +0 -0
- package/src/components/form/AComboBox.vue +0 -17
|
@@ -2,74 +2,78 @@
|
|
|
2
2
|
<template v-if="mode === 'display' || mode === 'read'">
|
|
3
3
|
<span class="aform_display-value">{{ date ? new Date(date).toLocaleDateString() : '' }}</span>
|
|
4
4
|
<label v-if="label">{{ label }}</label>
|
|
5
|
+
<p v-show="errorText" class="aform_error" v-html="errorText"></p>
|
|
6
|
+
</template>
|
|
7
|
+
<template v-else>
|
|
8
|
+
<div ref="datepicker" class="adatepicker" tabindex="0">
|
|
9
|
+
<table>
|
|
10
|
+
<tbody>
|
|
11
|
+
<tr>
|
|
12
|
+
<td id="previous-month-btn" :tabindex="-1" @click="previousMonth"><</td>
|
|
13
|
+
<th colspan="5" :tabindex="-1">{{ monthAndYear }}</th>
|
|
14
|
+
<td id="next-month-btn" :tabindex="-1" @click="nextMonth">></td>
|
|
15
|
+
</tr>
|
|
16
|
+
<tr v-if="selectRange">
|
|
17
|
+
<td colspan="7">
|
|
18
|
+
<div class="date-input">
|
|
19
|
+
<input
|
|
20
|
+
ref="start-date-input"
|
|
21
|
+
:value="getStartDate"
|
|
22
|
+
class="date-input-start aform_input-field"
|
|
23
|
+
type="text"
|
|
24
|
+
placeholder="start date"
|
|
25
|
+
@blur="enterInputDate()"
|
|
26
|
+
@keydown="enterDate" />
|
|
27
|
+
<div>-</div>
|
|
28
|
+
<input
|
|
29
|
+
ref="end-date-input"
|
|
30
|
+
:value="getEndDate"
|
|
31
|
+
class="date-input-end aform_input-field"
|
|
32
|
+
type="text"
|
|
33
|
+
placeholder="end date"
|
|
34
|
+
@blur="enterInputDate()"
|
|
35
|
+
@keydown="enterDate" />
|
|
36
|
+
</div>
|
|
37
|
+
<!-- {{ formattedDateRange }} -->
|
|
38
|
+
</td>
|
|
39
|
+
</tr>
|
|
40
|
+
<tr class="days-header">
|
|
41
|
+
<td>M</td>
|
|
42
|
+
<td>T</td>
|
|
43
|
+
<td>W</td>
|
|
44
|
+
<td>T</td>
|
|
45
|
+
<td>F</td>
|
|
46
|
+
<td>S</td>
|
|
47
|
+
<td>S</td>
|
|
48
|
+
</tr>
|
|
49
|
+
<tr v-for="rowNo in numberOfRows" :key="rowNo">
|
|
50
|
+
<!-- the 'ref' key is currently only used for test references -->
|
|
51
|
+
<td
|
|
52
|
+
v-for="colNo in numberOfColumns"
|
|
53
|
+
ref="celldate"
|
|
54
|
+
:key="getCurrentCell(rowNo, colNo)"
|
|
55
|
+
class="date-cell"
|
|
56
|
+
:contenteditable="false"
|
|
57
|
+
:spellcheck="false"
|
|
58
|
+
:tabindex="0"
|
|
59
|
+
:class="{
|
|
60
|
+
todaysDate: isTodaysDate(getCurrentDate(rowNo, colNo)),
|
|
61
|
+
selectedDate: isSelectedDate(getCurrentDate(rowNo, colNo)),
|
|
62
|
+
withinRange: selectRange ? isInDateRange(getCurrentDate(rowNo, colNo)) : false,
|
|
63
|
+
startDate: selectRange ? isStartDate(getCurrentDate(rowNo, colNo)) : false,
|
|
64
|
+
endDate: selectRange ? isEndDate(getCurrentDate(rowNo, colNo)) : false,
|
|
65
|
+
}"
|
|
66
|
+
@click.prevent.stop="selectDate(getCurrentCell(rowNo, colNo))"
|
|
67
|
+
@keydown.enter="selectDate(getCurrentCell(rowNo, colNo))"
|
|
68
|
+
@mouseover="hoverDate(getCurrentCell(rowNo, colNo))">
|
|
69
|
+
{{ new Date(getCurrentDate(rowNo, colNo)).getDate() }}
|
|
70
|
+
</td>
|
|
71
|
+
</tr>
|
|
72
|
+
</tbody>
|
|
73
|
+
</table>
|
|
74
|
+
</div>
|
|
75
|
+
<p v-show="errorText" class="aform_error" v-html="errorText"></p>
|
|
5
76
|
</template>
|
|
6
|
-
<div v-else ref="datepicker" class="adatepicker" tabindex="0">
|
|
7
|
-
<table>
|
|
8
|
-
<tbody>
|
|
9
|
-
<tr>
|
|
10
|
-
<td id="previous-month-btn" :tabindex="-1" @click="previousMonth"><</td>
|
|
11
|
-
<th colspan="5" :tabindex="-1">{{ monthAndYear }}</th>
|
|
12
|
-
<td id="next-month-btn" :tabindex="-1" @click="nextMonth">></td>
|
|
13
|
-
</tr>
|
|
14
|
-
<tr v-if="selectRange">
|
|
15
|
-
<td colspan="7">
|
|
16
|
-
<div class="date-input">
|
|
17
|
-
<input
|
|
18
|
-
ref="start-date-input"
|
|
19
|
-
:value="getStartDate"
|
|
20
|
-
class="date-input-start aform_input-field"
|
|
21
|
-
type="text"
|
|
22
|
-
placeholder="start date"
|
|
23
|
-
@blur="enterInputDate()"
|
|
24
|
-
@keydown="enterDate" />
|
|
25
|
-
<div>-</div>
|
|
26
|
-
<input
|
|
27
|
-
ref="end-date-input"
|
|
28
|
-
:value="getEndDate"
|
|
29
|
-
class="date-input-end aform_input-field"
|
|
30
|
-
type="text"
|
|
31
|
-
placeholder="end date"
|
|
32
|
-
@blur="enterInputDate()"
|
|
33
|
-
@keydown="enterDate" />
|
|
34
|
-
</div>
|
|
35
|
-
<!-- {{ formattedDateRange }} -->
|
|
36
|
-
</td>
|
|
37
|
-
</tr>
|
|
38
|
-
<tr class="days-header">
|
|
39
|
-
<td>M</td>
|
|
40
|
-
<td>T</td>
|
|
41
|
-
<td>W</td>
|
|
42
|
-
<td>T</td>
|
|
43
|
-
<td>F</td>
|
|
44
|
-
<td>S</td>
|
|
45
|
-
<td>S</td>
|
|
46
|
-
</tr>
|
|
47
|
-
<tr v-for="rowNo in numberOfRows" :key="rowNo">
|
|
48
|
-
<!-- the 'ref' key is currently only used for test references -->
|
|
49
|
-
<td
|
|
50
|
-
v-for="colNo in numberOfColumns"
|
|
51
|
-
ref="celldate"
|
|
52
|
-
:key="getCurrentCell(rowNo, colNo)"
|
|
53
|
-
class="date-cell"
|
|
54
|
-
:contenteditable="false"
|
|
55
|
-
:spellcheck="false"
|
|
56
|
-
:tabindex="0"
|
|
57
|
-
:class="{
|
|
58
|
-
todaysDate: isTodaysDate(getCurrentDate(rowNo, colNo)),
|
|
59
|
-
selectedDate: isSelectedDate(getCurrentDate(rowNo, colNo)),
|
|
60
|
-
withinRange: selectRange ? isInDateRange(getCurrentDate(rowNo, colNo)) : false,
|
|
61
|
-
startDate: selectRange ? isStartDate(getCurrentDate(rowNo, colNo)) : false,
|
|
62
|
-
endDate: selectRange ? isEndDate(getCurrentDate(rowNo, colNo)) : false,
|
|
63
|
-
}"
|
|
64
|
-
@click.prevent.stop="selectDate(getCurrentCell(rowNo, colNo))"
|
|
65
|
-
@keydown.enter="selectDate(getCurrentCell(rowNo, colNo))"
|
|
66
|
-
@mouseover="hoverDate(getCurrentCell(rowNo, colNo))">
|
|
67
|
-
{{ new Date(getCurrentDate(rowNo, colNo)).getDate() }}
|
|
68
|
-
</td>
|
|
69
|
-
</tr>
|
|
70
|
-
</tbody>
|
|
71
|
-
</table>
|
|
72
|
-
</div>
|
|
73
77
|
</template>
|
|
74
78
|
|
|
75
79
|
<script setup lang="ts">
|
|
@@ -82,7 +86,10 @@ import type { ComponentProps } from '../../types'
|
|
|
82
86
|
const numberOfRows = 6
|
|
83
87
|
const numberOfColumns = 7
|
|
84
88
|
|
|
85
|
-
const { mode, label, selectRange = false } = defineProps<ComponentProps>()
|
|
89
|
+
const { mode, label, selectRange = false, errors, validation = { errorMessage: '' } } = defineProps<ComponentProps>()
|
|
90
|
+
|
|
91
|
+
// Dynamic trigger errors take precedence over a static schema errorMessage; empty means the slot hides.
|
|
92
|
+
const errorText = computed(() => (errors?.length ? errors.join('; ') : (validation.errorMessage ?? '')))
|
|
86
93
|
|
|
87
94
|
const date = defineModel<number | Date>({ default: new Date() })
|
|
88
95
|
const selectedDate = ref(new Date(date.value))
|
|
@@ -351,7 +358,6 @@ defineExpose({ currentMonth, currentYear, selectedDate })
|
|
|
351
358
|
</script>
|
|
352
359
|
|
|
353
360
|
<style scoped>
|
|
354
|
-
/* @import url('@stonecrop/themes/default.css'); */
|
|
355
361
|
.adatepicker {
|
|
356
362
|
font-size: var(--sc-table-font-size);
|
|
357
363
|
display: inline-table;
|
|
@@ -432,4 +438,14 @@ defineExpose({ currentMonth, currentYear, selectedDate })
|
|
|
432
438
|
width: 50%;
|
|
433
439
|
padding: 2px;
|
|
434
440
|
}
|
|
441
|
+
|
|
442
|
+
/* Keep the field error in-flow below the calendar. The shared .aform_error is absolutely
|
|
443
|
+
positioned against a .aform_form-element anchor, which this grid component does not use. */
|
|
444
|
+
p.aform_error {
|
|
445
|
+
position: static;
|
|
446
|
+
display: block;
|
|
447
|
+
color: var(--sc-brand-danger, red);
|
|
448
|
+
font-size: 0.7rem;
|
|
449
|
+
margin: 0.25rem 0 0;
|
|
450
|
+
}
|
|
435
451
|
</style>
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
@click="openPicker" />
|
|
18
18
|
<label :for="uuid">{{ label }}</label>
|
|
19
19
|
|
|
20
|
-
<p v-show="
|
|
20
|
+
<p v-show="errorText" v-html="errorText"></p>
|
|
21
21
|
|
|
22
22
|
<ADateSelection
|
|
23
23
|
v-if="showPicker"
|
|
@@ -38,7 +38,10 @@ import type { ComponentProps } from '../../types'
|
|
|
38
38
|
|
|
39
39
|
const fmt = (d: string) => new Date(d).toLocaleDateString()
|
|
40
40
|
|
|
41
|
-
const { label = 'Date Range', mode, uuid, validation = { errorMessage: '
|
|
41
|
+
const { label = 'Date Range', mode, uuid, errors, validation = { errorMessage: '' } } = defineProps<ComponentProps>()
|
|
42
|
+
|
|
43
|
+
// Dynamic trigger errors take precedence over a static schema errorMessage; empty means the slot hides.
|
|
44
|
+
const errorText = computed(() => (errors?.length ? errors.join('; ') : (validation.errorMessage ?? '')))
|
|
42
45
|
|
|
43
46
|
export interface DateRangeValue {
|
|
44
47
|
start_date: string | null
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<div v-if="mode === 'display'" class="input-wrapper">
|
|
3
3
|
<span class="aform_display-value">{{ search ?? '' }}</span>
|
|
4
4
|
<label>{{ label }}</label>
|
|
5
|
+
<p v-show="errorText" class="aform_error" v-html="errorText"></p>
|
|
5
6
|
</div>
|
|
6
7
|
<div v-else v-on-click-outside="onClickOutside" class="autocomplete" :class="{ isOpen: dropdown.open }">
|
|
7
8
|
<div class="input-wrapper">
|
|
@@ -31,12 +32,13 @@
|
|
|
31
32
|
</ul>
|
|
32
33
|
<label>{{ label }}</label>
|
|
33
34
|
</div>
|
|
35
|
+
<p v-show="errorText" class="aform_error" v-html="errorText"></p>
|
|
34
36
|
</div>
|
|
35
37
|
</template>
|
|
36
38
|
|
|
37
39
|
<script setup lang="ts">
|
|
38
40
|
import { vOnClickOutside } from '@vueuse/components'
|
|
39
|
-
import { reactive, ref } from 'vue'
|
|
41
|
+
import { computed, reactive, ref } from 'vue'
|
|
40
42
|
|
|
41
43
|
import type { ComponentProps } from '../../types'
|
|
42
44
|
|
|
@@ -46,6 +48,8 @@ const {
|
|
|
46
48
|
isAsync = false,
|
|
47
49
|
filterFunction = undefined,
|
|
48
50
|
mode,
|
|
51
|
+
errors,
|
|
52
|
+
validation = { errorMessage: '' },
|
|
49
53
|
} = defineProps<
|
|
50
54
|
ComponentProps & {
|
|
51
55
|
options?: string[]
|
|
@@ -53,6 +57,9 @@ const {
|
|
|
53
57
|
filterFunction?: (search: string) => string[] | Promise<string[]>
|
|
54
58
|
}
|
|
55
59
|
>()
|
|
60
|
+
|
|
61
|
+
// Dynamic trigger errors take precedence over a static schema errorMessage; empty means the slot hides.
|
|
62
|
+
const errorText = computed(() => (errors?.length ? errors.join('; ') : (validation.errorMessage ?? '')))
|
|
56
63
|
const search = defineModel<string>()
|
|
57
64
|
|
|
58
65
|
// tracks the last explicitly-committed value so outside-click reverts instead of clears
|
|
@@ -219,4 +226,14 @@ label {
|
|
|
219
226
|
background-color: var(--sc-row-color-zebra-light);
|
|
220
227
|
color: var(--sc-input-active-border-color);
|
|
221
228
|
}
|
|
229
|
+
|
|
230
|
+
/* Keep the field error in-flow below the control. The shared .aform_error is absolutely
|
|
231
|
+
positioned against a .aform_form-element anchor, which this component does not use. */
|
|
232
|
+
p.aform_error {
|
|
233
|
+
position: static;
|
|
234
|
+
display: block;
|
|
235
|
+
color: var(--sc-brand-danger, red);
|
|
236
|
+
font-size: 0.7rem;
|
|
237
|
+
margin: 0.25rem 0 0;
|
|
238
|
+
}
|
|
222
239
|
</style>
|
|
@@ -13,14 +13,20 @@
|
|
|
13
13
|
:disabled="mode === 'read'"
|
|
14
14
|
:required="required" />
|
|
15
15
|
<label class="aform_field-label" :for="uuid">{{ label }}</label>
|
|
16
|
-
<p v-show="
|
|
16
|
+
<p v-show="errorText" class="aform_error" v-html="errorText"></p>
|
|
17
17
|
</template>
|
|
18
18
|
</div>
|
|
19
19
|
</template>
|
|
20
20
|
|
|
21
21
|
<script setup lang="ts">
|
|
22
|
+
import { computed } from 'vue'
|
|
23
|
+
|
|
22
24
|
import { ComponentProps } from '../../types'
|
|
23
25
|
|
|
24
|
-
const { label, required, mode, uuid, validation = { errorMessage: '
|
|
26
|
+
const { label, required, mode, uuid, errors, validation = { errorMessage: '' } } = defineProps<ComponentProps>()
|
|
27
|
+
|
|
28
|
+
// Dynamic trigger errors take precedence over a static schema errorMessage; empty means the slot hides.
|
|
29
|
+
const errorText = computed(() => (errors?.length ? errors.join('; ') : (validation.errorMessage ?? '')))
|
|
30
|
+
|
|
25
31
|
const inputNumber = defineModel<number>()
|
|
26
32
|
</script>
|
|
@@ -14,18 +14,21 @@
|
|
|
14
14
|
:maxlength="mask ? (maskFilled ? mask.length : undefined) : undefined"
|
|
15
15
|
:required="required" />
|
|
16
16
|
<label class="aform_field-label" :for="uuid">{{ label }} </label>
|
|
17
|
-
<p v-show="
|
|
17
|
+
<p v-show="errorText" class="aform_error" v-html="errorText"></p>
|
|
18
18
|
</template>
|
|
19
19
|
</div>
|
|
20
20
|
</template>
|
|
21
21
|
|
|
22
22
|
<script setup lang="ts">
|
|
23
|
-
import { /* inject, */ ref } from 'vue'
|
|
23
|
+
import { /* inject, */ computed, ref } from 'vue'
|
|
24
24
|
|
|
25
25
|
import { useStringMask as vMask } from '../../directives/mask'
|
|
26
26
|
import { ComponentProps } from '../../types'
|
|
27
27
|
|
|
28
|
-
const { label, mask, required, mode, uuid, validation = { errorMessage: '
|
|
28
|
+
const { label, mask, required, mode, uuid, errors, validation = { errorMessage: '' } } = defineProps<ComponentProps>()
|
|
29
|
+
|
|
30
|
+
// Dynamic trigger errors take precedence over a static schema errorMessage; empty means the slot hides.
|
|
31
|
+
const errorText = computed(() => (errors?.length ? errors.join('; ') : (validation.errorMessage ?? '')))
|
|
29
32
|
|
|
30
33
|
// TODO: setup maskFilled as a computed property
|
|
31
34
|
const maskFilled = ref(true)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="aform_form-element">
|
|
3
|
+
<template v-if="mode === 'display'">
|
|
4
|
+
<span class="aform_display-value">{{ inputText ?? '' }}</span>
|
|
5
|
+
<label class="aform_field-label">{{ label }}</label>
|
|
6
|
+
</template>
|
|
7
|
+
<template v-else>
|
|
8
|
+
<textarea
|
|
9
|
+
:id="uuid"
|
|
10
|
+
v-model="inputText"
|
|
11
|
+
class="aform_input-field aform_textarea"
|
|
12
|
+
:disabled="mode === 'read'"
|
|
13
|
+
:required="required"></textarea>
|
|
14
|
+
<label class="aform_field-label" :for="uuid">{{ label }} </label>
|
|
15
|
+
<p v-show="errorText" class="aform_error" v-html="errorText"></p>
|
|
16
|
+
</template>
|
|
17
|
+
</div>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script setup lang="ts">
|
|
21
|
+
import { computed } from 'vue'
|
|
22
|
+
|
|
23
|
+
import { ComponentProps } from '../../types'
|
|
24
|
+
|
|
25
|
+
const { label, required, mode, uuid, errors, validation = { errorMessage: '' } } = defineProps<ComponentProps>()
|
|
26
|
+
|
|
27
|
+
// Dynamic trigger errors take precedence over a static schema errorMessage; empty means the slot hides.
|
|
28
|
+
const errorText = computed(() => (errors?.length ? errors.join('; ') : (validation.errorMessage ?? '')))
|
|
29
|
+
|
|
30
|
+
const inputText = defineModel<number | string>()
|
|
31
|
+
</script>
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { install as installATable } from '@stonecrop/atable'
|
|
|
4
4
|
import type { App } from 'vue'
|
|
5
5
|
|
|
6
6
|
import ACheckbox from './components/form/ACheckbox.vue'
|
|
7
|
-
import AComboBox from './components/form/AComboBox.vue'
|
|
8
7
|
import ADate from './components/form/ADate.vue'
|
|
9
8
|
import ADropdown from './components/form/ADropdown.vue'
|
|
10
9
|
import ADatePicker from './components/form/ADatePicker.vue'
|
|
@@ -18,6 +17,7 @@ import AForm from './components/AForm.vue'
|
|
|
18
17
|
import AFormLink from './components/form/AFormLink.vue'
|
|
19
18
|
import ANumericInput from './components/form/ANumericInput.vue'
|
|
20
19
|
import ATextInput from './components/form/ATextInput.vue'
|
|
20
|
+
import ATextarea from './components/form/ATextarea.vue'
|
|
21
21
|
import Login from './components/utilities/Login.vue'
|
|
22
22
|
export type * from './types'
|
|
23
23
|
|
|
@@ -30,7 +30,6 @@ function install(app: App /* options */) {
|
|
|
30
30
|
app.use(installATable) // Install ATable components for use within AForm
|
|
31
31
|
|
|
32
32
|
app.component('ACheckbox', ACheckbox)
|
|
33
|
-
app.component('AComboBox', AComboBox)
|
|
34
33
|
app.component('ADate', ADate)
|
|
35
34
|
app.component('ADropdown', ADropdown)
|
|
36
35
|
app.component('ADatePicker', ADatePicker)
|
|
@@ -43,12 +42,12 @@ function install(app: App /* options */) {
|
|
|
43
42
|
app.component('AFormLink', AFormLink)
|
|
44
43
|
app.component('ANumericInput', ANumericInput)
|
|
45
44
|
app.component('ATextInput', ATextInput)
|
|
45
|
+
app.component('ATextarea', ATextarea)
|
|
46
46
|
app.component('ADuration', ADuration)
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
export {
|
|
50
50
|
ACheckbox,
|
|
51
|
-
AComboBox,
|
|
52
51
|
ADate,
|
|
53
52
|
ADropdown,
|
|
54
53
|
ADatePicker,
|
|
@@ -62,6 +61,7 @@ export {
|
|
|
62
61
|
AFormLink,
|
|
63
62
|
ANumericInput,
|
|
64
63
|
ATextInput,
|
|
64
|
+
ATextarea,
|
|
65
65
|
Login,
|
|
66
66
|
install,
|
|
67
67
|
}
|
package/src/theme/login.css
CHANGED
package/src/types/index.ts
CHANGED
|
@@ -14,7 +14,18 @@ export type { InteractionMode } from '@stonecrop/schema'
|
|
|
14
14
|
|
|
15
15
|
// ---------------------------------------------------------------------------
|
|
16
16
|
// ResolvedField — the output-space type that AForm consumes
|
|
17
|
-
//
|
|
17
|
+
//
|
|
18
|
+
// Usually produced by resolveSchema() from an authoring-space DoctypeField[].
|
|
19
|
+
// It may also be hand-authored, for view chrome that has no backing doctype
|
|
20
|
+
// (see Desktop.vue's doctype list) — annotate such literals with `satisfies
|
|
21
|
+
// ResolvedTable`/`satisfies ResolvedField` so they stay checked.
|
|
22
|
+
//
|
|
23
|
+
// Authoring space and output space are NOT interchangeable. The trap is tables:
|
|
24
|
+
// an authoring TableField carries its columns under `columns`, while resolveSchema
|
|
25
|
+
// renames that key to `schema` (registry.ts) because `schema` is the ATable prop
|
|
26
|
+
// that runs schemaToColumns(). Hand-authoring `columns` here — or feeding an
|
|
27
|
+
// authoring-space field straight to AForm — produces a field AForm does not
|
|
28
|
+
// recognise as a table.
|
|
18
29
|
// ---------------------------------------------------------------------------
|
|
19
30
|
|
|
20
31
|
/**
|
|
@@ -60,6 +71,14 @@ export interface ResolvedLink {
|
|
|
60
71
|
* A resolved table — either from a Link with `noneOrMany`/`atLeastOne` cardinality,
|
|
61
72
|
* or from an inline TableField. ATable receives columns via `:schema` (ColumnSchema[])
|
|
62
73
|
* and row data via `:rows` from formData at render time.
|
|
74
|
+
*
|
|
75
|
+
* Note the key rename: an authoring `TableField` declares its columns under `columns`;
|
|
76
|
+
* `resolveSchema` moves them to `schema` here, because `schema` is the ATable prop that
|
|
77
|
+
* runs `schemaToColumns()` (ATable's own `columns` prop means already-converted
|
|
78
|
+
* `TableColumn[]`). A hand-authored table must therefore use `schema`, not `columns`.
|
|
79
|
+
*
|
|
80
|
+
* Rows are never part of the schema. AForm sources them from the data model at
|
|
81
|
+
* `dataModel[fieldname]`, so a `rows` key placed on this object is ignored.
|
|
63
82
|
* @public
|
|
64
83
|
*/
|
|
65
84
|
export interface ResolvedTable {
|
|
@@ -111,9 +130,13 @@ export interface ResolvedFieldset {
|
|
|
111
130
|
}
|
|
112
131
|
|
|
113
132
|
/**
|
|
114
|
-
* The discriminated union of all resolved field types — what AForm consumes
|
|
115
|
-
* after `resolveSchema()` has transformed the authoring `DoctypeField[]
|
|
133
|
+
* The discriminated union of all resolved field types — what AForm consumes,
|
|
134
|
+
* usually after `resolveSchema()` has transformed the authoring `DoctypeField[]`,
|
|
135
|
+
* but also valid hand-authored for view chrome with no backing doctype.
|
|
116
136
|
* Narrowed by `kind`: `'field'` | `'link'` | `'table'` | `'fieldset'`.
|
|
137
|
+
*
|
|
138
|
+
* `kind` is required — AForm dispatches on it alone and does not infer a field's
|
|
139
|
+
* type from its structure.
|
|
117
140
|
* @public
|
|
118
141
|
*/
|
|
119
142
|
export type ResolvedField = ResolvedScalar | ResolvedLink | ResolvedTable | ResolvedFieldset
|
|
@@ -182,6 +205,14 @@ export type ComponentProps = {
|
|
|
182
205
|
|
|
183
206
|
[key: string]: any
|
|
184
207
|
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Inline validation error messages to display on this field. Fed by the host
|
|
211
|
+
* (e.g. mapped from the core validation store) — the renderer stays dumb and just
|
|
212
|
+
* shows what it is given. Takes precedence over the static `validation.errorMessage`.
|
|
213
|
+
* @public
|
|
214
|
+
*/
|
|
215
|
+
errors?: string[]
|
|
185
216
|
}
|
|
186
217
|
|
|
187
218
|
/**
|
package/dist/directives/mask.js
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Extracts a mask function from a stringified function
|
|
3
|
-
* @param mask - Mask string
|
|
4
|
-
* @returns Mask function
|
|
5
|
-
*/
|
|
6
|
-
function extractMaskFn(mask) {
|
|
7
|
-
try {
|
|
8
|
-
// eslint-disable-next-line @typescript-eslint/no-implied-eval, @typescript-eslint/no-unsafe-call
|
|
9
|
-
return Function(`"use strict";return (${mask})`)();
|
|
10
|
-
}
|
|
11
|
-
catch (error) {
|
|
12
|
-
if (error instanceof ReferenceError) {
|
|
13
|
-
// assume mask is a string
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Gets the mask for a given directive binding
|
|
19
|
-
* @param binding - Binding object from directive hook
|
|
20
|
-
* @returns Mask string
|
|
21
|
-
*/
|
|
22
|
-
function getMask(binding) {
|
|
23
|
-
const mask = binding.value;
|
|
24
|
-
if (!mask)
|
|
25
|
-
return undefined;
|
|
26
|
-
const maskFn = extractMaskFn(mask);
|
|
27
|
-
if (maskFn) {
|
|
28
|
-
// TODO: (state) replace with state management;
|
|
29
|
-
// pass the entire form/table data to the function
|
|
30
|
-
const locale = binding.instance?.['locale'];
|
|
31
|
-
return maskFn(locale);
|
|
32
|
-
}
|
|
33
|
-
return mask;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Unmasks the input string
|
|
37
|
-
* @param input - Input string
|
|
38
|
-
* @param maskToken - Mask token character
|
|
39
|
-
* @returns Unmasked input string
|
|
40
|
-
*/
|
|
41
|
-
function unmaskInput(input, maskToken) {
|
|
42
|
-
if (!maskToken) {
|
|
43
|
-
maskToken = '#';
|
|
44
|
-
}
|
|
45
|
-
let unmaskedInput = input;
|
|
46
|
-
const maskChars = [maskToken, '/', '-', '(', ')', ' '];
|
|
47
|
-
for (const char of maskChars) {
|
|
48
|
-
unmaskedInput = unmaskedInput.replaceAll(char, '');
|
|
49
|
-
}
|
|
50
|
-
return unmaskedInput;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Fills the mask with the input string
|
|
54
|
-
* @param input - Input string
|
|
55
|
-
* @param mask - Mask string
|
|
56
|
-
* @param maskToken - Mask token character
|
|
57
|
-
* @returns Masked input string
|
|
58
|
-
*/
|
|
59
|
-
function fillMask(input, mask, maskToken) {
|
|
60
|
-
if (!maskToken) {
|
|
61
|
-
maskToken = '#';
|
|
62
|
-
}
|
|
63
|
-
let replacement = mask;
|
|
64
|
-
for (const inputChar of input) {
|
|
65
|
-
const replaceIndex = replacement.indexOf(maskToken);
|
|
66
|
-
if (replaceIndex !== -1) {
|
|
67
|
-
const prefix = replacement.substring(0, replaceIndex);
|
|
68
|
-
const suffix = replacement.substring(replaceIndex + 1);
|
|
69
|
-
replacement = prefix + inputChar + suffix;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return replacement.slice(0, mask.length);
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Applies a mask to an input element
|
|
76
|
-
* @param el - Input element
|
|
77
|
-
* @param binding - Binding object from directive hook
|
|
78
|
-
* @returns void
|
|
79
|
-
* @public
|
|
80
|
-
*/
|
|
81
|
-
export function useStringMask(el, binding) {
|
|
82
|
-
const mask = getMask(binding);
|
|
83
|
-
if (!mask)
|
|
84
|
-
return;
|
|
85
|
-
const maskToken = '#';
|
|
86
|
-
const inputText = el.value;
|
|
87
|
-
// process input value with mask
|
|
88
|
-
const unmaskedInput = unmaskInput(inputText, maskToken);
|
|
89
|
-
if (unmaskedInput) {
|
|
90
|
-
const replacement = fillMask(unmaskedInput, mask, maskToken);
|
|
91
|
-
// TODO: (state) this is very opinionated;
|
|
92
|
-
// most likely fixed with state management;
|
|
93
|
-
// a better way could be to emit back to instance;
|
|
94
|
-
if (binding.instance?.['maskFilled']) {
|
|
95
|
-
binding.instance['maskFilled'] = !replacement.includes(maskToken);
|
|
96
|
-
}
|
|
97
|
-
el.value = replacement;
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
el.value = mask;
|
|
101
|
-
}
|
|
102
|
-
}
|
package/dist/index.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { install as installATable } from '@stonecrop/atable';
|
|
2
|
-
import ACheckbox from './components/form/ACheckbox.vue';
|
|
3
|
-
import AComboBox from './components/form/AComboBox.vue';
|
|
4
|
-
import ADate from './components/form/ADate.vue';
|
|
5
|
-
import ADropdown from './components/form/ADropdown.vue';
|
|
6
|
-
import ADatePicker from './components/form/ADatePicker.vue';
|
|
7
|
-
import AFieldset from './components/form/AFieldset.vue';
|
|
8
|
-
import AFileAttach from './components/form/AFileAttach.vue';
|
|
9
|
-
import AForm from './components/AForm.vue';
|
|
10
|
-
import ANumericInput from './components/form/ANumericInput.vue';
|
|
11
|
-
import ATextInput from './components/form/ATextInput.vue';
|
|
12
|
-
import Login from './components/utilities/Login.vue';
|
|
13
|
-
/**
|
|
14
|
-
* Install all AForm components
|
|
15
|
-
* @param app - Vue app instance
|
|
16
|
-
* @public
|
|
17
|
-
*/
|
|
18
|
-
function install(app /* options */) {
|
|
19
|
-
app.use(installATable); // Install ATable components for use within AForm
|
|
20
|
-
app.component('ACheckbox', ACheckbox);
|
|
21
|
-
app.component('AComboBox', AComboBox);
|
|
22
|
-
app.component('ADate', ADate);
|
|
23
|
-
app.component('ADropdown', ADropdown);
|
|
24
|
-
app.component('ADatePicker', ADatePicker);
|
|
25
|
-
app.component('AFieldset', AFieldset);
|
|
26
|
-
app.component('AFileAttach', AFileAttach);
|
|
27
|
-
app.component('AForm', AForm);
|
|
28
|
-
app.component('ANumericInput', ANumericInput);
|
|
29
|
-
app.component('ATextInput', ATextInput);
|
|
30
|
-
}
|
|
31
|
-
export { ACheckbox, AComboBox, ADate, ADropdown, ADatePicker, AFieldset, AFileAttach, AForm, ANumericInput, ATextInput, Login, install, };
|
package/dist/types/index.js
DELETED
|
File without changes
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<ATableModal :event="event" :cell-data="cellData" class="amodal">
|
|
3
|
-
<div>
|
|
4
|
-
<input type="text" />
|
|
5
|
-
<input type="text" />
|
|
6
|
-
<input type="text" />
|
|
7
|
-
</div>
|
|
8
|
-
</ATableModal>
|
|
9
|
-
</template>
|
|
10
|
-
|
|
11
|
-
<script setup lang="ts">
|
|
12
|
-
import type { ComponentProps } from '../../types'
|
|
13
|
-
|
|
14
|
-
// TODO: change props from individual elements to the store object
|
|
15
|
-
// TODO: implement full mode/display support
|
|
16
|
-
defineProps<ComponentProps & { event?: any; cellData?: any; tableID?: string }>()
|
|
17
|
-
</script>
|