@stonecrop/aform 0.13.12 → 0.14.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 +2494 -2425
- package/dist/aform.js.map +1 -1
- package/dist/assets/index.css +1 -1
- package/dist/probe.tsbuildinfo +1 -0
- 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/login.css +1 -0
- package/package.json +7 -5
- 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/src/components/form/AComboBox.vue +0 -17
package/dist/src/index.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ export type * from '@stonecrop/atable/types';
|
|
|
2
2
|
export { deserializeFunction } from './utils/deserialize';
|
|
3
3
|
import type { App } from 'vue';
|
|
4
4
|
import ACheckbox from './components/form/ACheckbox.vue';
|
|
5
|
-
import AComboBox from './components/form/AComboBox.vue';
|
|
6
5
|
import ADate from './components/form/ADate.vue';
|
|
7
6
|
import ADropdown from './components/form/ADropdown.vue';
|
|
8
7
|
import ADatePicker from './components/form/ADatePicker.vue';
|
|
@@ -16,6 +15,7 @@ import AForm from './components/AForm.vue';
|
|
|
16
15
|
import AFormLink from './components/form/AFormLink.vue';
|
|
17
16
|
import ANumericInput from './components/form/ANumericInput.vue';
|
|
18
17
|
import ATextInput from './components/form/ATextInput.vue';
|
|
18
|
+
import ATextarea from './components/form/ATextarea.vue';
|
|
19
19
|
import Login from './components/utilities/Login.vue';
|
|
20
20
|
export type * from './types';
|
|
21
21
|
/**
|
|
@@ -24,5 +24,5 @@ export type * from './types';
|
|
|
24
24
|
* @public
|
|
25
25
|
*/
|
|
26
26
|
declare function install(app: App): void;
|
|
27
|
-
export { ACheckbox,
|
|
27
|
+
export { ACheckbox, ADate, ADropdown, ADatePicker, ADateRange, ADateSelection, ADuration, ADateTime, AFieldset, AFileAttach, AForm, AFormLink, ANumericInput, ATextInput, ATextarea, Login, install, };
|
|
28
28
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,yBAAyB,CAAA;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAEzD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAE9B,OAAO,SAAS,MAAM,iCAAiC,CAAA;AACvD,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,yBAAyB,CAAA;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAEzD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAE9B,OAAO,SAAS,MAAM,iCAAiC,CAAA;AACvD,OAAO,KAAK,MAAM,6BAA6B,CAAA;AAC/C,OAAO,SAAS,MAAM,iCAAiC,CAAA;AACvD,OAAO,WAAW,MAAM,mCAAmC,CAAA;AAC3D,OAAO,SAAS,MAAM,iCAAiC,CAAA;AACvD,OAAO,cAAc,MAAM,sCAAsC,CAAA;AACjE,OAAO,SAAS,MAAM,iCAAiC,CAAA;AACvD,OAAO,UAAU,MAAM,kCAAkC,CAAA;AACzD,OAAO,SAAS,MAAM,iCAAiC,CAAA;AACvD,OAAO,WAAW,MAAM,mCAAmC,CAAA;AAC3D,OAAO,KAAK,MAAM,wBAAwB,CAAA;AAC1C,OAAO,SAAS,MAAM,iCAAiC,CAAA;AACvD,OAAO,aAAa,MAAM,qCAAqC,CAAA;AAC/D,OAAO,UAAU,MAAM,kCAAkC,CAAA;AACzD,OAAO,SAAS,MAAM,iCAAiC,CAAA;AACvD,OAAO,KAAK,MAAM,kCAAkC,CAAA;AACpD,mBAAmB,SAAS,CAAA;AAE5B;;;;GAIG;AACH,iBAAS,OAAO,CAAC,GAAG,EAAE,GAAG,QAkBxB;AAED,OAAO,EACN,SAAS,EACT,KAAK,EACL,SAAS,EACT,WAAW,EACX,UAAU,EACV,cAAc,EACd,SAAS,EACT,SAAS,EACT,SAAS,EACT,WAAW,EACX,KAAK,EACL,SAAS,EACT,aAAa,EACb,UAAU,EACV,SAAS,EACT,KAAK,EACL,OAAO,GACP,CAAA"}
|
package/dist/src/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export { deserializeFunction } from './utils/deserialize';
|
|
2
2
|
import { install as installATable } from '@stonecrop/atable';
|
|
3
3
|
import ACheckbox from './components/form/ACheckbox.vue';
|
|
4
|
-
import AComboBox from './components/form/AComboBox.vue';
|
|
5
4
|
import ADate from './components/form/ADate.vue';
|
|
6
5
|
import ADropdown from './components/form/ADropdown.vue';
|
|
7
6
|
import ADatePicker from './components/form/ADatePicker.vue';
|
|
@@ -15,6 +14,7 @@ import AForm from './components/AForm.vue';
|
|
|
15
14
|
import AFormLink from './components/form/AFormLink.vue';
|
|
16
15
|
import ANumericInput from './components/form/ANumericInput.vue';
|
|
17
16
|
import ATextInput from './components/form/ATextInput.vue';
|
|
17
|
+
import ATextarea from './components/form/ATextarea.vue';
|
|
18
18
|
import Login from './components/utilities/Login.vue';
|
|
19
19
|
/**
|
|
20
20
|
* Install all AForm components
|
|
@@ -24,7 +24,6 @@ import Login from './components/utilities/Login.vue';
|
|
|
24
24
|
function install(app /* options */) {
|
|
25
25
|
app.use(installATable); // Install ATable components for use within AForm
|
|
26
26
|
app.component('ACheckbox', ACheckbox);
|
|
27
|
-
app.component('AComboBox', AComboBox);
|
|
28
27
|
app.component('ADate', ADate);
|
|
29
28
|
app.component('ADropdown', ADropdown);
|
|
30
29
|
app.component('ADatePicker', ADatePicker);
|
|
@@ -37,6 +36,7 @@ function install(app /* options */) {
|
|
|
37
36
|
app.component('AFormLink', AFormLink);
|
|
38
37
|
app.component('ANumericInput', ANumericInput);
|
|
39
38
|
app.component('ATextInput', ATextInput);
|
|
39
|
+
app.component('ATextarea', ATextarea);
|
|
40
40
|
app.component('ADuration', ADuration);
|
|
41
41
|
}
|
|
42
|
-
export { ACheckbox,
|
|
42
|
+
export { ACheckbox, ADate, ADropdown, ADatePicker, ADateRange, ADateSelection, ADuration, ADateTime, AFieldset, AFileAttach, AForm, AFormLink, ANumericInput, ATextInput, ATextarea, Login, install, };
|
|
@@ -47,6 +47,14 @@ export interface ResolvedLink {
|
|
|
47
47
|
* A resolved table — either from a Link with `noneOrMany`/`atLeastOne` cardinality,
|
|
48
48
|
* or from an inline TableField. ATable receives columns via `:schema` (ColumnSchema[])
|
|
49
49
|
* and row data via `:rows` from formData at render time.
|
|
50
|
+
*
|
|
51
|
+
* Note the key rename: an authoring `TableField` declares its columns under `columns`;
|
|
52
|
+
* `resolveSchema` moves them to `schema` here, because `schema` is the ATable prop that
|
|
53
|
+
* runs `schemaToColumns()` (ATable's own `columns` prop means already-converted
|
|
54
|
+
* `TableColumn[]`). A hand-authored table must therefore use `schema`, not `columns`.
|
|
55
|
+
*
|
|
56
|
+
* Rows are never part of the schema. AForm sources them from the data model at
|
|
57
|
+
* `dataModel[fieldname]`, so a `rows` key placed on this object is ignored.
|
|
50
58
|
* @public
|
|
51
59
|
*/
|
|
52
60
|
export interface ResolvedTable {
|
|
@@ -96,9 +104,13 @@ export interface ResolvedFieldset {
|
|
|
96
104
|
schema: ResolvedField[];
|
|
97
105
|
}
|
|
98
106
|
/**
|
|
99
|
-
* The discriminated union of all resolved field types — what AForm consumes
|
|
100
|
-
* after `resolveSchema()` has transformed the authoring `DoctypeField[]
|
|
107
|
+
* The discriminated union of all resolved field types — what AForm consumes,
|
|
108
|
+
* usually after `resolveSchema()` has transformed the authoring `DoctypeField[]`,
|
|
109
|
+
* but also valid hand-authored for view chrome with no backing doctype.
|
|
101
110
|
* Narrowed by `kind`: `'field'` | `'link'` | `'table'` | `'fieldset'`.
|
|
111
|
+
*
|
|
112
|
+
* `kind` is required — AForm dispatches on it alone and does not infer a field's
|
|
113
|
+
* type from its structure.
|
|
102
114
|
* @public
|
|
103
115
|
*/
|
|
104
116
|
export type ResolvedField = ResolvedScalar | ResolvedLink | ResolvedTable | ResolvedFieldset;
|
|
@@ -153,6 +165,13 @@ export type ComponentProps = {
|
|
|
153
165
|
errorMessage: string;
|
|
154
166
|
[key: string]: any;
|
|
155
167
|
};
|
|
168
|
+
/**
|
|
169
|
+
* Inline validation error messages to display on this field. Fed by the host
|
|
170
|
+
* (e.g. mapped from the core validation store) — the renderer stays dumb and just
|
|
171
|
+
* shows what it is given. Takes precedence over the static `validation.errorMessage`.
|
|
172
|
+
* @public
|
|
173
|
+
*/
|
|
174
|
+
errors?: string[];
|
|
156
175
|
};
|
|
157
176
|
/**
|
|
158
177
|
* The value shape for AFormLink — a linked document reference with optional display text
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAMnG;;;;;GAKG;AACH,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stonecrop/aform",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": {
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@vueuse/core": "^14.2.1",
|
|
37
37
|
"@vueuse/components": "^14.2.1",
|
|
38
|
-
"@stonecrop/atable": "0.
|
|
39
|
-
"@stonecrop/schema": "0.
|
|
40
|
-
"@stonecrop/utilities": "0.
|
|
41
|
-
"@stonecrop/themes": "0.
|
|
38
|
+
"@stonecrop/atable": "0.14.0",
|
|
39
|
+
"@stonecrop/schema": "0.14.0",
|
|
40
|
+
"@stonecrop/utilities": "0.14.0",
|
|
41
|
+
"@stonecrop/themes": "0.14.0"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"pinia": "^3.0.4",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"vitest": "^4.1.5",
|
|
63
63
|
"vue": "^3.5.33",
|
|
64
64
|
"vue-router": "^5.0.6",
|
|
65
|
+
"vue-tsc": "^3.3.6",
|
|
65
66
|
"stonecrop-rig": "0.7.0"
|
|
66
67
|
},
|
|
67
68
|
"publishConfig": {
|
|
@@ -79,6 +80,7 @@
|
|
|
79
80
|
"lint:fix": "oxlint --fix",
|
|
80
81
|
"preview": "vite preview",
|
|
81
82
|
"test": "vitest run --coverage.enabled false",
|
|
83
|
+
"test:types": "vue-tsc --noEmit",
|
|
82
84
|
"test:watch": "vitest watch",
|
|
83
85
|
"test:coverage": "vitest run --coverage",
|
|
84
86
|
"test:ui": "vitest --ui"
|
package/src/components/AForm.vue
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
:schema="componentObj.schema"
|
|
15
15
|
:label="componentObj.label"
|
|
16
16
|
:collapsible="componentObj.kind === 'fieldset' ? componentObj.collapsible : undefined"
|
|
17
|
+
:errors="errors"
|
|
17
18
|
@update:data="(val: any) => updateNestedData(componentObj.fieldname, val)" />
|
|
18
19
|
</div>
|
|
19
20
|
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
:schema="componentObj"
|
|
27
28
|
:data="dataModel[componentObj.fieldname]"
|
|
28
29
|
:mode="resolvedMode(componentObj)"
|
|
30
|
+
:errors="errors?.[componentObj.fieldname]"
|
|
29
31
|
v-bind="componentProps(componentObj)">
|
|
30
32
|
</component>
|
|
31
33
|
</template>
|
|
@@ -40,7 +42,16 @@ import type { InteractionMode } from '@stonecrop/schema'
|
|
|
40
42
|
|
|
41
43
|
const emit = defineEmits(['update:schema', 'update:data'])
|
|
42
44
|
const dataModel = defineModel<Record<string, any>>('data', { required: true })
|
|
43
|
-
const {
|
|
45
|
+
const {
|
|
46
|
+
schema,
|
|
47
|
+
mode = 'edit',
|
|
48
|
+
errors,
|
|
49
|
+
} = defineProps<{
|
|
50
|
+
schema: ResolvedField[]
|
|
51
|
+
mode?: InteractionMode
|
|
52
|
+
/** Inline validation errors keyed by fieldname. Fed by the host; the form stays store-agnostic. */
|
|
53
|
+
errors?: Record<string, string[]>
|
|
54
|
+
}>()
|
|
44
55
|
|
|
45
56
|
const isNestedSection = (componentObj: ResolvedField): componentObj is ResolvedLink | ResolvedFieldset =>
|
|
46
57
|
(componentObj.kind === 'link' || componentObj.kind === 'fieldset') &&
|
|
@@ -83,14 +94,15 @@ const componentProps = (componentObj: ResolvedField) => {
|
|
|
83
94
|
for (const [key, value] of Object.entries(componentObj)) {
|
|
84
95
|
// 'mode' is excluded here because it is handled by resolvedMode()
|
|
85
96
|
// and passed explicitly via :mode to avoid conflicting with the form-level defaults.
|
|
86
|
-
if (!['component', '
|
|
97
|
+
if (!['component', 'primaryKey', 'computed', 'language', 'hidden', 'mode', 'width'].includes(key)) {
|
|
87
98
|
propsToPass[key] = value
|
|
88
99
|
}
|
|
89
100
|
}
|
|
90
101
|
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
|
|
102
|
+
// A table sources its rows from the data model, never from the schema. `kind` is the only
|
|
103
|
+
// check: every path into AForm sets it (Zod's injectKind, Doctype.fromObject's
|
|
104
|
+
// normalizeFieldKind, and the registry), and hand-built ResolvedTable literals declare it.
|
|
105
|
+
if (componentObj.kind === 'table') {
|
|
94
106
|
propsToPass['rows'] = dataModel.value[componentObj.fieldname] || []
|
|
95
107
|
}
|
|
96
108
|
|
|
@@ -228,9 +240,9 @@ const childModels = computed(() => childModelsCache.value)
|
|
|
228
240
|
line-height: normal;
|
|
229
241
|
}
|
|
230
242
|
p.aform_error {
|
|
231
|
-
display
|
|
243
|
+
/* v-show toggles visibility per field; base display must be visible (was stuck at `none`,
|
|
244
|
+
which overrode v-show and left every field error dormant). */
|
|
232
245
|
display: inline-block;
|
|
233
|
-
display: none;
|
|
234
246
|
background: linear-gradient(var(--sc-form-background) 50%, var(--sc-input-field-background) 50%);
|
|
235
247
|
padding: 0 0.25rem;
|
|
236
248
|
margin: 0rem;
|
|
@@ -249,7 +261,6 @@ p.aform_error {
|
|
|
249
261
|
</style>
|
|
250
262
|
|
|
251
263
|
<style scoped>
|
|
252
|
-
/* @import url('@stonecrop/themes/default.css'); */
|
|
253
264
|
.aform {
|
|
254
265
|
display: flex;
|
|
255
266
|
flex-wrap: wrap;
|
|
@@ -15,17 +15,21 @@
|
|
|
15
15
|
:disabled="mode === 'read'"
|
|
16
16
|
:required="required" />
|
|
17
17
|
</span>
|
|
18
|
-
<p v-show="
|
|
18
|
+
<p v-show="errorText" class="aform_error" v-html="errorText"></p>
|
|
19
19
|
</template>
|
|
20
20
|
</div>
|
|
21
21
|
</template>
|
|
22
22
|
|
|
23
23
|
<script setup lang="ts">
|
|
24
|
-
import { InputHTMLAttributes } from 'vue'
|
|
24
|
+
import { computed, InputHTMLAttributes } from 'vue'
|
|
25
25
|
|
|
26
26
|
import { ComponentProps } from '../../types'
|
|
27
27
|
|
|
28
|
-
const { label, required, mode, uuid, validation = { errorMessage: '
|
|
28
|
+
const { label, 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 ?? '')))
|
|
32
|
+
|
|
29
33
|
const checkbox = defineModel<InputHTMLAttributes['checked']>()
|
|
30
34
|
</script>
|
|
31
35
|
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
}
|
|
21
21
|
" />
|
|
22
22
|
<label :for="uuid">{{ label }}</label>
|
|
23
|
-
<p v-show="
|
|
23
|
+
<p v-show="errorText" v-html="errorText"></p>
|
|
24
24
|
<ADateSelection
|
|
25
25
|
v-if="showPicker"
|
|
26
26
|
ref="picker"
|
|
@@ -39,7 +39,17 @@ import { onClickOutside } from '@vueuse/core'
|
|
|
39
39
|
import ADateSelection from './ADateSelection.vue'
|
|
40
40
|
import type { ComponentProps } from '../../types'
|
|
41
41
|
|
|
42
|
-
const {
|
|
42
|
+
const {
|
|
43
|
+
label = 'Date',
|
|
44
|
+
required,
|
|
45
|
+
mode,
|
|
46
|
+
uuid,
|
|
47
|
+
errors,
|
|
48
|
+
validation = { errorMessage: '' },
|
|
49
|
+
} = defineProps<ComponentProps>()
|
|
50
|
+
|
|
51
|
+
// Dynamic trigger errors take precedence over a static schema errorMessage; empty means the slot hides.
|
|
52
|
+
const errorText = computed(() => (errors?.length ? errors.join('; ') : (validation.errorMessage ?? '')))
|
|
43
53
|
|
|
44
54
|
const modelValue = defineModel<string | Date>()
|
|
45
55
|
|
|
@@ -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>
|