@stonecrop/aform 0.13.14 → 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.
@@ -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, AComboBox, ADate, ADropdown, ADatePicker, ADateRange, ADateSelection, ADuration, ADateTime, AFieldset, AFileAttach, AForm, AFormLink, ANumericInput, ATextInput, Login, install, };
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
@@ -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,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,KAAK,MAAM,kCAAkC,CAAA;AACpD,mBAAmB,SAAS,CAAA;AAE5B;;;;GAIG;AACH,iBAAS,OAAO,CAAC,GAAG,EAAE,GAAG,QAkBxB;AAED,OAAO,EACN,SAAS,EACT,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,KAAK,EACL,OAAO,GACP,CAAA"}
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, AComboBox, ADate, ADropdown, ADatePicker, ADateRange, ADateSelection, ADuration, ADateTime, AFieldset, AFileAttach, AForm, AFormLink, ANumericInput, ATextInput, Login, install, };
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":"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"}
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.13.14",
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.13.14",
39
- "@stonecrop/schema": "0.13.14",
40
- "@stonecrop/themes": "0.13.14",
41
- "@stonecrop/utilities": "0.13.14"
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"
@@ -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 { schema, mode = 'edit' } = defineProps<{ schema: ResolvedField[]; mode?: InteractionMode }>()
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', 'fieldtype', 'hidden', 'mode', 'width'].includes(key)) {
97
+ if (!['component', 'primaryKey', 'computed', 'language', 'hidden', 'mode', 'width'].includes(key)) {
87
98
  propsToPass[key] = value
88
99
  }
89
100
  }
90
101
 
91
- // kind: 'table' is the canonical check; 'columns' in componentObj is a structural fallback
92
- // for schemas passed directly to AForm without going through the registry.
93
- if (componentObj.kind === 'table' || 'columns' in componentObj) {
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: block;
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="validation.errorMessage" class="aform_error" v-html="validation.errorMessage"></p>
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: '&nbsp;' } } = defineProps<ComponentProps>()
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="validation.errorMessage" v-html="validation.errorMessage"></p>
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 { label = 'Date', required, mode, uuid, validation = { errorMessage: '&nbsp;' } } = defineProps<ComponentProps>()
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">&lt;</td>
13
+ <th colspan="5" :tabindex="-1">{{ monthAndYear }}</th>
14
+ <td id="next-month-btn" :tabindex="-1" @click="nextMonth">&gt;</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">&lt;</td>
11
- <th colspan="5" :tabindex="-1">{{ monthAndYear }}</th>
12
- <td id="next-month-btn" :tabindex="-1" @click="nextMonth">&gt;</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="validation.errorMessage" v-html="validation.errorMessage"></p>
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: '&nbsp;' } } = defineProps<ComponentProps>()
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="validation.errorMessage" class="aform_error" v-html="validation.errorMessage"></p>
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: '&nbsp;' } } = defineProps<ComponentProps>()
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="validation.errorMessage" class="aform_error" v-html="validation.errorMessage"></p>
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: '&nbsp;' } } = defineProps<ComponentProps>()
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>
@@ -84,7 +84,6 @@ function onSubmit(event: Event) {
84
84
  </script>
85
85
 
86
86
  <style scoped>
87
- /* @import url('@stonecrop/themes/default.css'); */
88
87
  .login-container {
89
88
  width: 100%;
90
89
  position: relative;