@vit-foundation/ui 0.24.2 → 0.26.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 +11 -11
- package/dist/community.d.ts +12 -3
- package/dist/community.js +7 -3
- package/dist/components/pages/GetInvolvedPage.svelte +139 -0
- package/dist/components/pages/GetInvolvedPage.svelte.d.ts +36 -0
- package/dist/components/pages/HomePage.svelte +256 -0
- package/dist/components/pages/HomePage.svelte.d.ts +36 -0
- package/dist/components/pages/LegalPage.svelte +39 -0
- package/dist/components/pages/LegalPage.svelte.d.ts +14 -0
- package/dist/components/pages/ProjectPage.svelte +122 -0
- package/dist/components/pages/ProjectPage.svelte.d.ts +15 -0
- package/dist/components/pages/TransparencyPage.svelte +164 -0
- package/dist/components/pages/TransparencyPage.svelte.d.ts +40 -0
- package/dist/components/pages/WeekliesPage.svelte +184 -0
- package/dist/components/pages/WeekliesPage.svelte.d.ts +46 -0
- package/dist/components/pages/WeeklyPage.svelte +198 -0
- package/dist/components/pages/WeeklyPage.svelte.d.ts +33 -0
- package/dist/components/pages/WhatWeDoPage.svelte +107 -0
- package/dist/components/pages/WhatWeDoPage.svelte.d.ts +27 -0
- package/dist/components/pages/WhoWeArePage.svelte +108 -0
- package/dist/components/pages/WhoWeArePage.svelte.d.ts +32 -0
- package/dist/components/ui/Modal.svelte +32 -11
- package/dist/components/ui/Modal.svelte.d.ts +17 -1
- package/dist/config/context.d.ts +0 -6
- package/dist/config/context.js +1 -1
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.js +1 -1
- package/dist/config/messages.js +22 -0
- package/dist/config/types.d.ts +36 -0
- package/dist/content/pages.d.ts +64 -0
- package/dist/content/pages.js +71 -0
- package/dist/content/types.d.ts +45 -0
- package/dist/content/types.js +0 -5
- package/dist/content-components.d.ts +22 -1
- package/dist/content-components.js +17 -1
- package/dist/contract.d.ts +11 -4
- package/dist/contract.js +11 -3
- package/dist/edit/Editable.svelte +17 -161
- package/dist/edit/Editable.svelte.d.ts +2 -50
- package/dist/edit/chrome/AddSlot.svelte +11 -78
- package/dist/edit/chrome/AddSlot.svelte.d.ts +2 -15
- package/dist/edit/chrome/EditFrame.svelte +15 -158
- package/dist/edit/chrome/EditFrame.svelte.d.ts +2 -31
- package/dist/edit/chrome/EditPanel.svelte +14 -22
- package/dist/edit/chrome/EditPanel.svelte.d.ts +2 -13
- package/dist/edit/chrome/LinkEdit.svelte +13 -277
- package/dist/edit/chrome/LinkEdit.svelte.d.ts +2 -48
- package/dist/edit/chrome-props.d.ts +96 -0
- package/dist/edit/chrome-props.js +1 -0
- package/dist/edit/context.d.ts +38 -5
- package/dist/edit/context.js +18 -7
- package/dist/edit/index.d.ts +6 -4
- package/dist/edit/index.js +4 -3
- package/dist/edit/live/AddSlot.svelte +83 -0
- package/dist/edit/live/AddSlot.svelte.d.ts +4 -0
- package/dist/edit/live/EditFrame.svelte +153 -0
- package/dist/edit/live/EditFrame.svelte.d.ts +4 -0
- package/dist/edit/live/EditPanel.svelte +25 -0
- package/dist/edit/live/EditPanel.svelte.d.ts +4 -0
- package/dist/edit/live/Editable.svelte +162 -0
- package/dist/edit/live/Editable.svelte.d.ts +4 -0
- package/dist/edit/live/LinkEdit.svelte +276 -0
- package/dist/edit/live/LinkEdit.svelte.d.ts +4 -0
- package/dist/edit/{chrome → live}/PropertyRow.svelte +1 -1
- package/dist/edit/{commit.svelte.d.ts → live/commit.svelte.d.ts} +2 -2
- package/dist/edit/live/index.d.ts +10 -0
- package/dist/edit/live/index.js +14 -0
- package/package.json +1 -1
- package/dist/components/account/index.d.ts +0 -3
- package/dist/components/account/index.js +0 -2
- package/dist/components/auth/index.d.ts +0 -7
- package/dist/components/auth/index.js +0 -4
- package/dist/components/contact/index.d.ts +0 -2
- package/dist/components/contact/index.js +0 -1
- package/dist/content/index.d.ts +0 -4
- package/dist/content/index.js +0 -2
- /package/dist/edit/{chrome → live}/ConfirmDialog.svelte +0 -0
- /package/dist/edit/{chrome → live}/ConfirmDialog.svelte.d.ts +0 -0
- /package/dist/edit/{chrome → live}/EditPopover.svelte +0 -0
- /package/dist/edit/{chrome → live}/EditPopover.svelte.d.ts +0 -0
- /package/dist/edit/{chrome → live}/PropertyRow.svelte.d.ts +0 -0
- /package/dist/edit/{commit.svelte.js → live/commit.svelte.js} +0 -0
package/README.md
CHANGED
|
@@ -22,17 +22,17 @@ Requires `svelte` ^5.0.0 as a peer. Import the two stylesheets once:
|
|
|
22
22
|
|
|
23
23
|
Everything exports flat from the root, and again grouped by role:
|
|
24
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, helpers and the two list rules (createWeeklyList, createUrlFilters) |
|
|
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, collectionEditing, LocalizedText
|
|
33
|
-
| [`/config`](./docs/getting-started.md#wiring-an-app-uiprovider) | UiProvider, UiConfig, the locale set, the default Catalan messages
|
|
34
|
-
| `/contract` | The component-free half: LOCALES, BASE_LOCALE, localize, REACTIONS and the edit-descriptor types — the one subpath a host may import from SERVER code
|
|
35
|
-
| `/testing` | createRemoteFormMock — the remote-form stand-in for stories and tests
|
|
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, the nine page modules (HomePage … WeeklyPage) — plus the data shapes, the page-copy vocabulary, helpers and the two list rules (createWeeklyList, createUrlFilters) |
|
|
31
|
+
| [`/community`](./docs/components/community.md) | AuthPageShell, LoginForm, SignupForm, GoogleAuthForm, AccountPanel, NewsletterSignup, CommentSection, ReactionBar, ContactForm |
|
|
32
|
+
| [`/edit`](./docs/edit-mode.md) | Editable, setEditAdapter(adapter, EDIT_CHROME)/getEditAdapter, descriptors and helpers, collectionEditing, LocalizedText |
|
|
33
|
+
| [`/config`](./docs/getting-started.md#wiring-an-app-uiprovider) | UiProvider, UiConfig, the locale set, the default Catalan messages |
|
|
34
|
+
| `/contract` | The component-free half: LOCALES, BASE_LOCALE, localize, REACTIONS, PAGE_COPY_KEYS and the edit-descriptor types — the one subpath a host may import from SERVER code |
|
|
35
|
+
| `/testing` | createRemoteFormMock — the remote-form stand-in for stories and tests |
|
|
36
36
|
|
|
37
37
|
Every component renders standalone (Catalan copy, identity hrefs); an app
|
|
38
38
|
integrates its i18n and router through one `UiProvider` in the root layout.
|
package/dist/community.d.ts
CHANGED
|
@@ -3,9 +3,18 @@
|
|
|
3
3
|
* and contact. Every one takes its remote form(s) as required props — the
|
|
4
4
|
* host app owns schemas and preflight; see ./forms for the seam's types.
|
|
5
5
|
*/
|
|
6
|
-
export
|
|
7
|
-
export
|
|
8
|
-
export
|
|
6
|
+
export { default as AccountPanel } from './components/account/AccountPanel.svelte';
|
|
7
|
+
export type { DeleteAccountFormInstance, LogoutFormInstance, NewsletterToggleFormInstance, UpdateNameFormInstance } from './components/account/AccountPanel.svelte';
|
|
8
|
+
export { default as NewsletterSignup } from './components/account/NewsletterSignup.svelte';
|
|
9
|
+
export { default as AuthPageShell } from './components/auth/AuthPageShell.svelte';
|
|
10
|
+
export { default as GoogleAuthForm } from './components/auth/GoogleAuthForm.svelte';
|
|
11
|
+
export type { GoogleLoginFormInstance } from './components/auth/GoogleAuthForm.svelte';
|
|
12
|
+
export { default as LoginForm } from './components/auth/LoginForm.svelte';
|
|
13
|
+
export type { LoginFormInstance, MagicLinkFormInstance } from './components/auth/LoginForm.svelte';
|
|
14
|
+
export { default as SignupForm } from './components/auth/SignupForm.svelte';
|
|
15
|
+
export type { SignupFormInstance } from './components/auth/SignupForm.svelte';
|
|
16
|
+
export { default as ContactForm } from './components/contact/ContactForm.svelte';
|
|
17
|
+
export type { ContactFormInstance } from './components/contact/ContactForm.svelte';
|
|
9
18
|
export { default as CommentSection } from './components/weeklies/CommentSection.svelte';
|
|
10
19
|
export type { CommentFormInstance } from './components/weeklies/CommentSection.svelte';
|
|
11
20
|
export { default as ReactionBar } from './components/weeklies/ReactionBar.svelte';
|
package/dist/community.js
CHANGED
|
@@ -3,8 +3,12 @@
|
|
|
3
3
|
* and contact. Every one takes its remote form(s) as required props — the
|
|
4
4
|
* host app owns schemas and preflight; see ./forms for the seam's types.
|
|
5
5
|
*/
|
|
6
|
-
export
|
|
7
|
-
export
|
|
8
|
-
export
|
|
6
|
+
export { default as AccountPanel } from './components/account/AccountPanel.svelte';
|
|
7
|
+
export { default as NewsletterSignup } from './components/account/NewsletterSignup.svelte';
|
|
8
|
+
export { default as AuthPageShell } from './components/auth/AuthPageShell.svelte';
|
|
9
|
+
export { default as GoogleAuthForm } from './components/auth/GoogleAuthForm.svelte';
|
|
10
|
+
export { default as LoginForm } from './components/auth/LoginForm.svelte';
|
|
11
|
+
export { default as SignupForm } from './components/auth/SignupForm.svelte';
|
|
12
|
+
export { default as ContactForm } from './components/contact/ContactForm.svelte';
|
|
9
13
|
export { default as CommentSection } from './components/weeklies/CommentSection.svelte';
|
|
10
14
|
export { default as ReactionBar } from './components/weeklies/ReactionBar.svelte';
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { CopyEditFor } from '../../content/pages.js';
|
|
3
|
+
import type { JobOpeningData } from '../../content/types.js';
|
|
4
|
+
import type { CollectionRef } from '../../edit/types.js';
|
|
5
|
+
import type { JobEditMap } from '../jobs/JobList.svelte';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* What a CMS may open on /get-involved. The five «why get in touch» rows
|
|
9
|
+
* are copy blocks and edit through `copy` like the headings; `jobs` names
|
|
10
|
+
* the openings' collection, which turns on the list's add slot and removes.
|
|
11
|
+
*/
|
|
12
|
+
export interface GetInvolvedPageEdit {
|
|
13
|
+
copy?: CopyEditFor<'get-involved'>;
|
|
14
|
+
jobFor?: (job: JobOpeningData) => JobEditMap | undefined;
|
|
15
|
+
jobs?: CollectionRef;
|
|
16
|
+
}
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<script lang="ts">
|
|
20
|
+
import { getUiConfig } from '../../config/context.js';
|
|
21
|
+
import { GET_INVOLVED_REASON_KEYS, type PageCopy } from '../../content/pages.js';
|
|
22
|
+
import Editable from '../../edit/Editable.svelte';
|
|
23
|
+
import ContactForm, { type ContactFormInstance } from '../contact/ContactForm.svelte';
|
|
24
|
+
import JobList from '../jobs/JobList.svelte';
|
|
25
|
+
import PageShell from '../layout/PageShell.svelte';
|
|
26
|
+
import CopyIntro from '../ui/CopyIntro.svelte';
|
|
27
|
+
import DecorShapes from '../ui/DecorShapes.svelte';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The contact page: the reasons to write beside the contact form, the
|
|
31
|
+
* funding band, the open roles. The form arrives preflighted by the host
|
|
32
|
+
* against its own schema (the website's `contactForm.preflight(...)`; a
|
|
33
|
+
* CMS mirror passes the package's inert mock) — the one prop the hosts
|
|
34
|
+
* fill differently.
|
|
35
|
+
*
|
|
36
|
+
* The reasons list derives from `GET_INVOLVED_REASON_KEYS`, in that
|
|
37
|
+
* order, and does NOT skip a block with no row yet — the same answer
|
|
38
|
+
* CopyIntro gives, and the one `PageCopy` states.
|
|
39
|
+
*/
|
|
40
|
+
interface Props {
|
|
41
|
+
content: PageCopy<'get-involved'>;
|
|
42
|
+
jobs: JobOpeningData[];
|
|
43
|
+
form: ContactFormInstance;
|
|
44
|
+
edit?: GetInvolvedPageEdit;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let { content, jobs, form, edit }: Props = $props();
|
|
48
|
+
|
|
49
|
+
const config = getUiConfig();
|
|
50
|
+
const msg = $derived(config.messages);
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<PageShell title={msg.nav_getInvolved()}>
|
|
54
|
+
<Editable edit={config.messageEdit?.('nav_getInvolved')} value={msg.nav_getInvolved()}>
|
|
55
|
+
{#snippet children(text, attrs)}<h1 {...attrs}>{text}</h1>{/snippet}
|
|
56
|
+
</Editable>
|
|
57
|
+
|
|
58
|
+
<section class="contact" aria-labelledby="contact-heading">
|
|
59
|
+
<div class="contact-text">
|
|
60
|
+
<Editable edit={edit?.copy?.('contact_heading')} value={content.contact_heading}>
|
|
61
|
+
{#snippet children(text, attrs)}
|
|
62
|
+
<h2 class="section-heading" id="contact-heading" {...attrs}>{text}</h2>
|
|
63
|
+
{/snippet}
|
|
64
|
+
</Editable>
|
|
65
|
+
<CopyIntro text={content.contact_intro} edit={edit?.copy?.('contact_intro')} />
|
|
66
|
+
<ul class="reasons">
|
|
67
|
+
{#each GET_INVOLVED_REASON_KEYS as key (key)}
|
|
68
|
+
<!-- The li itself is the editable node: no extra span, so the
|
|
69
|
+
read-only markup stays the website's. -->
|
|
70
|
+
<Editable edit={edit?.copy?.(key)} value={content[key]}>
|
|
71
|
+
{#snippet children(text, attrs)}<li {...attrs}>{text}</li>{/snippet}
|
|
72
|
+
</Editable>
|
|
73
|
+
{/each}
|
|
74
|
+
</ul>
|
|
75
|
+
</div>
|
|
76
|
+
<ContactForm {form} />
|
|
77
|
+
</section>
|
|
78
|
+
|
|
79
|
+
<section class="funding" aria-labelledby="funding-heading">
|
|
80
|
+
<div>
|
|
81
|
+
<Editable edit={edit?.copy?.('funding_heading')} value={content.funding_heading}>
|
|
82
|
+
{#snippet children(text, attrs)}
|
|
83
|
+
<h2 class="section-heading" id="funding-heading" {...attrs}>{text}</h2>
|
|
84
|
+
{/snippet}
|
|
85
|
+
</Editable>
|
|
86
|
+
<CopyIntro text={content.funding_body} edit={edit?.copy?.('funding_body')} />
|
|
87
|
+
</div>
|
|
88
|
+
<DecorShapes flip />
|
|
89
|
+
</section>
|
|
90
|
+
|
|
91
|
+
<section aria-labelledby="careers-heading">
|
|
92
|
+
<Editable edit={edit?.copy?.('careers_heading')} value={content.careers_heading}>
|
|
93
|
+
{#snippet children(text, attrs)}
|
|
94
|
+
<h2 class="section-heading" id="careers-heading" {...attrs}>{text}</h2>
|
|
95
|
+
{/snippet}
|
|
96
|
+
</Editable>
|
|
97
|
+
<CopyIntro text={content.careers_intro} edit={edit?.copy?.('careers_intro')} />
|
|
98
|
+
<JobList {jobs} editFor={edit?.jobFor} collection={edit?.jobs} />
|
|
99
|
+
</section>
|
|
100
|
+
</PageShell>
|
|
101
|
+
|
|
102
|
+
<style>
|
|
103
|
+
.contact {
|
|
104
|
+
display: grid;
|
|
105
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
106
|
+
gap: var(--space-5);
|
|
107
|
+
align-items: start;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.reasons {
|
|
111
|
+
padding-left: var(--space-4);
|
|
112
|
+
display: flex;
|
|
113
|
+
flex-direction: column;
|
|
114
|
+
gap: var(--space-2);
|
|
115
|
+
color: var(--color-ink-secondary);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.funding {
|
|
119
|
+
display: flex;
|
|
120
|
+
justify-content: space-between;
|
|
121
|
+
align-items: center;
|
|
122
|
+
gap: var(--space-5);
|
|
123
|
+
margin-block: var(--space-6);
|
|
124
|
+
padding: var(--space-4);
|
|
125
|
+
background: var(--color-cream);
|
|
126
|
+
border-radius: var(--radius-lg);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@media (max-width: 720px) {
|
|
130
|
+
.contact {
|
|
131
|
+
grid-template-columns: 1fr;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.funding {
|
|
135
|
+
flex-direction: column;
|
|
136
|
+
align-items: flex-start;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
</style>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { CopyEditFor } from '../../content/pages.js';
|
|
2
|
+
import type { JobOpeningData } from '../../content/types.js';
|
|
3
|
+
import type { CollectionRef } from '../../edit/types.js';
|
|
4
|
+
import type { JobEditMap } from '../jobs/JobList.svelte';
|
|
5
|
+
/**
|
|
6
|
+
* What a CMS may open on /get-involved. The five «why get in touch» rows
|
|
7
|
+
* are copy blocks and edit through `copy` like the headings; `jobs` names
|
|
8
|
+
* the openings' collection, which turns on the list's add slot and removes.
|
|
9
|
+
*/
|
|
10
|
+
export interface GetInvolvedPageEdit {
|
|
11
|
+
copy?: CopyEditFor<'get-involved'>;
|
|
12
|
+
jobFor?: (job: JobOpeningData) => JobEditMap | undefined;
|
|
13
|
+
jobs?: CollectionRef;
|
|
14
|
+
}
|
|
15
|
+
import { type PageCopy } from '../../content/pages.js';
|
|
16
|
+
import { type ContactFormInstance } from '../contact/ContactForm.svelte';
|
|
17
|
+
/**
|
|
18
|
+
* The contact page: the reasons to write beside the contact form, the
|
|
19
|
+
* funding band, the open roles. The form arrives preflighted by the host
|
|
20
|
+
* against its own schema (the website's `contactForm.preflight(...)`; a
|
|
21
|
+
* CMS mirror passes the package's inert mock) — the one prop the hosts
|
|
22
|
+
* fill differently.
|
|
23
|
+
*
|
|
24
|
+
* The reasons list derives from `GET_INVOLVED_REASON_KEYS`, in that
|
|
25
|
+
* order, and does NOT skip a block with no row yet — the same answer
|
|
26
|
+
* CopyIntro gives, and the one `PageCopy` states.
|
|
27
|
+
*/
|
|
28
|
+
interface Props {
|
|
29
|
+
content: PageCopy<'get-involved'>;
|
|
30
|
+
jobs: JobOpeningData[];
|
|
31
|
+
form: ContactFormInstance;
|
|
32
|
+
edit?: GetInvolvedPageEdit;
|
|
33
|
+
}
|
|
34
|
+
declare const GetInvolvedPage: import("svelte").Component<Props, {}, "">;
|
|
35
|
+
type GetInvolvedPage = ReturnType<typeof GetInvolvedPage>;
|
|
36
|
+
export default GetInvolvedPage;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { CopyEditFor } from '../../content/pages.js';
|
|
3
|
+
import type { MilestoneData, WeeklyCardData } from '../../content/types.js';
|
|
4
|
+
import type { MilestoneEditMap } from '../timeline/TimelineMilestone.svelte';
|
|
5
|
+
import type { WeeklyEditMap } from '../weeklies/WeeklieCard.svelte';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* What a CMS may open on the home page. Every member optional: a read-only
|
|
9
|
+
* host passes no `edit` at all, and the render is byte-identical to the
|
|
10
|
+
* website's route. The chrome wording (the «Mostra-ho tot» link, the CTAs,
|
|
11
|
+
* the search placeholder, «…o explora'n un») needs no member here — the
|
|
12
|
+
* module reads `config.messageEdit`, like every component does for its
|
|
13
|
+
* own message sites.
|
|
14
|
+
*/
|
|
15
|
+
export interface HomePageEdit {
|
|
16
|
+
copy?: CopyEditFor<'home'>;
|
|
17
|
+
milestoneFor?: (milestone: MilestoneData) => MilestoneEditMap | undefined;
|
|
18
|
+
weeklyFor?: (weekly: WeeklyCardData) => WeeklyEditMap | undefined;
|
|
19
|
+
}
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<script lang="ts">
|
|
23
|
+
import { getUiConfig } from '../../config/context.js';
|
|
24
|
+
import type { PageCopy } from '../../content/pages.js';
|
|
25
|
+
import LinkEdit from '../../edit/chrome/LinkEdit.svelte';
|
|
26
|
+
import Editable from '../../edit/Editable.svelte';
|
|
27
|
+
import { chromeProperty } from '../../edit/helpers.js';
|
|
28
|
+
import PageShell from '../layout/PageShell.svelte';
|
|
29
|
+
import Timeline from '../timeline/Timeline.svelte';
|
|
30
|
+
import CopyIntro from '../ui/CopyIntro.svelte';
|
|
31
|
+
import DecorShapes from '../ui/DecorShapes.svelte';
|
|
32
|
+
import Link from '../ui/Link.svelte';
|
|
33
|
+
import SearchInput from '../ui/SearchInput.svelte';
|
|
34
|
+
import WeeklieCard from '../weeklies/WeeklieCard.svelte';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The landing page: hero, the latest milestones, a weeklies band with a
|
|
38
|
+
* search box, and two CTAs. Takes the website's read projection — its
|
|
39
|
+
* `+page.server.ts` returns exactly these three — plus the one thing the
|
|
40
|
+
* hosts do differently: where a search goes. The package has no router,
|
|
41
|
+
* so the host navigates to its own /weeklies with the query.
|
|
42
|
+
*/
|
|
43
|
+
interface Props {
|
|
44
|
+
content: PageCopy<'home'>;
|
|
45
|
+
milestones: MilestoneData[];
|
|
46
|
+
weeklies: WeeklyCardData[];
|
|
47
|
+
/** Called with the trimmed query after the search box's debounce. */
|
|
48
|
+
onsearch: (query: string) => void;
|
|
49
|
+
edit?: HomePageEdit;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let { content, milestones, weeklies, onsearch, edit }: Props = $props();
|
|
53
|
+
|
|
54
|
+
const config = getUiConfig();
|
|
55
|
+
const msg = $derived(config.messages);
|
|
56
|
+
|
|
57
|
+
// A link is configurable whole: clicking it while editing opens ONE modal
|
|
58
|
+
// with Text and Adreça — the label saves as wording, the destination as
|
|
59
|
+
// its *Href key. Both halves undefined without `messageEdit`, so the
|
|
60
|
+
// control stays inert (the CommentSection rule).
|
|
61
|
+
const hrefProperty = (key: string) =>
|
|
62
|
+
config.messageEdit
|
|
63
|
+
? chromeProperty(key, {
|
|
64
|
+
type: 'text',
|
|
65
|
+
locale: config.locale(),
|
|
66
|
+
label: config.editMessages.edit_linkUrl()
|
|
67
|
+
})
|
|
68
|
+
: undefined;
|
|
69
|
+
// A placeholder cannot hold a caret, so it edits through the search box's
|
|
70
|
+
// panel — only where the host exposes `messageEdit` (the ContactForm rule).
|
|
71
|
+
const placeholderProperty = $derived(
|
|
72
|
+
config.messageEdit
|
|
73
|
+
? chromeProperty('weeklies_searchPlaceholder', {
|
|
74
|
+
type: 'text',
|
|
75
|
+
locale: config.locale(),
|
|
76
|
+
label: msg.weeklies_searchPlaceholder()
|
|
77
|
+
})
|
|
78
|
+
: undefined
|
|
79
|
+
);
|
|
80
|
+
</script>
|
|
81
|
+
|
|
82
|
+
<PageShell title={content.hero_title} description={content.hero_subtitle}>
|
|
83
|
+
<section class="hero">
|
|
84
|
+
<div class="shapes left" aria-hidden="true"><DecorShapes /></div>
|
|
85
|
+
<div class="hero-text">
|
|
86
|
+
<Editable edit={edit?.copy?.('hero_title')} value={content.hero_title}>
|
|
87
|
+
{#snippet children(text, attrs)}<h1 {...attrs}>{text}</h1>{/snippet}
|
|
88
|
+
</Editable>
|
|
89
|
+
<Editable edit={edit?.copy?.('hero_subtitle')} value={content.hero_subtitle}>
|
|
90
|
+
{#snippet children(text, attrs)}<p {...attrs}>{text}</p>{/snippet}
|
|
91
|
+
</Editable>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="shapes right" aria-hidden="true"><DecorShapes flip /></div>
|
|
94
|
+
</section>
|
|
95
|
+
|
|
96
|
+
<section aria-labelledby="milestones-heading">
|
|
97
|
+
<Editable edit={edit?.copy?.('milestones_heading')} value={content.milestones_heading}>
|
|
98
|
+
{#snippet children(text, attrs)}
|
|
99
|
+
<h2 class="section-heading" id="milestones-heading" {...attrs}>{text}</h2>
|
|
100
|
+
{/snippet}
|
|
101
|
+
</Editable>
|
|
102
|
+
<Timeline {milestones} variant="compact" editFor={edit?.milestoneFor} />
|
|
103
|
+
<p class="see-all">
|
|
104
|
+
<LinkEdit
|
|
105
|
+
text={{ edit: config.messageEdit?.('common_seeAll'), value: msg.common_seeAll() }}
|
|
106
|
+
href={{ descriptor: hrefProperty('common_seeAllHref'), value: msg.common_seeAllHref() }}
|
|
107
|
+
>
|
|
108
|
+
{#snippet control()}
|
|
109
|
+
<Link href={msg.common_seeAllHref()}>{msg.common_seeAll()} →</Link>
|
|
110
|
+
{/snippet}
|
|
111
|
+
</LinkEdit>
|
|
112
|
+
</p>
|
|
113
|
+
</section>
|
|
114
|
+
|
|
115
|
+
<section class="weeklies-band" aria-labelledby="weeklies-heading">
|
|
116
|
+
<Editable edit={edit?.copy?.('weeklies_heading')} value={content.weeklies_heading}>
|
|
117
|
+
{#snippet children(text, attrs)}
|
|
118
|
+
<h2 class="section-heading" id="weeklies-heading" {...attrs}>{text}</h2>
|
|
119
|
+
{/snippet}
|
|
120
|
+
</Editable>
|
|
121
|
+
<CopyIntro text={content.weeklies_intro} edit={edit?.copy?.('weeklies_intro')} />
|
|
122
|
+
<SearchInput
|
|
123
|
+
placeholder={msg.weeklies_searchPlaceholder()}
|
|
124
|
+
label={msg.weeklies_searchPlaceholder()}
|
|
125
|
+
onsearch={(query) => onsearch(query)}
|
|
126
|
+
debounceMs={800}
|
|
127
|
+
placeholderEdit={placeholderProperty}
|
|
128
|
+
/>
|
|
129
|
+
<Editable edit={config.messageEdit?.('weeklies_exploreOne')} value={msg.weeklies_exploreOne()}>
|
|
130
|
+
{#snippet children(text, attrs)}<p class="explore" {...attrs}>{text}</p>{/snippet}
|
|
131
|
+
</Editable>
|
|
132
|
+
<div class="grid">
|
|
133
|
+
{#each weeklies as weekly (weekly.slug)}
|
|
134
|
+
<WeeklieCard {weekly} edit={edit?.weeklyFor?.(weekly)} />
|
|
135
|
+
{/each}
|
|
136
|
+
</div>
|
|
137
|
+
</section>
|
|
138
|
+
|
|
139
|
+
<section class="know-more" aria-labelledby="know-more-heading">
|
|
140
|
+
<Editable edit={edit?.copy?.('know_more_heading')} value={content.know_more_heading}>
|
|
141
|
+
{#snippet children(text, attrs)}<h2 id="know-more-heading" {...attrs}>{text}</h2>{/snippet}
|
|
142
|
+
</Editable>
|
|
143
|
+
<div class="ctas">
|
|
144
|
+
<LinkEdit
|
|
145
|
+
text={{ edit: config.messageEdit?.('cta_meetTeam'), value: msg.cta_meetTeam() }}
|
|
146
|
+
href={{ descriptor: hrefProperty('cta_meetTeamHref'), value: msg.cta_meetTeamHref() }}
|
|
147
|
+
>
|
|
148
|
+
{#snippet control()}
|
|
149
|
+
<Link href={msg.cta_meetTeamHref()} class="cta">{msg.cta_meetTeam()}</Link>
|
|
150
|
+
{/snippet}
|
|
151
|
+
</LinkEdit>
|
|
152
|
+
<LinkEdit
|
|
153
|
+
text={{ edit: config.messageEdit?.('cta_contactUs'), value: msg.cta_contactUs() }}
|
|
154
|
+
href={{ descriptor: hrefProperty('cta_contactUsHref'), value: msg.cta_contactUsHref() }}
|
|
155
|
+
>
|
|
156
|
+
{#snippet control()}
|
|
157
|
+
<Link href={msg.cta_contactUsHref()} class="cta primary">{msg.cta_contactUs()}</Link>
|
|
158
|
+
{/snippet}
|
|
159
|
+
</LinkEdit>
|
|
160
|
+
</div>
|
|
161
|
+
</section>
|
|
162
|
+
</PageShell>
|
|
163
|
+
|
|
164
|
+
<style>
|
|
165
|
+
.hero {
|
|
166
|
+
display: grid;
|
|
167
|
+
grid-template-columns: auto 1fr auto;
|
|
168
|
+
align-items: center;
|
|
169
|
+
gap: var(--space-4);
|
|
170
|
+
padding-block: var(--space-6);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.hero-text {
|
|
174
|
+
text-align: center;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* The one heading in the library that sets its own size: --text-hero is a
|
|
178
|
+
* deliberate decision about the landing page. Every other page module
|
|
179
|
+
* takes the h1/h2 scale from the host's global rules. */
|
|
180
|
+
h1 {
|
|
181
|
+
font-size: var(--text-hero);
|
|
182
|
+
margin-bottom: var(--space-3);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.hero-text p {
|
|
186
|
+
font-size: var(--text-lg);
|
|
187
|
+
color: var(--color-ink-secondary);
|
|
188
|
+
max-width: 44ch;
|
|
189
|
+
margin-inline: auto;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.see-all :global(a) {
|
|
193
|
+
color: var(--color-brand);
|
|
194
|
+
font-weight: 600;
|
|
195
|
+
text-decoration: none;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.weeklies-band {
|
|
199
|
+
background: var(--color-band-grey);
|
|
200
|
+
border-radius: var(--radius-lg);
|
|
201
|
+
padding: var(--space-4);
|
|
202
|
+
margin-block: var(--space-5);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.explore {
|
|
206
|
+
color: var(--color-ink-secondary);
|
|
207
|
+
max-width: 60ch;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.explore {
|
|
211
|
+
margin-top: var(--space-3);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.grid {
|
|
215
|
+
display: grid;
|
|
216
|
+
grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
|
|
217
|
+
gap: var(--space-4);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.know-more {
|
|
221
|
+
text-align: center;
|
|
222
|
+
padding-block: var(--space-6);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.ctas {
|
|
226
|
+
display: flex;
|
|
227
|
+
justify-content: center;
|
|
228
|
+
gap: var(--space-3);
|
|
229
|
+
flex-wrap: wrap;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.ctas :global(a.cta) {
|
|
233
|
+
padding: var(--space-2) var(--space-5);
|
|
234
|
+
border: 2px solid var(--color-ink);
|
|
235
|
+
border-radius: var(--radius);
|
|
236
|
+
text-decoration: none;
|
|
237
|
+
font-weight: 600;
|
|
238
|
+
color: var(--color-ink);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.ctas :global(a.cta.primary) {
|
|
242
|
+
background: var(--color-brand);
|
|
243
|
+
border-color: var(--color-brand);
|
|
244
|
+
color: var(--color-surface);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
@media (max-width: 900px) {
|
|
248
|
+
.shapes {
|
|
249
|
+
display: none;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.hero {
|
|
253
|
+
grid-template-columns: 1fr;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
</style>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { CopyEditFor } from '../../content/pages.js';
|
|
2
|
+
import type { MilestoneData, WeeklyCardData } from '../../content/types.js';
|
|
3
|
+
import type { MilestoneEditMap } from '../timeline/TimelineMilestone.svelte';
|
|
4
|
+
import type { WeeklyEditMap } from '../weeklies/WeeklieCard.svelte';
|
|
5
|
+
/**
|
|
6
|
+
* What a CMS may open on the home page. Every member optional: a read-only
|
|
7
|
+
* host passes no `edit` at all, and the render is byte-identical to the
|
|
8
|
+
* website's route. The chrome wording (the «Mostra-ho tot» link, the CTAs,
|
|
9
|
+
* the search placeholder, «…o explora'n un») needs no member here — the
|
|
10
|
+
* module reads `config.messageEdit`, like every component does for its
|
|
11
|
+
* own message sites.
|
|
12
|
+
*/
|
|
13
|
+
export interface HomePageEdit {
|
|
14
|
+
copy?: CopyEditFor<'home'>;
|
|
15
|
+
milestoneFor?: (milestone: MilestoneData) => MilestoneEditMap | undefined;
|
|
16
|
+
weeklyFor?: (weekly: WeeklyCardData) => WeeklyEditMap | undefined;
|
|
17
|
+
}
|
|
18
|
+
import type { PageCopy } from '../../content/pages.js';
|
|
19
|
+
/**
|
|
20
|
+
* The landing page: hero, the latest milestones, a weeklies band with a
|
|
21
|
+
* search box, and two CTAs. Takes the website's read projection — its
|
|
22
|
+
* `+page.server.ts` returns exactly these three — plus the one thing the
|
|
23
|
+
* hosts do differently: where a search goes. The package has no router,
|
|
24
|
+
* so the host navigates to its own /weeklies with the query.
|
|
25
|
+
*/
|
|
26
|
+
interface Props {
|
|
27
|
+
content: PageCopy<'home'>;
|
|
28
|
+
milestones: MilestoneData[];
|
|
29
|
+
weeklies: WeeklyCardData[];
|
|
30
|
+
/** Called with the trimmed query after the search box's debounce. */
|
|
31
|
+
onsearch: (query: string) => void;
|
|
32
|
+
edit?: HomePageEdit;
|
|
33
|
+
}
|
|
34
|
+
declare const HomePage: import("svelte").Component<Props, {}, "">;
|
|
35
|
+
type HomePage = ReturnType<typeof HomePage>;
|
|
36
|
+
export default HomePage;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { CopyEditFor } from '../../content/pages.js';
|
|
3
|
+
|
|
4
|
+
/** What a CMS may open on /legal: its two copy blocks (the body edits as rich text). */
|
|
5
|
+
export interface LegalPageEdit {
|
|
6
|
+
copy?: CopyEditFor<'legal'>;
|
|
7
|
+
}
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<script lang="ts">
|
|
11
|
+
import type { PageCopy } from '../../content/pages.js';
|
|
12
|
+
import Editable from '../../edit/Editable.svelte';
|
|
13
|
+
import PageShell from '../layout/PageShell.svelte';
|
|
14
|
+
import RichText from '../ui/RichText.svelte';
|
|
15
|
+
|
|
16
|
+
/** The terms page: a heading and one rich-text body, on the chrome shell. */
|
|
17
|
+
interface Props {
|
|
18
|
+
content: PageCopy<'legal'>;
|
|
19
|
+
edit?: LegalPageEdit;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let { content, edit }: Props = $props();
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<PageShell title={content.heading} variant="chrome">
|
|
26
|
+
<Editable edit={edit?.copy?.('heading')} value={content.heading}>
|
|
27
|
+
{#snippet children(text, attrs)}<h1 {...attrs}>{text}</h1>{/snippet}
|
|
28
|
+
</Editable>
|
|
29
|
+
<div class="body">
|
|
30
|
+
<RichText body={content.body} edit={edit?.copy?.('body')} />
|
|
31
|
+
</div>
|
|
32
|
+
</PageShell>
|
|
33
|
+
|
|
34
|
+
<style>
|
|
35
|
+
.body {
|
|
36
|
+
color: var(--color-ink-secondary);
|
|
37
|
+
max-width: 40rem;
|
|
38
|
+
}
|
|
39
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CopyEditFor } from '../../content/pages.js';
|
|
2
|
+
/** What a CMS may open on /legal: its two copy blocks (the body edits as rich text). */
|
|
3
|
+
export interface LegalPageEdit {
|
|
4
|
+
copy?: CopyEditFor<'legal'>;
|
|
5
|
+
}
|
|
6
|
+
import type { PageCopy } from '../../content/pages.js';
|
|
7
|
+
/** The terms page: a heading and one rich-text body, on the chrome shell. */
|
|
8
|
+
interface Props {
|
|
9
|
+
content: PageCopy<'legal'>;
|
|
10
|
+
edit?: LegalPageEdit;
|
|
11
|
+
}
|
|
12
|
+
declare const LegalPage: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type LegalPage = ReturnType<typeof LegalPage>;
|
|
14
|
+
export default LegalPage;
|