@vit-foundation/ui 0.2.1 → 0.5.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 -101
- package/dist/chrome.d.ts +7 -0
- package/dist/chrome.js +7 -0
- package/dist/community.d.ts +12 -0
- package/dist/community.js +10 -0
- package/dist/components/account/NewsletterSignup.svelte +19 -4
- package/dist/components/jobs/JobList.svelte +9 -3
- package/dist/components/layout/Footer.svelte +10 -1
- package/dist/components/weeklies/CommentSection.svelte +15 -3
- package/dist/config/context.js +4 -1
- package/dist/config/types.d.ts +10 -0
- package/dist/content-components.d.ts +22 -0
- package/dist/content-components.js +17 -0
- package/dist/edit/helpers.d.ts +9 -0
- package/dist/edit/helpers.js +8 -0
- package/dist/edit/index.d.ts +1 -1
- package/dist/edit/index.js +1 -1
- package/dist/edit/types.d.ts +9 -2
- package/dist/forms/index.d.ts +7 -0
- package/dist/forms/index.js +7 -0
- package/dist/index.d.ts +4 -14
- package/dist/index.js +10 -18
- package/dist/primitives.d.ts +17 -0
- package/dist/primitives.js +17 -0
- package/package.json +21 -51
package/README.md
CHANGED
|
@@ -1,116 +1,70 @@
|
|
|
1
1
|
# @vit-foundation/ui
|
|
2
2
|
|
|
3
3
|
Fundació VIT's shared Svelte 5 component library: the components behind
|
|
4
|
-
[fundaciovit.org](https://fundaciovit.org), decoupled from any one app, with
|
|
5
|
-
optional **edit mode** that lets a CMS-shaped consumer make the rendered
|
|
4
|
+
[fundaciovit.org](https://fundaciovit.org), decoupled from any one app, with
|
|
5
|
+
an optional **edit mode** that lets a CMS-shaped consumer make the rendered
|
|
6
6
|
content editable in place.
|
|
7
7
|
|
|
8
8
|
```sh
|
|
9
9
|
npm install @vit-foundation/ui
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
Requires `svelte` ^5.0.0 as a peer.
|
|
13
|
-
|
|
14
|
-
## Quick start
|
|
15
|
-
|
|
16
|
-
```svelte
|
|
17
|
-
<script>
|
|
18
|
-
import { WeeklieCard, PageShell } from '@vit-foundation/ui';
|
|
19
|
-
import '@vit-foundation/ui/tokens.css'; // design tokens (CSS custom properties)
|
|
20
|
-
import '@vit-foundation/ui/base.css'; // utility classes + edit affordance
|
|
21
|
-
</script>
|
|
22
|
-
|
|
23
|
-
<PageShell title="Weeklies">
|
|
24
|
-
<WeeklieCard weekly={data.weekly} />
|
|
25
|
-
</PageShell>
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Every component renders standalone with Catalan copy and identity hrefs.
|
|
29
|
-
Subpath imports are available when you want a narrower graph:
|
|
30
|
-
`@vit-foundation/ui/ui`, `/layout`, `/weeklies`, `/projects`, `/team`,
|
|
31
|
-
`/timeline`, `/jobs`, `/edit`, `/config`.
|
|
32
|
-
|
|
33
|
-
## Wiring a host app: `UiProvider`
|
|
34
|
-
|
|
35
|
-
Strings, href resolution, the active locale, and the current URL come from a
|
|
36
|
-
`UiConfig` context. Mount it once, in the root layout. The foundation website
|
|
37
|
-
wires it to Paraglide and SvelteKit like this:
|
|
12
|
+
Requires `svelte` ^5.0.0 as a peer. Import the two stylesheets once:
|
|
38
13
|
|
|
39
14
|
```svelte
|
|
40
15
|
<script>
|
|
41
|
-
import
|
|
42
|
-
import
|
|
43
|
-
import { m } from '$lib/paraglide/messages';
|
|
44
|
-
import { getLocale, locales, deLocalizeUrl } from '$lib/paraglide/runtime';
|
|
16
|
+
import '@vit-foundation/ui/tokens.css';
|
|
17
|
+
import '@vit-foundation/ui/base.css';
|
|
45
18
|
</script>
|
|
46
|
-
|
|
47
|
-
<UiProvider
|
|
48
|
-
config={{
|
|
49
|
-
href: (path, options) => localizeHref(path, options),
|
|
50
|
-
locale: () => getLocale(),
|
|
51
|
-
locales,
|
|
52
|
-
url: () => page.url,
|
|
53
|
-
canonicalPathname: (url) => deLocalizeUrl(url).pathname,
|
|
54
|
-
siteName: 'ViT',
|
|
55
|
-
messages: m
|
|
56
|
-
}}
|
|
57
|
-
>
|
|
58
|
-
{@render children()}
|
|
59
|
-
</UiProvider>
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
`locale`, `url`, and `messages` reads are lazy, so reactive sources flow
|
|
63
|
-
through: a locale switch re-renders every consumer without a remount.
|
|
64
|
-
`messages` is a typed structural subset of a Paraglide `m` module — pass it
|
|
65
|
-
wholesale. Keys consumed only through this package still count as used;
|
|
66
|
-
don't prune them from your message catalogue.
|
|
67
|
-
|
|
68
|
-
## Edit mode
|
|
69
|
-
|
|
70
|
-
Components render plain, already-localized strings. To make one editable,
|
|
71
|
-
pass an _edit descriptor_ naming what the string is — and install an
|
|
72
|
-
`EditAdapter` that knows how to persist it. No adapter (or `isEditing`
|
|
73
|
-
off) means every descriptor is inert and the render is byte-identical to
|
|
74
|
-
the read-only one. **The package never talks to a database.**
|
|
75
|
-
|
|
76
|
-
```svelte
|
|
77
|
-
<script>
|
|
78
|
-
import { setEditAdapter, entityEdit, WeeklieCard } from '@vit-foundation/ui';
|
|
79
|
-
|
|
80
|
-
let editing = $state(false);
|
|
81
|
-
|
|
82
|
-
setEditAdapter({
|
|
83
|
-
get isEditing() {
|
|
84
|
-
return editing;
|
|
85
|
-
},
|
|
86
|
-
// Merge { [descriptor.locale]: value } into the localized column.
|
|
87
|
-
// Reject when persistence fails — the editor keeps the draft and
|
|
88
|
-
// shows the error state. Never let a save empty the canonical `ca`.
|
|
89
|
-
save: (descriptor, value) => saveContent(descriptor, value)
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const edit = entityEdit('weeklies', weekly.id, 'ca');
|
|
93
|
-
</script>
|
|
94
|
-
|
|
95
|
-
<WeeklieCard {weekly} edit={{ title: edit('title'), excerpt: edit('excerpt') }} />
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
Editing UX: click into an outlined text, type, and blur or press Enter to
|
|
99
|
-
save (Cmd/Ctrl+Enter for multiline fields, Escape reverts). `RichText`
|
|
100
|
-
opens a source editor over the block mini-format with a live preview.
|
|
101
|
-
Wrap your own render sites with the `Editable` primitive:
|
|
102
|
-
|
|
103
|
-
```svelte
|
|
104
|
-
<Editable edit={pageCopyEdit('home', 'hero_title', 'ca')} value={copy.hero_title}>
|
|
105
|
-
{#snippet children(text, attrs)}
|
|
106
|
-
<h1 {...attrs}>{text}</h1>
|
|
107
|
-
{/snippet}
|
|
108
|
-
</Editable>
|
|
109
19
|
```
|
|
110
20
|
|
|
111
|
-
The
|
|
112
|
-
|
|
113
|
-
|
|
21
|
+
## The library at a glance
|
|
22
|
+
|
|
23
|
+
Everything exports flat from the root, and again grouped by role:
|
|
24
|
+
|
|
25
|
+
| Entry point | What lives there |
|
|
26
|
+
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
27
|
+
| [`/primitives`](./docs/components/primitives.md) | Button, GhostButton, Link, CardMedia, CardTitle, CopyIntro, DateText, DecorShapes, FilterChips, Pagination, RichText, SearchInput, ShareRow |
|
|
28
|
+
| [`/forms`](./docs/forms.md) | Field, FormFeedback, FormErrorFeedback, FormResultSlot, Honeypot, LocaleField, NewsletterIntentField — plus the remote-form seam: structural types, transport constants, field bounds |
|
|
29
|
+
| [`/chrome`](./docs/components/chrome.md) | PageShell, Nav, Footer |
|
|
30
|
+
| [`/content`](./docs/components/content.md) | WeeklieCard, ProjectCard, Timeline, TimelineMilestone, TeamMemberCard, CollaboratorList, JobList, SortSelect — plus the data shapes and helpers |
|
|
31
|
+
| [`/community`](./docs/components/community.md) | AuthPageShell, LoginForm, SignupForm, GoogleAuthForm, AccountPanel, NewsletterSignup, CommentSection, ReactionBar, ContactForm |
|
|
32
|
+
| [`/edit`](./docs/edit-mode.md) | Editable, setEditAdapter/getEditAdapter, descriptors and helpers, LocalizedText |
|
|
33
|
+
| [`/config`](./docs/getting-started.md#wiring-an-app-uiprovider) | UiProvider, UiConfig, the locale set, the default Catalan messages |
|
|
34
|
+
| `/testing` | createRemoteFormMock — the remote-form stand-in for stories and tests |
|
|
35
|
+
|
|
36
|
+
Every component renders standalone (Catalan copy, identity hrefs); an app
|
|
37
|
+
integrates its i18n and router through one `UiProvider` in the root layout.
|
|
38
|
+
|
|
39
|
+
## Documentation
|
|
40
|
+
|
|
41
|
+
- **[Getting started](./docs/getting-started.md)** — install, styles, entry
|
|
42
|
+
points, wiring `UiProvider` (with the foundation website's Paraglide +
|
|
43
|
+
SvelteKit example)
|
|
44
|
+
- **[Edit mode](./docs/edit-mode.md)** — descriptors, the `EditAdapter`
|
|
45
|
+
contract, the `Editable` primitive, the editing UX, the save semantics
|
|
46
|
+
- **[Forms](./docs/forms.md)** — the remote-form seam: passing preflighted
|
|
47
|
+
forms in, keyed factories, the shared constants, the form building blocks
|
|
48
|
+
- **[Theming](./docs/theming.md)** — the design tokens and the base utilities
|
|
49
|
+
- **Component reference** — [primitives](./docs/components/primitives.md) ·
|
|
50
|
+
[chrome](./docs/components/chrome.md) ·
|
|
51
|
+
[content](./docs/components/content.md) ·
|
|
52
|
+
[community](./docs/components/community.md)
|
|
53
|
+
- **Storybook** — `npm run storybook`: every component has a story;
|
|
54
|
+
`Edit mode/EditMode` demos the whole editing loop against an in-memory
|
|
55
|
+
adapter
|
|
56
|
+
|
|
57
|
+
## Two design rules worth knowing before anything else
|
|
58
|
+
|
|
59
|
+
1. **The package never talks to a backend.** Content arrives as props; edits
|
|
60
|
+
leave through an injected `EditAdapter`; remote forms are passed in
|
|
61
|
+
preflighted by the host against its own schemas. An app that injects
|
|
62
|
+
nothing gets a read-only render, byte-identical to a build with no edit
|
|
63
|
+
mode at all.
|
|
64
|
+
2. **Shared constants have one owner.** The honeypot field name, the
|
|
65
|
+
newsletter-intent encoding, and the field bounds export from `./forms`;
|
|
66
|
+
host apps import them into their schemas so markup and server envelope
|
|
67
|
+
can never drift apart.
|
|
114
68
|
|
|
115
69
|
## Development
|
|
116
70
|
|
|
@@ -122,8 +76,12 @@ npm run check && npm run lint && npm run test:unit -- --run
|
|
|
122
76
|
npm run build # svelte-package + publint
|
|
123
77
|
```
|
|
124
78
|
|
|
125
|
-
|
|
126
|
-
`
|
|
79
|
+
Internally, components live in `src/lib/components/<website-group>/` with
|
|
80
|
+
stories in `components/stories/<group>/` and tests in
|
|
81
|
+
`components/tests/<group>/` — the layout they had in fndvit-website, kept
|
|
82
|
+
diffable against history. The public structure is the semantic entry points
|
|
83
|
+
above, assembled in `src/lib/{primitives,chrome,content-components,community}.ts`
|
|
84
|
+
and `src/lib/forms/index.ts`.
|
|
127
85
|
|
|
128
86
|
Releases: bump with `npm version`, push the `v*` tag, and the release
|
|
129
|
-
workflow publishes to npm.
|
|
87
|
+
workflow publishes to npm (requires the `NPM_TOKEN` secret).
|
package/dist/chrome.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Site chrome: the shell every page renders through, and the header/footer
|
|
3
|
+
* pair. Nav and Footer take the same SiteLink[] so the two can never drift.
|
|
4
|
+
*/
|
|
5
|
+
export { default as Footer } from './components/layout/Footer.svelte';
|
|
6
|
+
export { default as Nav } from './components/layout/Nav.svelte';
|
|
7
|
+
export { default as PageShell } from './components/layout/PageShell.svelte';
|
package/dist/chrome.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Site chrome: the shell every page renders through, and the header/footer
|
|
3
|
+
* pair. Nav and Footer take the same SiteLink[] so the two can never drift.
|
|
4
|
+
*/
|
|
5
|
+
export { default as Footer } from './components/layout/Footer.svelte';
|
|
6
|
+
export { default as Nav } from './components/layout/Nav.svelte';
|
|
7
|
+
export { default as PageShell } from './components/layout/PageShell.svelte';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The server-flow components: auth, account, newsletter, comments, reactions
|
|
3
|
+
* and contact. Every one takes its remote form(s) as required props — the
|
|
4
|
+
* host app owns schemas and preflight; see ./forms for the seam's types.
|
|
5
|
+
*/
|
|
6
|
+
export * from './components/account/index.js';
|
|
7
|
+
export * from './components/auth/index.js';
|
|
8
|
+
export * from './components/contact/index.js';
|
|
9
|
+
export { default as CommentSection } from './components/weeklies/CommentSection.svelte';
|
|
10
|
+
export type { CommentFormInstance } from './components/weeklies/CommentSection.svelte';
|
|
11
|
+
export { default as ReactionBar } from './components/weeklies/ReactionBar.svelte';
|
|
12
|
+
export type { CommentReactionChip, ReactionBarForms, WeeklyReactionChip } from './components/weeklies/ReactionBar.svelte';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The server-flow components: auth, account, newsletter, comments, reactions
|
|
3
|
+
* and contact. Every one takes its remote form(s) as required props — the
|
|
4
|
+
* host app owns schemas and preflight; see ./forms for the seam's types.
|
|
5
|
+
*/
|
|
6
|
+
export * from './components/account/index.js';
|
|
7
|
+
export * from './components/auth/index.js';
|
|
8
|
+
export * from './components/contact/index.js';
|
|
9
|
+
export { default as CommentSection } from './components/weeklies/CommentSection.svelte';
|
|
10
|
+
export { default as ReactionBar } from './components/weeklies/ReactionBar.svelte';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { getUiConfig } from '../../config/context.js';
|
|
3
|
+
import Editable from '../../edit/Editable.svelte';
|
|
3
4
|
import { withNewsletterIntent } from '../../forms/transport.js';
|
|
4
5
|
import type { NewsletterToggleFormInstance } from './AccountPanel.svelte';
|
|
5
6
|
import Button from '../ui/Button.svelte';
|
|
@@ -27,19 +28,33 @@
|
|
|
27
28
|
|
|
28
29
|
<section class="newsletter" aria-labelledby="newsletter-title">
|
|
29
30
|
<div class="inner band">
|
|
30
|
-
<
|
|
31
|
-
|
|
31
|
+
<Editable edit={config.messageEdit?.('newsletter_title')} value={msg.newsletter_title()}>
|
|
32
|
+
{#snippet children(text, attrs)}<h2 id="newsletter-title" {...attrs}>{text}</h2>{/snippet}
|
|
33
|
+
</Editable>
|
|
34
|
+
<Editable edit={config.messageEdit?.('newsletter_intro')} value={msg.newsletter_intro()}>
|
|
35
|
+
{#snippet children(text, attrs)}<p {...attrs}>{text}</p>{/snippet}
|
|
36
|
+
</Editable>
|
|
32
37
|
|
|
33
38
|
{#if account === null}
|
|
34
39
|
<p class="prompt">
|
|
35
|
-
|
|
40
|
+
<Editable
|
|
41
|
+
edit={config.messageEdit?.('newsletter_promptLoggedOut')}
|
|
42
|
+
value={msg.newsletter_promptLoggedOut()}
|
|
43
|
+
>
|
|
44
|
+
{#snippet children(text, attrs)}<span {...attrs}>{text}</span>{/snippet}
|
|
45
|
+
</Editable>
|
|
36
46
|
<Link href={withNewsletterIntent('/signup')}>{msg.comments_signupLink()}</Link>
|
|
37
47
|
·
|
|
38
48
|
<Link href={withNewsletterIntent('/login')}>{msg.comments_loginLink()}</Link>
|
|
39
49
|
</p>
|
|
40
50
|
{:else if account.newsletterSubscribed}
|
|
41
51
|
<p class="prompt">
|
|
42
|
-
|
|
52
|
+
<Editable
|
|
53
|
+
edit={config.messageEdit?.('newsletter_subscribedNote')}
|
|
54
|
+
value={msg.newsletter_subscribedNote()}
|
|
55
|
+
>
|
|
56
|
+
{#snippet children(text, attrs)}<span {...attrs}>{text}</span>{/snippet}
|
|
57
|
+
</Editable>
|
|
43
58
|
<Link href="/account">{msg.account_navLabel()}</Link>
|
|
44
59
|
</p>
|
|
45
60
|
{:else}
|
|
@@ -30,8 +30,12 @@
|
|
|
30
30
|
</script>
|
|
31
31
|
|
|
32
32
|
{#if jobs.length === 0}
|
|
33
|
-
<
|
|
34
|
-
|
|
33
|
+
<Editable edit={config.messageEdit?.('jobs_empty')} value={msg.jobs_empty()}>
|
|
34
|
+
{#snippet children(text, attrs)}<p class="empty" {...attrs}>{text}</p>{/snippet}
|
|
35
|
+
</Editable>
|
|
36
|
+
<Editable edit={config.messageEdit?.('jobs_newsletterNudge')} value={msg.jobs_newsletterNudge()}>
|
|
37
|
+
{#snippet children(text, attrs)}<p class="nudge" {...attrs}>{text}</p>{/snippet}
|
|
38
|
+
</Editable>
|
|
35
39
|
{:else}
|
|
36
40
|
<ul>
|
|
37
41
|
{#each jobs as job (job.slug)}
|
|
@@ -49,7 +53,9 @@
|
|
|
49
53
|
</li>
|
|
50
54
|
{/each}
|
|
51
55
|
</ul>
|
|
52
|
-
<
|
|
56
|
+
<Editable edit={config.messageEdit?.('jobs_newsletterNudge')} value={msg.jobs_newsletterNudge()}>
|
|
57
|
+
{#snippet children(text, attrs)}<p class="nudge" {...attrs}>{text}</p>{/snippet}
|
|
58
|
+
</Editable>
|
|
53
59
|
{/if}
|
|
54
60
|
|
|
55
61
|
<style>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { getUiConfig } from '../../config/context.js';
|
|
3
3
|
import type { SiteLink } from '../../config/types.js';
|
|
4
|
+
import Editable from '../../edit/Editable.svelte';
|
|
4
5
|
import Link from '../ui/Link.svelte';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -29,7 +30,15 @@
|
|
|
29
30
|
<li><Link href={link.href}>{link.label}</Link></li>
|
|
30
31
|
{/each}
|
|
31
32
|
</ul>
|
|
32
|
-
<p class="rights"
|
|
33
|
+
<p class="rights">
|
|
34
|
+
© {year}
|
|
35
|
+
<Editable
|
|
36
|
+
edit={config.messageEdit?.('footer_rights')}
|
|
37
|
+
value={config.messages.footer_rights()}
|
|
38
|
+
>
|
|
39
|
+
{#snippet children(text, attrs)}<span {...attrs}>{text}</span>{/snippet}
|
|
40
|
+
</Editable>
|
|
41
|
+
</p>
|
|
33
42
|
</div>
|
|
34
43
|
</footer>
|
|
35
44
|
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
<script lang="ts">
|
|
12
12
|
import { getUiConfig } from '../../config/context.js';
|
|
13
|
+
import Editable from '../../edit/Editable.svelte';
|
|
13
14
|
import { COMMENT_BODY } from '../../forms/constraints.js';
|
|
14
15
|
import type { CommentData, CommentThreadData } from '../../content/types.js';
|
|
15
16
|
import Button from '../ui/Button.svelte';
|
|
@@ -67,10 +68,16 @@
|
|
|
67
68
|
{/snippet}
|
|
68
69
|
|
|
69
70
|
<section aria-labelledby="comments-heading">
|
|
70
|
-
<
|
|
71
|
+
<Editable edit={config.messageEdit?.('comments_title')} value={msg.comments_title()}>
|
|
72
|
+
{#snippet children(text, attrs)}
|
|
73
|
+
<h2 class="subsection-heading" id="comments-heading" {...attrs}>{text}</h2>
|
|
74
|
+
{/snippet}
|
|
75
|
+
</Editable>
|
|
71
76
|
|
|
72
77
|
{#if comments.length === 0}
|
|
73
|
-
<
|
|
78
|
+
<Editable edit={config.messageEdit?.('comments_empty')} value={msg.comments_empty()}>
|
|
79
|
+
{#snippet children(text, attrs)}<p class="empty" {...attrs}>{text}</p>{/snippet}
|
|
80
|
+
</Editable>
|
|
74
81
|
{:else}
|
|
75
82
|
<ul class="list">
|
|
76
83
|
{#each comments as thread (thread.id)}
|
|
@@ -148,7 +155,12 @@
|
|
|
148
155
|
</form>
|
|
149
156
|
{:else}
|
|
150
157
|
<p class="login-prompt">
|
|
151
|
-
|
|
158
|
+
<Editable
|
|
159
|
+
edit={config.messageEdit?.('comments_loginPrompt')}
|
|
160
|
+
value={msg.comments_loginPrompt()}
|
|
161
|
+
>
|
|
162
|
+
{#snippet children(text, attrs)}<span {...attrs}>{text}</span>{/snippet}
|
|
163
|
+
</Editable>
|
|
152
164
|
<Link href="/login">{msg.comments_loginLink()}</Link>
|
|
153
165
|
·
|
|
154
166
|
<Link href="/signup">{msg.comments_signupLink()}</Link>
|
package/dist/config/context.js
CHANGED
|
@@ -37,7 +37,10 @@ export function setUiConfig(input) {
|
|
|
37
37
|
},
|
|
38
38
|
get messages() {
|
|
39
39
|
return input().messages ?? defaultMessages;
|
|
40
|
-
}
|
|
40
|
+
},
|
|
41
|
+
// Undefined means inert: components render their message strings as
|
|
42
|
+
// plain text, which is every app but the CMS.
|
|
43
|
+
messageEdit: (key) => input().messageEdit?.(key)
|
|
41
44
|
};
|
|
42
45
|
return setContext(KEY, resolved);
|
|
43
46
|
}
|
package/dist/config/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { EditDescriptor } from '../edit/types.js';
|
|
1
2
|
/**
|
|
2
3
|
* The site's locales and their canonical member. Fixed rather than generic:
|
|
3
4
|
* `ca` is load-bearing — it is the required key of every localized column
|
|
@@ -156,6 +157,14 @@ export interface UiConfig {
|
|
|
156
157
|
/** Appended to document titles and shown as the wordmark. */
|
|
157
158
|
siteName: string;
|
|
158
159
|
messages: UiMessages;
|
|
160
|
+
/**
|
|
161
|
+
* Edit descriptor for one of THIS config's message strings, by catalog
|
|
162
|
+
* key — the interface-wording half of edit mode. Optional and undefined by
|
|
163
|
+
* default, so a read-only app (and every story/test) renders the messages
|
|
164
|
+
* as plain text. A CMS-shaped host supplies it alongside an EditAdapter;
|
|
165
|
+
* components offer only their parameterless, plain-text message sites.
|
|
166
|
+
*/
|
|
167
|
+
messageEdit?: (key: string) => EditDescriptor | undefined;
|
|
159
168
|
}
|
|
160
169
|
/** What an app hands UiProvider: any subset; the rest keeps package defaults. */
|
|
161
170
|
export interface UiConfigInput {
|
|
@@ -166,4 +175,5 @@ export interface UiConfigInput {
|
|
|
166
175
|
canonicalPathname?: UiConfig['canonicalPathname'];
|
|
167
176
|
siteName?: string;
|
|
168
177
|
messages?: UiMessages;
|
|
178
|
+
messageEdit?: UiConfig['messageEdit'];
|
|
169
179
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content renderers: the components that present database content — cards,
|
|
3
|
+
* the timeline, listings. All prop-driven, all edit-mode capable, plus the
|
|
4
|
+
* structural data shapes they consume.
|
|
5
|
+
*/
|
|
6
|
+
export { default as CollaboratorList } from './components/team/CollaboratorList.svelte';
|
|
7
|
+
export { default as JobList } from './components/jobs/JobList.svelte';
|
|
8
|
+
export type { JobEditMap } from './components/jobs/JobList.svelte';
|
|
9
|
+
export { default as ProjectCard } from './components/projects/ProjectCard.svelte';
|
|
10
|
+
export type { ProjectEditMap } from './components/projects/ProjectCard.svelte';
|
|
11
|
+
export { default as SortSelect } from './components/weeklies/SortSelect.svelte';
|
|
12
|
+
export { default as TeamMemberCard } from './components/team/TeamMemberCard.svelte';
|
|
13
|
+
export type { TeamMemberEditMap } from './components/team/TeamMemberCard.svelte';
|
|
14
|
+
export { default as Timeline } from './components/timeline/Timeline.svelte';
|
|
15
|
+
export { default as TimelineMilestone } from './components/timeline/TimelineMilestone.svelte';
|
|
16
|
+
export type { MilestoneEditMap } from './components/timeline/TimelineMilestone.svelte';
|
|
17
|
+
export { default as WeeklieCard } from './components/weeklies/WeeklieCard.svelte';
|
|
18
|
+
export type { WeeklyEditMap } from './components/weeklies/WeeklieCard.svelte';
|
|
19
|
+
export * from './content/index.js';
|
|
20
|
+
export { MILESTONE_CATEGORY_COLOR, milestoneCategoryLabel } from './utils/milestones.js';
|
|
21
|
+
export { contactCategoryLabel } from './utils/contact.js';
|
|
22
|
+
export { formatDate, yearOf } from './utils/dates.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content renderers: the components that present database content — cards,
|
|
3
|
+
* the timeline, listings. All prop-driven, all edit-mode capable, plus the
|
|
4
|
+
* structural data shapes they consume.
|
|
5
|
+
*/
|
|
6
|
+
export { default as CollaboratorList } from './components/team/CollaboratorList.svelte';
|
|
7
|
+
export { default as JobList } from './components/jobs/JobList.svelte';
|
|
8
|
+
export { default as ProjectCard } from './components/projects/ProjectCard.svelte';
|
|
9
|
+
export { default as SortSelect } from './components/weeklies/SortSelect.svelte';
|
|
10
|
+
export { default as TeamMemberCard } from './components/team/TeamMemberCard.svelte';
|
|
11
|
+
export { default as Timeline } from './components/timeline/Timeline.svelte';
|
|
12
|
+
export { default as TimelineMilestone } from './components/timeline/TimelineMilestone.svelte';
|
|
13
|
+
export { default as WeeklieCard } from './components/weeklies/WeeklieCard.svelte';
|
|
14
|
+
export * from './content/index.js';
|
|
15
|
+
export { MILESTONE_CATEGORY_COLOR, milestoneCategoryLabel } from './utils/milestones.js';
|
|
16
|
+
export { contactCategoryLabel } from './utils/contact.js';
|
|
17
|
+
export { formatDate, yearOf } from './utils/dates.js';
|
package/dist/edit/helpers.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import type { Locale } from '../config/types.js';
|
|
2
2
|
import type { EditableEntity, EditDescriptor } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Descriptor for one interface-wording message (a Paraglide catalog key).
|
|
5
|
+
* Only parameterless messages are sensibly editable in place — editing the
|
|
6
|
+
* RENDERED text of a parameterized one would overwrite its template.
|
|
7
|
+
*/
|
|
8
|
+
export declare function chromeEdit(key: string, locale: Locale, options?: {
|
|
9
|
+
format?: EditDescriptor['format'];
|
|
10
|
+
label?: string;
|
|
11
|
+
}): EditDescriptor;
|
|
3
12
|
/** Descriptor for one page-copy block (`page_content` row). */
|
|
4
13
|
export declare function pageCopyEdit(page: string, sectionKey: string, locale: Locale, options?: {
|
|
5
14
|
format?: EditDescriptor['format'];
|
package/dist/edit/helpers.js
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Descriptor for one interface-wording message (a Paraglide catalog key).
|
|
3
|
+
* Only parameterless messages are sensibly editable in place — editing the
|
|
4
|
+
* RENDERED text of a parameterized one would overwrite its template.
|
|
5
|
+
*/
|
|
6
|
+
export function chromeEdit(key, locale, options) {
|
|
7
|
+
return { ref: { kind: 'chrome', key }, locale, ...options };
|
|
8
|
+
}
|
|
1
9
|
/** Descriptor for one page-copy block (`page_content` row). */
|
|
2
10
|
export function pageCopyEdit(page, sectionKey, locale, options) {
|
|
3
11
|
return { ref: { kind: 'page-copy', page, sectionKey }, locale, ...options };
|
package/dist/edit/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { getEditAdapter, setEditAdapter } from './context.js';
|
|
2
2
|
export { default as Editable } from './Editable.svelte';
|
|
3
|
-
export { entityEdit, pageCopyEdit } from './helpers.js';
|
|
3
|
+
export { chromeEdit, entityEdit, pageCopyEdit } from './helpers.js';
|
|
4
4
|
export { localize } from './types.js';
|
|
5
5
|
export type { ContentRef, EditableEntity, EditAdapter, EditDescriptor, LocalizedText } from './types.js';
|
package/dist/edit/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { getEditAdapter, setEditAdapter } from './context.js';
|
|
2
2
|
export { default as Editable } from './Editable.svelte';
|
|
3
|
-
export { entityEdit, pageCopyEdit } from './helpers.js';
|
|
3
|
+
export { chromeEdit, entityEdit, pageCopyEdit } from './helpers.js';
|
|
4
4
|
export { localize } from './types.js';
|
package/dist/edit/types.d.ts
CHANGED
|
@@ -17,8 +17,12 @@ export declare function localize(value: LocalizedText, locale: Locale): string;
|
|
|
17
17
|
*/
|
|
18
18
|
export type EditableEntity = 'weeklies' | 'projects' | 'milestones' | 'team_members' | 'job_openings';
|
|
19
19
|
/**
|
|
20
|
-
* The identity of one editable string in the content model:
|
|
21
|
-
*
|
|
20
|
+
* The identity of one editable string in the content model: a page copy
|
|
21
|
+
* block (`page_content` row), one localized field of a domain entity, or one
|
|
22
|
+
* interface-wording message (`chrome`) — a Paraglide catalog key. What a
|
|
23
|
+
* chrome save MEANS is the host's decision, like every other ref: the
|
|
24
|
+
* foundation CMS writes the website's `ui_messages` table, from which the
|
|
25
|
+
* catalogs are regenerated at deploy.
|
|
22
26
|
*/
|
|
23
27
|
export type ContentRef = {
|
|
24
28
|
kind: 'page-copy';
|
|
@@ -29,6 +33,9 @@ export type ContentRef = {
|
|
|
29
33
|
entity: EditableEntity;
|
|
30
34
|
id: string | number;
|
|
31
35
|
field: string;
|
|
36
|
+
} | {
|
|
37
|
+
kind: 'chrome';
|
|
38
|
+
key: string;
|
|
32
39
|
};
|
|
33
40
|
/** What a component hands the adapter alongside the new value. */
|
|
34
41
|
export interface EditDescriptor {
|
package/dist/forms/index.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
export { default as Field } from '../components/ui/Field.svelte';
|
|
2
|
+
export { default as FormErrorFeedback } from '../components/ui/FormErrorFeedback.svelte';
|
|
3
|
+
export { default as FormFeedback } from '../components/ui/FormFeedback.svelte';
|
|
4
|
+
export { default as FormResultSlot } from '../components/ui/FormResultSlot.svelte';
|
|
5
|
+
export { default as Honeypot } from '../components/ui/Honeypot.svelte';
|
|
6
|
+
export { default as LocaleField } from '../components/ui/LocaleField.svelte';
|
|
7
|
+
export { default as NewsletterIntentField } from '../components/ui/NewsletterIntentField.svelte';
|
|
1
8
|
export { COMMENT_BODY, CONTACT_MESSAGE, CONTACT_NAME, DISPLAY_NAME, EMAIL, LOGIN_PASSWORD, PASSWORD } from './constraints.js';
|
|
2
9
|
export { hasNewsletterIntent, HONEYPOT_FIELD, isNewsletterIntent, NEWSLETTER_INTENT_PARAM, NEWSLETTER_INTENT_VALUE, withNewsletterIntent } from './transport.js';
|
|
3
10
|
export type { FormFail, FormFieldIssue, FormResultLike, FormResultOf, KeyedRemoteForms, RemoteField, RemoteFormAttributes, RemoteFormInstance } from './types.js';
|
package/dist/forms/index.js
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
|
+
export { default as Field } from '../components/ui/Field.svelte';
|
|
2
|
+
export { default as FormErrorFeedback } from '../components/ui/FormErrorFeedback.svelte';
|
|
3
|
+
export { default as FormFeedback } from '../components/ui/FormFeedback.svelte';
|
|
4
|
+
export { default as FormResultSlot } from '../components/ui/FormResultSlot.svelte';
|
|
5
|
+
export { default as Honeypot } from '../components/ui/Honeypot.svelte';
|
|
6
|
+
export { default as LocaleField } from '../components/ui/LocaleField.svelte';
|
|
7
|
+
export { default as NewsletterIntentField } from '../components/ui/NewsletterIntentField.svelte';
|
|
1
8
|
export { COMMENT_BODY, CONTACT_MESSAGE, CONTACT_NAME, DISPLAY_NAME, EMAIL, LOGIN_PASSWORD, PASSWORD } from './constraints.js';
|
|
2
9
|
export { hasNewsletterIntent, HONEYPOT_FIELD, isNewsletterIntent, NEWSLETTER_INTENT_PARAM, NEWSLETTER_INTENT_VALUE, withNewsletterIntent } from './transport.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,18 +1,8 @@
|
|
|
1
|
-
export * from './
|
|
2
|
-
export * from './
|
|
3
|
-
export * from './components
|
|
4
|
-
export * from './
|
|
5
|
-
export * from './components/layout/index.js';
|
|
6
|
-
export * from './components/weeklies/index.js';
|
|
7
|
-
export * from './components/projects/index.js';
|
|
8
|
-
export * from './components/team/index.js';
|
|
9
|
-
export * from './components/timeline/index.js';
|
|
10
|
-
export * from './components/jobs/index.js';
|
|
1
|
+
export * from './primitives.js';
|
|
2
|
+
export * from './chrome.js';
|
|
3
|
+
export * from './content-components.js';
|
|
4
|
+
export * from './community.js';
|
|
11
5
|
export * from './forms/index.js';
|
|
12
6
|
export * from './config/index.js';
|
|
13
7
|
export * from './edit/index.js';
|
|
14
|
-
export * from './content/index.js';
|
|
15
|
-
export { formatDate, yearOf } from './utils/dates.js';
|
|
16
|
-
export { contactCategoryLabel } from './utils/contact.js';
|
|
17
|
-
export { MILESTONE_CATEGORY_COLOR, milestoneCategoryLabel } from './utils/milestones.js';
|
|
18
8
|
export { isPathUnder } from './utils/paths.js';
|
package/dist/index.js
CHANGED
|
@@ -1,23 +1,15 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export * from './
|
|
5
|
-
export * from './
|
|
6
|
-
export * from './components
|
|
7
|
-
export * from './
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export * from './components/timeline/index.js';
|
|
11
|
-
export * from './components/jobs/index.js';
|
|
12
|
-
// The form seam: structural remote-form types, transport constants, field bounds.
|
|
1
|
+
// The whole surface, flat — importable piecemeal through the semantic
|
|
2
|
+
// subpaths: ./primitives, ./forms, ./chrome, ./content, ./community,
|
|
3
|
+
// ./edit, ./config, ./testing.
|
|
4
|
+
export * from './primitives.js';
|
|
5
|
+
export * from './chrome.js';
|
|
6
|
+
export * from './content-components.js';
|
|
7
|
+
export * from './community.js';
|
|
8
|
+
// The form seam: form components, structural remote-form types, transport
|
|
9
|
+
// constants, field bounds.
|
|
13
10
|
export * from './forms/index.js';
|
|
14
11
|
// App wiring: configuration context and the edit-mode contract.
|
|
15
12
|
export * from './config/index.js';
|
|
16
13
|
export * from './edit/index.js';
|
|
17
|
-
//
|
|
18
|
-
export * from './content/index.js';
|
|
19
|
-
// Utilities the components share with their host apps.
|
|
20
|
-
export { formatDate, yearOf } from './utils/dates.js';
|
|
21
|
-
export { contactCategoryLabel } from './utils/contact.js';
|
|
22
|
-
export { MILESTONE_CATEGORY_COLOR, milestoneCategoryLabel } from './utils/milestones.js';
|
|
14
|
+
// Path utility shared with host apps.
|
|
23
15
|
export { isPathUnder } from './utils/paths.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic UI atoms: buttons, cards, text, media, list controls. Domain-free —
|
|
3
|
+
* everything here renders whatever it is handed.
|
|
4
|
+
*/
|
|
5
|
+
export { default as Button } from './components/ui/Button.svelte';
|
|
6
|
+
export { default as CardMedia } from './components/ui/CardMedia.svelte';
|
|
7
|
+
export { default as CardTitle } from './components/ui/CardTitle.svelte';
|
|
8
|
+
export { default as CopyIntro } from './components/ui/CopyIntro.svelte';
|
|
9
|
+
export { default as DateText } from './components/ui/DateText.svelte';
|
|
10
|
+
export { default as DecorShapes } from './components/ui/DecorShapes.svelte';
|
|
11
|
+
export { default as FilterChips } from './components/ui/FilterChips.svelte';
|
|
12
|
+
export { default as GhostButton } from './components/ui/GhostButton.svelte';
|
|
13
|
+
export { default as Link } from './components/ui/Link.svelte';
|
|
14
|
+
export { default as Pagination } from './components/ui/Pagination.svelte';
|
|
15
|
+
export { default as RichText } from './components/ui/RichText.svelte';
|
|
16
|
+
export { default as SearchInput } from './components/ui/SearchInput.svelte';
|
|
17
|
+
export { default as ShareRow } from './components/ui/ShareRow.svelte';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic UI atoms: buttons, cards, text, media, list controls. Domain-free —
|
|
3
|
+
* everything here renders whatever it is handed.
|
|
4
|
+
*/
|
|
5
|
+
export { default as Button } from './components/ui/Button.svelte';
|
|
6
|
+
export { default as CardMedia } from './components/ui/CardMedia.svelte';
|
|
7
|
+
export { default as CardTitle } from './components/ui/CardTitle.svelte';
|
|
8
|
+
export { default as CopyIntro } from './components/ui/CopyIntro.svelte';
|
|
9
|
+
export { default as DateText } from './components/ui/DateText.svelte';
|
|
10
|
+
export { default as DecorShapes } from './components/ui/DecorShapes.svelte';
|
|
11
|
+
export { default as FilterChips } from './components/ui/FilterChips.svelte';
|
|
12
|
+
export { default as GhostButton } from './components/ui/GhostButton.svelte';
|
|
13
|
+
export { default as Link } from './components/ui/Link.svelte';
|
|
14
|
+
export { default as Pagination } from './components/ui/Pagination.svelte';
|
|
15
|
+
export { default as RichText } from './components/ui/RichText.svelte';
|
|
16
|
+
export { default as SearchInput } from './components/ui/SearchInput.svelte';
|
|
17
|
+
export { default as ShareRow } from './components/ui/ShareRow.svelte';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vit-foundation/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "vite dev",
|
|
6
6
|
"build": "vite build && npm run prepack",
|
|
@@ -37,55 +37,30 @@
|
|
|
37
37
|
"svelte": "./dist/index.js",
|
|
38
38
|
"default": "./dist/index.js"
|
|
39
39
|
},
|
|
40
|
-
"./
|
|
41
|
-
"types": "./dist/
|
|
42
|
-
"svelte": "./dist/
|
|
43
|
-
"default": "./dist/
|
|
40
|
+
"./primitives": {
|
|
41
|
+
"types": "./dist/primitives.d.ts",
|
|
42
|
+
"svelte": "./dist/primitives.js",
|
|
43
|
+
"default": "./dist/primitives.js"
|
|
44
44
|
},
|
|
45
|
-
"./
|
|
46
|
-
"types": "./dist/
|
|
47
|
-
"svelte": "./dist/
|
|
48
|
-
"default": "./dist/
|
|
49
|
-
},
|
|
50
|
-
"./account": {
|
|
51
|
-
"types": "./dist/components/account/index.d.ts",
|
|
52
|
-
"svelte": "./dist/components/account/index.js",
|
|
53
|
-
"default": "./dist/components/account/index.js"
|
|
54
|
-
},
|
|
55
|
-
"./contact": {
|
|
56
|
-
"types": "./dist/components/contact/index.d.ts",
|
|
57
|
-
"svelte": "./dist/components/contact/index.js",
|
|
58
|
-
"default": "./dist/components/contact/index.js"
|
|
59
|
-
},
|
|
60
|
-
"./layout": {
|
|
61
|
-
"types": "./dist/components/layout/index.d.ts",
|
|
62
|
-
"svelte": "./dist/components/layout/index.js",
|
|
63
|
-
"default": "./dist/components/layout/index.js"
|
|
64
|
-
},
|
|
65
|
-
"./weeklies": {
|
|
66
|
-
"types": "./dist/components/weeklies/index.d.ts",
|
|
67
|
-
"svelte": "./dist/components/weeklies/index.js",
|
|
68
|
-
"default": "./dist/components/weeklies/index.js"
|
|
69
|
-
},
|
|
70
|
-
"./projects": {
|
|
71
|
-
"types": "./dist/components/projects/index.d.ts",
|
|
72
|
-
"svelte": "./dist/components/projects/index.js",
|
|
73
|
-
"default": "./dist/components/projects/index.js"
|
|
45
|
+
"./forms": {
|
|
46
|
+
"types": "./dist/forms/index.d.ts",
|
|
47
|
+
"svelte": "./dist/forms/index.js",
|
|
48
|
+
"default": "./dist/forms/index.js"
|
|
74
49
|
},
|
|
75
|
-
"./
|
|
76
|
-
"types": "./dist/
|
|
77
|
-
"svelte": "./dist/
|
|
78
|
-
"default": "./dist/
|
|
50
|
+
"./chrome": {
|
|
51
|
+
"types": "./dist/chrome.d.ts",
|
|
52
|
+
"svelte": "./dist/chrome.js",
|
|
53
|
+
"default": "./dist/chrome.js"
|
|
79
54
|
},
|
|
80
|
-
"./
|
|
81
|
-
"types": "./dist/components
|
|
82
|
-
"svelte": "./dist/components
|
|
83
|
-
"default": "./dist/components
|
|
55
|
+
"./content": {
|
|
56
|
+
"types": "./dist/content-components.d.ts",
|
|
57
|
+
"svelte": "./dist/content-components.js",
|
|
58
|
+
"default": "./dist/content-components.js"
|
|
84
59
|
},
|
|
85
|
-
"./
|
|
86
|
-
"types": "./dist/
|
|
87
|
-
"svelte": "./dist/
|
|
88
|
-
"default": "./dist/
|
|
60
|
+
"./community": {
|
|
61
|
+
"types": "./dist/community.d.ts",
|
|
62
|
+
"svelte": "./dist/community.js",
|
|
63
|
+
"default": "./dist/community.js"
|
|
89
64
|
},
|
|
90
65
|
"./edit": {
|
|
91
66
|
"types": "./dist/edit/index.d.ts",
|
|
@@ -97,11 +72,6 @@
|
|
|
97
72
|
"svelte": "./dist/config/index.js",
|
|
98
73
|
"default": "./dist/config/index.js"
|
|
99
74
|
},
|
|
100
|
-
"./forms": {
|
|
101
|
-
"types": "./dist/forms/index.d.ts",
|
|
102
|
-
"svelte": "./dist/forms/index.js",
|
|
103
|
-
"default": "./dist/forms/index.js"
|
|
104
|
-
},
|
|
105
75
|
"./testing": {
|
|
106
76
|
"types": "./dist/testing/remote-form.d.ts",
|
|
107
77
|
"svelte": "./dist/testing/remote-form.js",
|