storyloco 2.0.1 → 2.2.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 +10 -0
- package/package.json +7 -2
- package/packages/currency/types.ts +11 -0
- package/packages/vite/dist/schema/plugin.js +33 -2
- package/packages/vite/dist/server.d.ts +6 -8
- package/packages/vite/dist/server.js +35 -18
- package/packages/vite/dist/storyblok.svelte.d.ts +4 -1
- package/packages/vite/dist/storyblok.svelte.js +1 -0
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ A collection of slick Storyblok field plugins built with Svelte 5, TypeScript, a
|
|
|
15
15
|
- **🏗️ Plans** - Floor plan manager with drag-and-drop sorting for types, floors, dimensions, and rooms
|
|
16
16
|
- **📝 Input** - Comprehensive form input field with support for all HTML input types, checkboxes, radio buttons, selects, and textareas
|
|
17
17
|
- **⏰ Time** - Simple time input field with step control
|
|
18
|
+
- **💷 Currency** - Amount input with a currency selector; configure allowed codes via the `currencies` and `default_currency` options
|
|
18
19
|
|
|
19
20
|
### Vite Plugins
|
|
20
21
|
|
|
@@ -185,6 +186,14 @@ Your params are spread last, so any default can be overridden:
|
|
|
185
186
|
await storyblok.story('layout', { resolve_assets: 0 })
|
|
186
187
|
```
|
|
187
188
|
|
|
189
|
+
`story` treats a missing story as a 404. Where a slug is allowed not to exist, `find` returns
|
|
190
|
+
`null` instead — the miss is then neither raised nor logged:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
const product = await storyblok.find<Product>(`products/${slug}`)
|
|
194
|
+
if (!product) error(404, 'Product not found')
|
|
195
|
+
```
|
|
196
|
+
|
|
188
197
|
It's callable too, for anything the methods don't cover:
|
|
189
198
|
|
|
190
199
|
```typescript
|
|
@@ -258,6 +267,7 @@ export interface StoryblokCustomPlugins {
|
|
|
258
267
|
twitter_description: string
|
|
259
268
|
}
|
|
260
269
|
'loco-time': string
|
|
270
|
+
'loco-currency': import('storyloco/currency').Currency
|
|
261
271
|
'loco-asset': import('storyloco/asset').Asset
|
|
262
272
|
}
|
|
263
273
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "storyloco",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "A collection of slick Storyblok field plugins built with Svelte 5, TypeScript, and Tailwind CSS",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"storyblok",
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"packages/input/types.ts",
|
|
34
34
|
"packages/time/types.ts",
|
|
35
35
|
"packages/seo/types.ts",
|
|
36
|
-
"packages/hubspot_form/types.ts"
|
|
36
|
+
"packages/hubspot_form/types.ts",
|
|
37
|
+
"packages/currency/types.ts"
|
|
37
38
|
],
|
|
38
39
|
"exports": {
|
|
39
40
|
".": {
|
|
@@ -93,6 +94,10 @@
|
|
|
93
94
|
"./hubspot_form": {
|
|
94
95
|
"types": "./packages/hubspot_form/types.ts",
|
|
95
96
|
"svelte": "./packages/hubspot_form/src/app.svelte.ts"
|
|
97
|
+
},
|
|
98
|
+
"./currency": {
|
|
99
|
+
"types": "./packages/currency/types.ts",
|
|
100
|
+
"svelte": "./packages/currency/src/app.svelte.ts"
|
|
96
101
|
}
|
|
97
102
|
},
|
|
98
103
|
"devDependencies": {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Narrow `Code` to the codes configured in the field's `currencies` option,
|
|
3
|
+
* e.g. `Currency<'GBP' | 'USD'>`.
|
|
4
|
+
*/
|
|
5
|
+
export interface Currency<Code extends string = Uppercase<string>> {
|
|
6
|
+
/** ISO 4217 code, e.g. `GBP` */
|
|
7
|
+
currency: Code
|
|
8
|
+
amount: number | null
|
|
9
|
+
/** `amount` formatted for display, e.g. `£130,000.00`; `null` when there's no amount */
|
|
10
|
+
formatted: string | null
|
|
11
|
+
}
|
|
@@ -176,6 +176,23 @@ import type { ISbStoryData } from '@storyblok/js';`, "");
|
|
|
176
176
|
// determine how many folders are in output_path
|
|
177
177
|
const output_path_parts = out_dir.split("/");
|
|
178
178
|
const import_path = output_path_parts.map(() => `..`).join("/");
|
|
179
|
+
// Replicates the storyblok CLI's `getComponentType` (no prefix/suffix), so these
|
|
180
|
+
// entries reference the interface names it generates below.
|
|
181
|
+
const type_name = (component) => {
|
|
182
|
+
const sanitized = component
|
|
183
|
+
.replace(/[^a-z0-9]/gi, "_")
|
|
184
|
+
.replace(/_+/g, "_")
|
|
185
|
+
.replace(/^_+|_+$/g, "");
|
|
186
|
+
const camel = sanitized
|
|
187
|
+
.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())
|
|
188
|
+
.replace(/_/g, "");
|
|
189
|
+
const pascal = camel ? camel[0].toUpperCase() + camel.slice(1) : camel;
|
|
190
|
+
return /^\d/.test(pascal) ? `_${pascal}` : pascal;
|
|
191
|
+
};
|
|
192
|
+
const content_types = new_components
|
|
193
|
+
.filter((component) => component.is_root)
|
|
194
|
+
.map((component) => component.name)
|
|
195
|
+
.sort();
|
|
179
196
|
const header = `
|
|
180
197
|
/**
|
|
181
198
|
* AUTO-GENERATED FILE. DO NOT EDIT.
|
|
@@ -184,9 +201,23 @@ import type { ISbStoryData } from '@storyblok/js';`, "");
|
|
|
184
201
|
*/
|
|
185
202
|
|
|
186
203
|
import type { StoryblokCustomPlugins } from "${import_path}/vite.config.js"
|
|
187
|
-
import type { SbBlokData } from "@storyblok/svelte"
|
|
188
204
|
|
|
189
|
-
|
|
205
|
+
/**
|
|
206
|
+
* No \`SbBlokData\` intersection: its string index signature would reject the
|
|
207
|
+
* generated interfaces, which (deliberately) declare no index signature.
|
|
208
|
+
*/
|
|
209
|
+
export type Blok<T> = T & { component?: string; _uid?: string; _editable?: string }
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Content-type (root) components keyed by component name — the bloks a story
|
|
213
|
+
* can be. The CLI types every component identically, so this comes from the
|
|
214
|
+
* space's component list instead.
|
|
215
|
+
*/
|
|
216
|
+
export interface ContentTypes {
|
|
217
|
+
${content_types.map((name) => `"${name}": ${type_name(name)}`).join("\n")}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export type ContentType = ContentTypes[keyof ContentTypes]
|
|
190
221
|
`;
|
|
191
222
|
let content = component_types.replace(`// This file was generated by the storyblok CLI.
|
|
192
223
|
// DO NOT MODIFY THIS FILE BY HAND.`, header);
|
|
@@ -19,6 +19,7 @@ export declare function version(): Version;
|
|
|
19
19
|
*
|
|
20
20
|
* // anywhere on the server
|
|
21
21
|
* await storyblok.story("layout")
|
|
22
|
+
* await storyblok.find<Product>(`products/${slug}`) // null when missing
|
|
22
23
|
* await storyblok.stories<Blog>({ content_type: "blog", per_page: 2 })
|
|
23
24
|
*
|
|
24
25
|
* const { client, version } = storyblok()
|
|
@@ -29,15 +30,12 @@ export declare function setup(client: StoryblokClient): (() => {
|
|
|
29
30
|
client: StoryblokClient;
|
|
30
31
|
version: Version;
|
|
31
32
|
}) & {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
* unparameterised call behaves as before.
|
|
37
|
-
*/
|
|
38
|
-
story<T = import("storyblok-js-client").ISbComponentType<string> & {
|
|
33
|
+
story: <T = import("storyblok-js-client").ISbComponentType<string> & {
|
|
34
|
+
[index: string]: any;
|
|
35
|
+
}>(slug: string, params?: ISbStoryParams) => Promise<ISbStoryData<T>>;
|
|
36
|
+
find: <T = import("storyblok-js-client").ISbComponentType<string> & {
|
|
39
37
|
[index: string]: any;
|
|
40
|
-
}>(slug: string, params?: ISbStoryParams)
|
|
38
|
+
}>(slug: string, params?: ISbStoryParams) => Promise<ISbStoryData<T> | null>;
|
|
41
39
|
/**
|
|
42
40
|
* The link tree as a flat array — Storyblok returns it keyed by uuid, which is
|
|
43
41
|
* almost never what you want. Filtering and ordering are left to the caller.
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { getRequestEvent } from "$app/server";
|
|
2
|
+
import { error } from "@sveltejs/kit";
|
|
2
3
|
import { handle_error } from "./storyblok.svelte.js";
|
|
4
|
+
function is_not_found(err) {
|
|
5
|
+
return typeof err === "object" && err !== null && "status" in err && err.status === 404;
|
|
6
|
+
}
|
|
3
7
|
/** The content version for the current request, as set by the `version` hook. */
|
|
4
8
|
export function version() {
|
|
5
9
|
return getRequestEvent().locals.version;
|
|
@@ -20,6 +24,7 @@ export function version() {
|
|
|
20
24
|
*
|
|
21
25
|
* // anywhere on the server
|
|
22
26
|
* await storyblok.story("layout")
|
|
27
|
+
* await storyblok.find<Product>(`products/${slug}`) // null when missing
|
|
23
28
|
* await storyblok.stories<Blog>({ content_type: "blog", per_page: 2 })
|
|
24
29
|
*
|
|
25
30
|
* const { client, version } = storyblok()
|
|
@@ -28,25 +33,37 @@ export function version() {
|
|
|
28
33
|
*/
|
|
29
34
|
export function setup(client) {
|
|
30
35
|
const context = () => ({ client, version: version() });
|
|
36
|
+
/**
|
|
37
|
+
* A single story by full slug, or `null` when there isn't one. Resolves the client's
|
|
38
|
+
* `relations`, unlike `stories`. For slugs that are allowed not to exist — a miss is
|
|
39
|
+
* routine, so it's neither raised nor logged. `T` is the story's content type.
|
|
40
|
+
*/
|
|
41
|
+
async function find(slug, params = {}) {
|
|
42
|
+
const response = await client
|
|
43
|
+
.get(`cdn/stories/${slug}`, {
|
|
44
|
+
version: version(),
|
|
45
|
+
resolve_links: "url",
|
|
46
|
+
resolve_relations: client.relations,
|
|
47
|
+
resolve_assets: 1,
|
|
48
|
+
...params,
|
|
49
|
+
})
|
|
50
|
+
.catch((err) => {
|
|
51
|
+
if (is_not_found(err))
|
|
52
|
+
return null;
|
|
53
|
+
return handle_error(err);
|
|
54
|
+
});
|
|
55
|
+
return (response?.data.story ?? null);
|
|
56
|
+
}
|
|
57
|
+
/** A single story by full slug; a missing one is a 404. `find` for stories that may not exist. */
|
|
58
|
+
async function story(slug, params = {}) {
|
|
59
|
+
const found = await find(slug, params);
|
|
60
|
+
if (!found)
|
|
61
|
+
error(404, "Story not found");
|
|
62
|
+
return found;
|
|
63
|
+
}
|
|
31
64
|
return Object.assign(context, {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
*
|
|
35
|
-
* `T` is the story's content type; it defaults to the SDK's permissive one, so an
|
|
36
|
-
* unparameterised call behaves as before.
|
|
37
|
-
*/
|
|
38
|
-
async story(slug, params = {}) {
|
|
39
|
-
const response = await client
|
|
40
|
-
.get(`cdn/stories/${slug}`, {
|
|
41
|
-
version: version(),
|
|
42
|
-
resolve_links: "url",
|
|
43
|
-
resolve_relations: client.relations,
|
|
44
|
-
resolve_assets: 1,
|
|
45
|
-
...params,
|
|
46
|
-
})
|
|
47
|
-
.catch(handle_error);
|
|
48
|
-
return response.data.story;
|
|
49
|
-
},
|
|
65
|
+
story,
|
|
66
|
+
find,
|
|
50
67
|
/**
|
|
51
68
|
* The link tree as a flat array — Storyblok returns it keyed by uuid, which is
|
|
52
69
|
* almost never what you want. Filtering and ordering are left to the caller.
|
|
@@ -196,7 +196,10 @@ interface BlokFields {
|
|
|
196
196
|
* </section>
|
|
197
197
|
* ```
|
|
198
198
|
*/
|
|
199
|
-
export declare function attrs<T extends BlokFields>(blok:
|
|
199
|
+
export declare function attrs<T extends BlokFields>(blok: T & {
|
|
200
|
+
_uid?: string;
|
|
201
|
+
_editable?: string;
|
|
202
|
+
}): {
|
|
200
203
|
[x: symbol]: (node: HTMLElement) => void;
|
|
201
204
|
id: string | undefined;
|
|
202
205
|
};
|