@plitzi/sdk-server 0.37.8 → 0.37.10
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 +825 -0
- package/README.md +24 -2
- package/dist/actions.d.ts +1 -1
- package/dist/adapters/cloudAdapters.d.ts +5 -3
- package/dist/adapters/cloudAdapters.js +123 -63
- package/dist/adapters/space/types.d.ts +3 -0
- package/dist/core/configSeam.d.ts +14 -0
- package/dist/core/configSeam.js +114 -0
- package/dist/core/configSeam.test.d.ts +1 -0
- package/dist/core/createServer.js +11 -5
- package/dist/core/createServer.publicData.test.d.ts +1 -0
- package/dist/core/freePort.d.ts +10 -0
- package/dist/core/freePort.js +36 -0
- package/dist/core/freePort.test.d.ts +1 -0
- package/dist/core/http/stages/images.d.ts +6 -0
- package/dist/core/http/stages/images.js +47 -0
- package/dist/core/http/stages/images.test.d.ts +1 -0
- package/dist/core/server/fleet/channel.js +1 -1
- package/dist/core/server/fleet/relay.js +1 -1
- package/dist/core/services/action.js +5 -4
- package/dist/core/services/functionRoutes.d.ts +2 -2
- package/dist/core/services/functionRoutes.js +2 -2
- package/dist/core/services/registry.js +2 -0
- package/dist/functions-api.d.ts +23 -2
- package/dist/functions.d.ts +1 -1
- package/dist/functions.js +2 -2
- package/dist/helpers/cache/keys.d.ts +23 -10
- package/dist/helpers/cache/keys.js +16 -2
- package/dist/helpers/flagOverrides.d.ts +27 -0
- package/dist/helpers/flagOverrides.js +25 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/dist/modules/actions/index.d.ts +1 -1
- package/dist/modules/actions/index.js +3 -2
- package/dist/modules/actions/moduleFor.js +5 -3
- package/dist/modules/actions/runtime/flags.d.ts +16 -0
- package/dist/modules/actions/runtime/flags.js +49 -0
- package/dist/modules/actions/runtime/flags.test.d.ts +1 -0
- package/dist/modules/actions/runtime/kvList.js +1 -1
- package/dist/modules/actions/runtime/renderResolver.js +30 -4
- package/dist/modules/actions/runtime/runAction.js +2 -0
- package/dist/modules/actions/taskCatalog.d.ts +3 -1
- package/dist/modules/actions/taskCatalog.js +1 -0
- package/dist/modules/actions/tasks/registry.js +4 -2
- package/dist/modules/actions/transport/callHandler.js +8 -0
- package/dist/modules/actions/types.d.ts +20 -1
- package/dist/modules/connectors/engine.js +1 -1
- package/dist/modules/functions/capabilities.js +1 -1
- package/dist/modules/functions/config.d.ts +19 -3
- package/dist/modules/functions/config.js +30 -14
- package/dist/modules/functions/contract.d.ts +10 -3
- package/dist/modules/functions/driver.js +4 -2
- package/dist/modules/functions/load.js +2 -2
- package/dist/modules/functions/manifest.d.ts +5 -3
- package/dist/modules/functions/manifest.js +37 -6
- package/dist/modules/functions/routes.d.ts +1 -1
- package/dist/modules/functions/routes.js +3 -3
- package/dist/modules/functions/runner/messages.js +1 -1
- package/dist/modules/functions/space.d.ts +7 -5
- package/dist/modules/functions/space.js +17 -10
- package/dist/modules/images/allowlist.d.ts +5 -0
- package/dist/modules/images/allowlist.js +18 -0
- package/dist/modules/images/imageProxy.d.ts +36 -0
- package/dist/modules/images/imageProxy.js +273 -0
- package/dist/modules/images/imageProxy.test.d.ts +1 -0
- package/dist/modules/images/transform.d.ts +10 -0
- package/dist/modules/images/transform.js +28 -0
- package/dist/modules/images/transform.test.d.ts +1 -0
- package/dist/modules/rsc/connectorRscData.d.ts +12 -7
- package/dist/modules/rsc/connectorRscData.js +12 -8
- package/dist/modules/rsc/handler.js +18 -4
- package/dist/modules/rsc/publicFileResolver.d.ts +13 -0
- package/dist/modules/rsc/publicFileResolver.js +39 -0
- package/dist/modules/rsc/publicFileResolver.test.d.ts +1 -0
- package/dist/modules/rsc/resolveRscData.d.ts +4 -2
- package/dist/modules/rsc/resolveRscData.js +10 -2
- package/dist/modules/runtime/bundle.js +2 -2
- package/dist/modules/runtime/contract.d.ts +1 -1
- package/dist/modules/runtime/stages.d.ts +1 -1
- package/dist/modules/runtime/stages.js +1 -1
- package/dist/modules/ssr/Component.d.ts +5 -1
- package/dist/modules/ssr/Component.js +3 -1
- package/dist/modules/ssr/prepareRender.js +38 -10
- package/dist/modules/ssr/registerExternalPlugins.d.ts +5 -1
- package/dist/modules/ssr/registerExternalPlugins.js +6 -2
- package/package.json +9 -4
- package/skills/plitzi-authoring/CHEATSHEET.md +135 -0
- package/skills/plitzi-authoring/SKILL.md +77 -74
- package/skills/plitzi-authoring/recipes/carousel.ts +114 -0
- package/skills/plitzi-authoring/recipes/detail-page.ts +70 -0
- package/skills/plitzi-authoring/recipes/embed-and-svg.ts +48 -0
- package/skills/plitzi-authoring/recipes/every-few-seconds.ts +119 -0
- package/skills/plitzi-authoring/recipes/feature-flag.ts +24 -0
- package/skills/plitzi-authoring/recipes/filter-a-list.ts +63 -0
- package/skills/plitzi-authoring/recipes/forms-and-modals.ts +64 -0
- package/skills/plitzi-authoring/recipes/from-tailwind.ts +52 -0
- package/skills/plitzi-authoring/recipes/link-to-a-section.ts +35 -0
- package/skills/plitzi-authoring/recipes/link-with-data.ts +40 -0
- package/skills/plitzi-authoring/recipes/marquee.ts +57 -0
- package/skills/plitzi-authoring/recipes/plugin.ts +53 -0
- package/skills/plitzi-authoring/recipes/scroll-a-row.ts +79 -0
- package/skills/plitzi-authoring/recipes/show-data.ts +73 -0
- package/skills/plitzi-authoring/recipes/style-a-page.ts +45 -0
- package/skills/plitzi-authoring/recipes/typed-data.ts +64 -0
- package/skills/plitzi-authoring/recipes/usable-without-sight.ts +39 -0
- package/skills/plitzi-authoring/reference/accessibility.md +2 -2
- package/skills/plitzi-authoring/reference/authoring-errors.md +195 -53
- package/skills/plitzi-authoring/reference/colours-and-motion.md +63 -0
- package/skills/plitzi-authoring/reference/components.md +83 -0
- package/skills/plitzi-authoring/reference/data-and-visibility.md +29 -29
- package/skills/plitzi-authoring/reference/efficiency.md +53 -0
- package/skills/plitzi-authoring/reference/elements-and-styles.md +39 -24
- package/skills/plitzi-authoring/reference/feature-flags.md +78 -0
- package/skills/plitzi-authoring/reference/flows.md +26 -37
- package/skills/plitzi-authoring/reference/kept-state.md +28 -0
- package/skills/plitzi-authoring/reference/layouts.md +20 -3
- package/skills/plitzi-authoring/reference/lists.md +45 -16
- package/skills/plitzi-authoring/reference/plugins.md +27 -0
- package/skills/plitzi-authoring/reference/realtime.md +35 -0
- package/skills/plitzi-authoring/reference/review-checklist.md +8 -3
- package/skills/plitzi-authoring/reference/{templates-and-export.md → snippets-and-export.md} +15 -8
- package/skills/plitzi-authoring/reference/structure.md +29 -20
- package/skills/plitzi-authoring/reference/tailwind.md +19 -0
- package/skills/plitzi-authoring/reference/templates.md +6 -5
- package/skills/plitzi-authoring/reference/testing.md +11 -0
- package/skills/plitzi-authoring/reference/typed-sources.md +20 -0
- package/skills/plitzi-authoring/reference/validation.md +6 -2
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A marquee: a row of names that scrolls for ever and stops under the pointer. The items are rendered three times
|
|
3
|
+
* and the keyframe moves the track by a third, so the end meets the start with no jump.
|
|
4
|
+
*/
|
|
5
|
+
import { apiContainer, container, list, styles, text } from '@plitzi/sdk-authoring';
|
|
6
|
+
|
|
7
|
+
import type { SpaceSpec } from '@plitzi/sdk-authoring';
|
|
8
|
+
|
|
9
|
+
const marquee = styles('marquee', { overflow: 'hidden' });
|
|
10
|
+
|
|
11
|
+
const track = styles('marquee-track', {
|
|
12
|
+
css: {
|
|
13
|
+
display: 'flex',
|
|
14
|
+
gap: '48px',
|
|
15
|
+
width: 'max-content',
|
|
16
|
+
margin: '0px',
|
|
17
|
+
padding: '0px',
|
|
18
|
+
'list-style-type': 'none',
|
|
19
|
+
animation: 'marquee 30s linear infinite'
|
|
20
|
+
},
|
|
21
|
+
// An ancestor's state reaches inside it: hovering the band pauses the track.
|
|
22
|
+
ancestors: { [marquee.name]: { states: { hover: { 'animation-play-state': 'paused' } } } }
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const recipe: SpaceSpec = {
|
|
26
|
+
name: 'Marquee',
|
|
27
|
+
permanentUrl: 'marquee',
|
|
28
|
+
customCss: '@keyframes marquee { to { transform: translateX(calc(-100% / 3)); } }',
|
|
29
|
+
pages: [
|
|
30
|
+
{
|
|
31
|
+
id: 'home',
|
|
32
|
+
name: 'Home',
|
|
33
|
+
slug: '',
|
|
34
|
+
body: [
|
|
35
|
+
apiContainer({
|
|
36
|
+
id: 'catalog',
|
|
37
|
+
query: '/data/brands.json',
|
|
38
|
+
cache: true,
|
|
39
|
+
children: [
|
|
40
|
+
container({
|
|
41
|
+
class: marquee,
|
|
42
|
+
children: [
|
|
43
|
+
list({
|
|
44
|
+
id: 'brands',
|
|
45
|
+
class: track,
|
|
46
|
+
from: 'catalog.data.brands',
|
|
47
|
+
as: '{{ source|merge(source)|merge(source) }}',
|
|
48
|
+
children: [text({ from: 'brands.item' })]
|
|
49
|
+
})
|
|
50
|
+
]
|
|
51
|
+
})
|
|
52
|
+
]
|
|
53
|
+
})
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An element of your own (a plugin): authored from its declaration — the one `plitzi add plugin` writes — so a flow on
|
|
3
|
+
* its events, a step to its actions and its attributes are all checked. Hand the declaration to `authorSpace` too.
|
|
4
|
+
*/
|
|
5
|
+
import {
|
|
6
|
+
button,
|
|
7
|
+
declaredCallback,
|
|
8
|
+
declaredTrigger,
|
|
9
|
+
defineElement,
|
|
10
|
+
named,
|
|
11
|
+
onClick,
|
|
12
|
+
setState
|
|
13
|
+
} from '@plitzi/sdk-authoring';
|
|
14
|
+
|
|
15
|
+
import type { PluginDeclarationData, SpaceSpec } from '@plitzi/sdk-authoring';
|
|
16
|
+
|
|
17
|
+
// In a project: `import declaration from './plugins/seatPicker/declaration';`
|
|
18
|
+
const declaration = {
|
|
19
|
+
type: 'seatPicker',
|
|
20
|
+
triggers: { onPick: { action: 'onPick', title: 'On Pick', type: 'trigger', params: {}, preview: { seat: '' } } },
|
|
21
|
+
callbacks: { reset: { action: 'reset', title: 'Reset', type: 'callback', params: {} } },
|
|
22
|
+
content: { attributes: { rows: 10 }, definition: { label: 'Seat Picker' } }
|
|
23
|
+
} as const;
|
|
24
|
+
|
|
25
|
+
const seatPicker = defineElement<{ rows?: number }>(declaration);
|
|
26
|
+
|
|
27
|
+
// `authorSpace(recipe, { plugins })` — in a project, `src/plugins/declarations.ts` lists them all.
|
|
28
|
+
export const plugins: PluginDeclarationData[] = [declaration];
|
|
29
|
+
|
|
30
|
+
export const recipe: SpaceSpec = {
|
|
31
|
+
name: 'Seats',
|
|
32
|
+
permanentUrl: 'seats',
|
|
33
|
+
pages: [
|
|
34
|
+
{
|
|
35
|
+
id: 'home',
|
|
36
|
+
name: 'Home',
|
|
37
|
+
slug: '',
|
|
38
|
+
body: [
|
|
39
|
+
seatPicker({
|
|
40
|
+
id: 'seats',
|
|
41
|
+
rows: 12,
|
|
42
|
+
flows: [
|
|
43
|
+
[
|
|
44
|
+
named('picked', declaredTrigger(declaration, 'onPick')),
|
|
45
|
+
setState({ key: 'seat', type: 'text', value: '{{ picked.seat }}' })
|
|
46
|
+
]
|
|
47
|
+
]
|
|
48
|
+
}),
|
|
49
|
+
button({ content: 'Clear', flows: [[onClick(), declaredCallback(declaration, 'reset', { on: 'seats' })]] })
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A row that scrolls sideways — swiped on a phone, moved by arrows elsewhere: the row's box scrolls
|
|
3
|
+
* (`overflow-x: auto`), the arrows move it by most of what it shows, and `onScroll` hides each arrow at its end.
|
|
4
|
+
*/
|
|
5
|
+
import {
|
|
6
|
+
button,
|
|
7
|
+
container,
|
|
8
|
+
list,
|
|
9
|
+
named,
|
|
10
|
+
onClick,
|
|
11
|
+
onScroll,
|
|
12
|
+
scrollBy,
|
|
13
|
+
setState,
|
|
14
|
+
styles,
|
|
15
|
+
text
|
|
16
|
+
} from '@plitzi/sdk-authoring';
|
|
17
|
+
|
|
18
|
+
import type { SpaceSpec } from '@plitzi/sdk-authoring';
|
|
19
|
+
|
|
20
|
+
const row = styles('card-row', {
|
|
21
|
+
display: 'flex',
|
|
22
|
+
gap: '16px',
|
|
23
|
+
'overflow-x': 'auto',
|
|
24
|
+
// Stops on a card, never between two.
|
|
25
|
+
'scroll-snap-type': 'x mandatory',
|
|
26
|
+
margin: '0px',
|
|
27
|
+
padding: '0px',
|
|
28
|
+
'list-style-type': 'none'
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const card = styles('row-card', { 'flex-shrink': '0', width: '240px', padding: '16px', 'scroll-snap-align': 'start' });
|
|
32
|
+
|
|
33
|
+
const CATEGORIES = ['Processors', 'Graphics cards', 'Memory', 'Storage', 'Monitors', 'Keyboards', 'Mice', 'Cases'];
|
|
34
|
+
|
|
35
|
+
export const recipe: SpaceSpec = {
|
|
36
|
+
name: 'Row',
|
|
37
|
+
permanentUrl: 'row',
|
|
38
|
+
pages: [
|
|
39
|
+
{
|
|
40
|
+
id: 'home',
|
|
41
|
+
name: 'Home',
|
|
42
|
+
slug: '',
|
|
43
|
+
body: [
|
|
44
|
+
container({
|
|
45
|
+
children: [
|
|
46
|
+
// Shown until the row says it is at that end: the arrows are there before anybody scrolls.
|
|
47
|
+
button({
|
|
48
|
+
content: '',
|
|
49
|
+
title: 'Previous categories',
|
|
50
|
+
visible: '!state.rowAtStart',
|
|
51
|
+
children: [text('‹')],
|
|
52
|
+
flows: [[onClick(), scrollBy('categories', { x: '-80%' })]]
|
|
53
|
+
}),
|
|
54
|
+
list({
|
|
55
|
+
id: 'categories',
|
|
56
|
+
class: row,
|
|
57
|
+
items: CATEGORIES,
|
|
58
|
+
flows: [
|
|
59
|
+
[
|
|
60
|
+
named('moved', onScroll()),
|
|
61
|
+
setState({ key: 'rowAtStart', type: 'boolean', value: '{{ moved.atStart }}' }),
|
|
62
|
+
setState({ key: 'rowAtEnd', type: 'boolean', value: '{{ moved.atEnd }}' })
|
|
63
|
+
]
|
|
64
|
+
],
|
|
65
|
+
children: [text({ class: card, from: 'categories.item' })]
|
|
66
|
+
}),
|
|
67
|
+
button({
|
|
68
|
+
content: '',
|
|
69
|
+
title: 'More categories',
|
|
70
|
+
visible: '!state.rowAtEnd',
|
|
71
|
+
children: [text('›')],
|
|
72
|
+
flows: [[onClick(), scrollBy('categories', { x: '80%' })]]
|
|
73
|
+
})
|
|
74
|
+
]
|
|
75
|
+
})
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/* eslint-disable quotes -- templates quote their own strings, and read best in the other quotes */
|
|
2
|
+
/**
|
|
3
|
+
* Show data with no backend: a JSON file the project serves (`public/data/products.json`), read by a provider — one
|
|
4
|
+
* card per product, a skeleton while it loads, a count, a computed value, and an empty state that never flashes.
|
|
5
|
+
*/
|
|
6
|
+
import { apiContainer, container, heading, list, styles, text } from '@plitzi/sdk-authoring';
|
|
7
|
+
|
|
8
|
+
import type { SpaceSpec } from '@plitzi/sdk-authoring';
|
|
9
|
+
|
|
10
|
+
// A list is a `<ul>`: a grid of cards takes the bullets and the indent off.
|
|
11
|
+
const grid = styles('product-grid', {
|
|
12
|
+
display: 'grid',
|
|
13
|
+
'grid-template-columns': 'repeat(auto-fill, minmax(220px, 1fr))',
|
|
14
|
+
gap: '16px',
|
|
15
|
+
margin: '0px',
|
|
16
|
+
padding: '0px',
|
|
17
|
+
'list-style-type': 'none'
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const card = styles('product-card', { padding: '16px', 'border-radius': '12px', 'background-color': 'var(--card)' });
|
|
21
|
+
|
|
22
|
+
// The shape of what is coming, shown until the file answers.
|
|
23
|
+
const skeleton = styles('product-skeleton', {
|
|
24
|
+
height: '96px',
|
|
25
|
+
'border-radius': '12px',
|
|
26
|
+
'background-color': 'var(--card)'
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const recipe: SpaceSpec = {
|
|
30
|
+
name: 'Catalogue',
|
|
31
|
+
permanentUrl: 'catalogue',
|
|
32
|
+
variables: { color: { card: { light: '#ffffff', dark: '#16161d', default: '#ffffff' } } },
|
|
33
|
+
// Read anywhere as `computed.cartCount`, declared once.
|
|
34
|
+
computed: { cartCount: '{{ (state.cart ?? [])|length }}' },
|
|
35
|
+
// How a price is shown, said once: `text({ from: …, as: 'price' })` anywhere.
|
|
36
|
+
formats: { price: "{{ source|currency('USD', 'en', { trimZeros: true }) }}" },
|
|
37
|
+
pages: [
|
|
38
|
+
{
|
|
39
|
+
id: 'home',
|
|
40
|
+
name: 'Home',
|
|
41
|
+
slug: '',
|
|
42
|
+
body: [
|
|
43
|
+
text({ from: 'computed.cartCount', as: '{{ source }} in the cart' }),
|
|
44
|
+
apiContainer({
|
|
45
|
+
id: 'catalog',
|
|
46
|
+
query: '/data/products.json',
|
|
47
|
+
cache: true,
|
|
48
|
+
// Shown in place of the rest until the first answer, and gone after it.
|
|
49
|
+
loadingSlot: 'catalog-skeleton',
|
|
50
|
+
children: [
|
|
51
|
+
container({ id: 'catalog-skeleton', class: skeleton }),
|
|
52
|
+
// Inside a template a source is spelled in full; a binding's own source is `source`.
|
|
53
|
+
text({ from: 'catalog.data.products', as: '{{ source|length }} products' }),
|
|
54
|
+
list({
|
|
55
|
+
id: 'products',
|
|
56
|
+
class: grid,
|
|
57
|
+
items: 'catalog.data.products',
|
|
58
|
+
children: [
|
|
59
|
+
// Inside a row, a binding names the row short (`products.item`), a template in full (`list_products`).
|
|
60
|
+
heading({ subType: 'h3', class: card, from: 'products.item.title' }),
|
|
61
|
+
text({ from: 'products.item.price', as: 'price' })
|
|
62
|
+
]
|
|
63
|
+
}),
|
|
64
|
+
// Revealed by the data, so it starts hidden: nothing flashes while the file loads.
|
|
65
|
+
text('No products yet.', {
|
|
66
|
+
visible: { source: 'catalog.data.products', template: '{{ source is defined and source|length == 0 }}' }
|
|
67
|
+
})
|
|
68
|
+
]
|
|
69
|
+
})
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Style without copies: colours are tokens with a light and a dark value, a look used twice is a class, "this class
|
|
3
|
+
* plus one thing" is rules on top of it (`class: [card, { … }]`, which needs an `id`), and a rule for tablet AND phone
|
|
4
|
+
* is written once under `compact`.
|
|
5
|
+
*/
|
|
6
|
+
import { container, heading, styles, text } from '@plitzi/sdk-authoring';
|
|
7
|
+
|
|
8
|
+
import type { SpaceSpec } from '@plitzi/sdk-authoring';
|
|
9
|
+
|
|
10
|
+
const card = styles('card', {
|
|
11
|
+
css: {
|
|
12
|
+
desktop: { display: 'flex', 'flex-direction': 'column', gap: '8px', padding: '24px', 'border-radius': '16px' },
|
|
13
|
+
// Tablet (48–64rem) and mobile (below 48rem) each inherit from desktop alone: `compact` is both.
|
|
14
|
+
compact: { padding: '16px' }
|
|
15
|
+
},
|
|
16
|
+
states: { hover: { 'border-color': 'var(--primary)' } }
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const recipe: SpaceSpec = {
|
|
20
|
+
name: 'Styled',
|
|
21
|
+
permanentUrl: 'styled',
|
|
22
|
+
variables: {
|
|
23
|
+
color: {
|
|
24
|
+
foreground: { light: '#0c0c14', dark: '#ededf3', default: '#0c0c14' },
|
|
25
|
+
card: { light: '#ffffff', dark: '#101019', default: '#ffffff' },
|
|
26
|
+
primary: { light: '#4f46e5', dark: '#818cf8', default: '#4f46e5' }
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
pages: [
|
|
30
|
+
{
|
|
31
|
+
id: 'home',
|
|
32
|
+
name: 'Home',
|
|
33
|
+
slug: '',
|
|
34
|
+
css: { 'background-color': 'var(--card)', color: 'var(--foreground)', padding: '48px 24px' },
|
|
35
|
+
body: [
|
|
36
|
+
container({ class: card, children: [heading('Plain', { subType: 'h2' }), text('The class as it is.')] }),
|
|
37
|
+
container({
|
|
38
|
+
id: 'featured',
|
|
39
|
+
class: [card, { 'border-width': '2px', 'border-style': 'solid', 'border-color': 'var(--primary)' }],
|
|
40
|
+
children: [heading('Featured', { subType: 'h2' }), text('The class, plus one thing.')]
|
|
41
|
+
})
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/* eslint-disable quotes -- templates quote their own strings, and read best in the other quotes */
|
|
2
|
+
/**
|
|
3
|
+
* Data typed by a sample of it: `source()` turns the JSON the page reads into names the editor completes and checks —
|
|
4
|
+
* `site.data.hero.titel` is a type error, not an empty heading. In a project the sample is the file itself:
|
|
5
|
+
* `import home from '../../public/data/home.json' with { type: 'json' }`.
|
|
6
|
+
*/
|
|
7
|
+
import { apiContainer, heading, list, source, styles, text, twig } from '@plitzi/sdk-authoring';
|
|
8
|
+
|
|
9
|
+
import type { SpaceSpec } from '@plitzi/sdk-authoring';
|
|
10
|
+
|
|
11
|
+
const home = {
|
|
12
|
+
hero: { title: 'Fresh this week', subtitle: 'Picked by the team' },
|
|
13
|
+
products: [
|
|
14
|
+
{ title: 'Desk lamp', price: 49 },
|
|
15
|
+
{ title: 'Notebook', price: 12, badge: 'new' }
|
|
16
|
+
]
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const site = source('site', home);
|
|
20
|
+
|
|
21
|
+
const grid = styles('typed-grid', {
|
|
22
|
+
display: 'grid',
|
|
23
|
+
gap: '12px',
|
|
24
|
+
margin: '0px',
|
|
25
|
+
padding: '0px',
|
|
26
|
+
listStyleType: 'none'
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const recipe: SpaceSpec = {
|
|
30
|
+
name: 'Typed data',
|
|
31
|
+
permanentUrl: 'typed-data',
|
|
32
|
+
formats: { price: "{{ source|currency('USD', 'en') }}" },
|
|
33
|
+
pages: [
|
|
34
|
+
{
|
|
35
|
+
id: 'home',
|
|
36
|
+
name: 'Home',
|
|
37
|
+
slug: '',
|
|
38
|
+
body: [
|
|
39
|
+
apiContainer({
|
|
40
|
+
id: site.id,
|
|
41
|
+
query: '/data/home.json',
|
|
42
|
+
cache: true,
|
|
43
|
+
children: [
|
|
44
|
+
heading({ from: site.data.hero.title }),
|
|
45
|
+
text({ from: site.data.hero.subtitle }),
|
|
46
|
+
// A template takes the paths through `twig`, each written in full.
|
|
47
|
+
text({ content: twig`{{ ${site.data.products}|length }} products` }),
|
|
48
|
+
list({
|
|
49
|
+
id: 'products',
|
|
50
|
+
class: grid,
|
|
51
|
+
items: site.data.products,
|
|
52
|
+
// The row's item is typed as the sample's: every field any product has.
|
|
53
|
+
row: product => [
|
|
54
|
+
text({ from: product.item.title }),
|
|
55
|
+
text({ from: product.item.price, as: 'price' }),
|
|
56
|
+
text({ from: product.item.badge, visible: product.item.badge })
|
|
57
|
+
]
|
|
58
|
+
})
|
|
59
|
+
]
|
|
60
|
+
})
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Controls a screen reader and a browser agent can use: an icon-only button says what it does, a clickable card IS a
|
|
3
|
+
* button, a field keeps its label out of sight rather than dropping it, a picture says what it shows or that it only
|
|
4
|
+
* decorates.
|
|
5
|
+
*/
|
|
6
|
+
import { button, fontAwesome, formControl, image, setState, styles, text, onClick } from '@plitzi/sdk-authoring';
|
|
7
|
+
|
|
8
|
+
import type { SpaceSpec } from '@plitzi/sdk-authoring';
|
|
9
|
+
|
|
10
|
+
const card = styles('plan-card', {
|
|
11
|
+
display: 'flex',
|
|
12
|
+
'flex-direction': 'column',
|
|
13
|
+
padding: '16px',
|
|
14
|
+
'text-align': 'left'
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const recipe: SpaceSpec = {
|
|
18
|
+
name: 'Accessible',
|
|
19
|
+
permanentUrl: 'accessible',
|
|
20
|
+
pages: [
|
|
21
|
+
{
|
|
22
|
+
id: 'home',
|
|
23
|
+
name: 'Home',
|
|
24
|
+
slug: '',
|
|
25
|
+
body: [
|
|
26
|
+
button({ content: '', title: 'Search', children: [fontAwesome({ icon: 'fa-solid fa-magnifying-glass' })] }),
|
|
27
|
+
button({
|
|
28
|
+
content: '',
|
|
29
|
+
class: card,
|
|
30
|
+
children: [text('Pro'), text('12 € a month')],
|
|
31
|
+
flows: [[onClick(), setState({ key: 'plan', type: 'text', value: 'pro' })]]
|
|
32
|
+
}),
|
|
33
|
+
formControl({ name: 'q', label: 'Search the docs', hideLabel: true, placeholder: 'Search…' }),
|
|
34
|
+
image({ src: '/team.jpg', alt: 'The team at the 2026 offsite' }),
|
|
35
|
+
image({ src: '/grain.png', decorative: true })
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
};
|
|
@@ -19,7 +19,7 @@ button({ content: '', title: 'Close', children: [fontAwesome({ icon: 'fa-solid f
|
|
|
19
19
|
button({ content: '', label: 'Select (V)', title: 'Select — V', children: [fontAwesome({ icon: 'fa-solid fa-arrow-pointer' }), text('V')] })
|
|
20
20
|
|
|
21
21
|
// A link around a whole card: without `label` it is announced as every word inside the card.
|
|
22
|
-
link({ href: '/posts/field-notes',
|
|
22
|
+
link({ href: '/posts/field-notes', label: 'Read “Field notes”', children: [ /* cover, topic, title, byline */ ] })
|
|
23
23
|
|
|
24
24
|
// A field whose design says what it is: the label stays, out of sight.
|
|
25
25
|
formControl({ name: 'q', label: 'Search the docs', hideLabel: true, placeholder: 'Search…' })
|
|
@@ -100,7 +100,7 @@ toggle's pressed option.
|
|
|
100
100
|
|
|
101
101
|
Anything drawn on a canvas is one picture to these readers. Render what it shows as elements too: a list with a
|
|
102
102
|
named button per action. Keep that list visible when someone asks for it, because an agent clicks by position and
|
|
103
|
-
can't click something that only appears on focus.
|
|
103
|
+
can't click something that only appears on focus.
|
|
104
104
|
|
|
105
105
|
## Checking
|
|
106
106
|
|