@pienter/ui 0.8.0 → 0.9.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/CHANGELOG.md +17 -0
- package/README.md +4 -0
- package/composables/useUrlTab.ts +38 -0
- package/package.json +2 -1
- package/utils/cms/index.ts +21 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.9.0 - 2026-09-15
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `useUrlTab(keys, defaultKey, param = 'tab')` at
|
|
10
|
+
`@pienter/ui/composables/useUrlTab` keeps the active `Tabs` key in the URL
|
|
11
|
+
query, the single source of truth, so a shared link or a reload opens the
|
|
12
|
+
same tab. It returns a writable computed for `v-model`: the default key
|
|
13
|
+
drops the parameter, an unknown value falls back to it, and writes go
|
|
14
|
+
through `router.replace` so tab switches stay out of the back stack and
|
|
15
|
+
other query parameters survive. Requires the optional `vue-router` peer,
|
|
16
|
+
like `useUrlSort`.
|
|
17
|
+
- `formFailure(cause, fallback)` in `@pienter/ui/utils/cms` turns a caught
|
|
18
|
+
save error into RecordForm's `{ issues, errors }`: a value carrying the
|
|
19
|
+
`ErrorResponse` envelope contributes its field issues and leaves `errors`
|
|
20
|
+
empty; otherwise `errors` holds the caught `Error` message or `fallback`.
|
|
21
|
+
|
|
5
22
|
## 0.8.0 - 2026-09-15
|
|
6
23
|
|
|
7
24
|
### Breaking
|
package/README.md
CHANGED
|
@@ -92,6 +92,10 @@ declarations, endpoint functions and saving. Use RecordForm’s `fields` slot an
|
|
|
92
92
|
RecordFields to put groups in sidebars and tab panels while retaining one form.
|
|
93
93
|
BlockEditor accepts `v-model`, block creation factories and a `block` slot; it
|
|
94
94
|
provides drag-and-drop, keyboard movement, add/remove and collapse controls.
|
|
95
|
+
`useUrlTab` from `@pienter/ui/composables/useUrlTab` keeps the active tab key in
|
|
96
|
+
the query string for `v-model` on Tabs, and `formFailure(cause, fallback)` from
|
|
97
|
+
`@pienter/ui/utils/cms` turns a caught save error into RecordForm's `issues` and
|
|
98
|
+
`errors`.
|
|
95
99
|
|
|
96
100
|
The [CMS guide](../../docs/cms/README.md) contains working examples. The
|
|
97
101
|
[backend contract](../../docs/cms/backend-contract.md) defines query parameters,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { computed, type WritableComputedRef } from 'vue';
|
|
2
|
+
import { useRoute, useRouter } from 'vue-router';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Keeps the active `Tabs` key in the URL query, the single source of truth, so a shared
|
|
6
|
+
* link or a reload opens the same tab. Bind with `v-model`. The default key drops the
|
|
7
|
+
* parameter and an unknown value falls back to it; writes go through `router.replace`,
|
|
8
|
+
* so tab switches stay out of the back stack and other query parameters survive.
|
|
9
|
+
* Requires the optional `vue-router` peer dependency.
|
|
10
|
+
*
|
|
11
|
+
* @param keys - the tab keys the view renders.
|
|
12
|
+
* @param defaultKey - key used when the parameter is absent or unknown.
|
|
13
|
+
* @param param - query parameter name; defaults to `'tab'`.
|
|
14
|
+
*/
|
|
15
|
+
export function useUrlTab(
|
|
16
|
+
keys: readonly string[],
|
|
17
|
+
defaultKey: string,
|
|
18
|
+
param = 'tab',
|
|
19
|
+
): WritableComputedRef<string> {
|
|
20
|
+
const route = useRoute();
|
|
21
|
+
const router = useRouter();
|
|
22
|
+
|
|
23
|
+
return computed<string>({
|
|
24
|
+
get: () => {
|
|
25
|
+
const raw = route.query[param];
|
|
26
|
+
const value = Array.isArray(raw) ? raw[0] : raw;
|
|
27
|
+
return typeof value === 'string' && keys.includes(value)
|
|
28
|
+
? value
|
|
29
|
+
: defaultKey;
|
|
30
|
+
},
|
|
31
|
+
set: (key) => {
|
|
32
|
+
const query = { ...route.query };
|
|
33
|
+
if (key === defaultKey) delete query[param];
|
|
34
|
+
else query[param] = key;
|
|
35
|
+
void router.replace({ query });
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pienter/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Shared Pienter UI components, styles, icons, and browser utilities.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
"./composables/useMenu": "./composables/useMenu.ts",
|
|
88
88
|
"./composables/usePopover": "./composables/usePopover.ts",
|
|
89
89
|
"./composables/useUrlSort": "./composables/useUrlSort.ts",
|
|
90
|
+
"./composables/useUrlTab": "./composables/useUrlTab.ts",
|
|
90
91
|
"./icons": "./icons/index.ts",
|
|
91
92
|
"./icons/*": "./icons/*",
|
|
92
93
|
"./utils": "./utils/index.ts",
|
package/utils/cms/index.ts
CHANGED
|
@@ -281,3 +281,24 @@ export function errorsFor(
|
|
|
281
281
|
)
|
|
282
282
|
.flatMap((issue) => issue.messages);
|
|
283
283
|
}
|
|
284
|
+
|
|
285
|
+
export interface FormFailure {
|
|
286
|
+
issues: ValidationIssue[];
|
|
287
|
+
errors: string[];
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Turns a failed save into RecordForm's `issues` and `errors` props. A caught value
|
|
292
|
+
* carrying the `ErrorResponse` envelope (`{ error: ApiError }`) contributes its field
|
|
293
|
+
* issues; without issues, `errors` holds the caught `Error` message, falling back to
|
|
294
|
+
* `fallback`.
|
|
295
|
+
*/
|
|
296
|
+
export function formFailure(cause: unknown, fallback: string): FormFailure {
|
|
297
|
+
const error = (cause as Partial<ErrorResponse> | null | undefined)?.error;
|
|
298
|
+
const issues = Array.isArray(error?.issues) ? error.issues : [];
|
|
299
|
+
if (issues.length) return { issues, errors: [] };
|
|
300
|
+
return {
|
|
301
|
+
issues: [],
|
|
302
|
+
errors: [cause instanceof Error ? cause.message : fallback],
|
|
303
|
+
};
|
|
304
|
+
}
|