@vit-foundation/ui 0.11.0 → 0.13.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.
Files changed (35) hide show
  1. package/dist/chrome.d.ts +1 -0
  2. package/dist/components/account/NewsletterSignup.svelte +19 -3
  3. package/dist/components/contact/ContactForm.svelte +62 -14
  4. package/dist/components/jobs/JobList.svelte +75 -12
  5. package/dist/components/jobs/JobList.svelte.d.ts +19 -2
  6. package/dist/components/layout/Footer.svelte +54 -5
  7. package/dist/components/layout/Footer.svelte.d.ts +6 -1
  8. package/dist/components/layout/Nav.svelte +64 -8
  9. package/dist/components/layout/Nav.svelte.d.ts +6 -1
  10. package/dist/components/layout/site-link-edit.d.ts +17 -0
  11. package/dist/components/layout/site-link-edit.js +1 -0
  12. package/dist/components/projects/ProjectCard.svelte +80 -33
  13. package/dist/components/projects/ProjectCard.svelte.d.ts +11 -2
  14. package/dist/components/team/CollaboratorList.svelte +87 -9
  15. package/dist/components/team/CollaboratorList.svelte.d.ts +26 -0
  16. package/dist/components/team/TeamMemberCard.svelte +41 -19
  17. package/dist/components/team/TeamMemberCard.svelte.d.ts +8 -4
  18. package/dist/components/timeline/TimelineMilestone.svelte +3 -1
  19. package/dist/components/ui/CardMedia.svelte +66 -3
  20. package/dist/components/ui/CardMedia.svelte.d.ts +6 -2
  21. package/dist/components/ui/SearchInput.svelte +31 -10
  22. package/dist/components/ui/SearchInput.svelte.d.ts +7 -0
  23. package/dist/components/weeklies/CommentSection.svelte +20 -3
  24. package/dist/components/weeklies/SortSelect.svelte +41 -11
  25. package/dist/components/weeklies/SortSelect.svelte.d.ts +8 -0
  26. package/dist/components/weeklies/WeeklieCard.svelte +46 -19
  27. package/dist/components/weeklies/WeeklieCard.svelte.d.ts +9 -2
  28. package/dist/config/edit-messages.d.ts +2 -0
  29. package/dist/config/edit-messages.js +1 -0
  30. package/dist/config/types.d.ts +4 -0
  31. package/dist/content/types.d.ts +12 -0
  32. package/dist/edit/chrome/DraftBadge.svelte +30 -0
  33. package/dist/edit/chrome/DraftBadge.svelte.d.ts +18 -0
  34. package/dist/edit/types.d.ts +1 -1
  35. package/package.json +1 -1
@@ -97,7 +97,14 @@
97
97
  {@const reply = replyFormFor(String(thread.id))}
98
98
  <!-- Native disclosure: the reply form opens without JS. -->
99
99
  <details class="reply">
100
- <summary>{msg.comments_replyLabel()}</summary>
100
+ <summary>
101
+ <ActionLabel
102
+ edit={config.messageEdit?.('comments_replyLabel')}
103
+ value={msg.comments_replyLabel()}
104
+ >
105
+ {#snippet control()}{msg.comments_replyLabel()}{/snippet}
106
+ </ActionLabel>
107
+ </summary>
101
108
  <form class="form-stack" {...reply}>
102
109
  <Field
103
110
  id="reply-body-{thread.id}"
@@ -173,9 +180,19 @@
173
180
  >
174
181
  {#snippet children(text, attrs)}<span {...attrs}>{text}</span>{/snippet}
175
182
  </Editable>
176
- <Link href="/login">{msg.comments_loginLink()}</Link>
183
+ <ActionLabel
184
+ edit={config.messageEdit?.('comments_loginLink')}
185
+ value={msg.comments_loginLink()}
186
+ >
187
+ {#snippet control()}<Link href="/login">{msg.comments_loginLink()}</Link>{/snippet}
188
+ </ActionLabel>
177
189
  ·
178
- <Link href="/signup">{msg.comments_signupLink()}</Link>
190
+ <ActionLabel
191
+ edit={config.messageEdit?.('comments_signupLink')}
192
+ value={msg.comments_signupLink()}
193
+ >
194
+ {#snippet control()}<Link href="/signup">{msg.comments_signupLink()}</Link>{/snippet}
195
+ </ActionLabel>
179
196
  </p>
180
197
  {/if}
181
198
  </section>
@@ -2,34 +2,64 @@
2
2
  import { getUiConfig } from '../../config/context.js';
3
3
  import type { SortDirection } from '../../content/types.js';
4
4
  import ActionLabel from '../../edit/ActionLabel.svelte';
5
+ import EditFrame from '../../edit/chrome/EditFrame.svelte';
6
+ import EditPanel from '../../edit/chrome/EditPanel.svelte';
7
+ import type { PropertyDescriptor } from '../../edit/types.js';
5
8
 
6
9
  interface Props {
7
10
  value: SortDirection;
8
11
  onchange: (value: SortDirection) => void;
12
+ /**
13
+ * An <option> cannot hold a caret, so the option labels edit through
14
+ * the frame's panel — one text row per direction, descriptors from the
15
+ * host (chrome keys on the foundation site). The visible label keeps
16
+ * its ActionLabel swap. Inert without an adapter.
17
+ */
18
+ optionsEdit?: (direction: SortDirection) => PropertyDescriptor | undefined;
9
19
  }
10
20
 
11
- let { value, onchange }: Props = $props();
21
+ let { value, onchange, optionsEdit }: Props = $props();
12
22
 
13
23
  const config = getUiConfig();
14
24
  const msg = $derived(config.messages);
15
25
 
26
+ const panelRows = $derived(
27
+ (
28
+ [
29
+ ['desc', msg.weeklies_sortDesc()],
30
+ ['asc', msg.weeklies_sortAsc()]
31
+ ] as const
32
+ ).flatMap(([direction, label]) => {
33
+ const descriptor = optionsEdit?.(direction);
34
+ return descriptor ? [{ descriptor, value: label }] : [];
35
+ })
36
+ );
37
+ const frameSpec = $derived(
38
+ panelRows.length > 0 ? { label: msg.weeklies_sortLabel(), hasPanel: true } : undefined
39
+ );
40
+
16
41
  function handleChange(event: Event): void {
17
42
  const target = event.currentTarget as HTMLSelectElement;
18
43
  onchange(target.value === 'asc' ? 'asc' : 'desc');
19
44
  }
20
45
  </script>
21
46
 
22
- <ActionLabel edit={config.messageEdit?.('weeklies_sortLabel')} value={msg.weeklies_sortLabel()}>
23
- {#snippet control()}
24
- <label>
25
- <span>{msg.weeklies_sortLabel()}</span>
26
- <select class="control" {value} onchange={handleChange}>
27
- <option value="desc">{msg.weeklies_sortDesc()}</option>
28
- <option value="asc">{msg.weeklies_sortAsc()}</option>
29
- </select>
30
- </label>
47
+ <EditFrame spec={frameSpec}>
48
+ {#snippet panel()}
49
+ <EditPanel rows={panelRows} />
31
50
  {/snippet}
32
- </ActionLabel>
51
+ <ActionLabel edit={config.messageEdit?.('weeklies_sortLabel')} value={msg.weeklies_sortLabel()}>
52
+ {#snippet control()}
53
+ <label>
54
+ <span>{msg.weeklies_sortLabel()}</span>
55
+ <select class="control" {value} onchange={handleChange}>
56
+ <option value="desc">{msg.weeklies_sortDesc()}</option>
57
+ <option value="asc">{msg.weeklies_sortAsc()}</option>
58
+ </select>
59
+ </label>
60
+ {/snippet}
61
+ </ActionLabel>
62
+ </EditFrame>
33
63
 
34
64
  <style>
35
65
  label {
@@ -1,7 +1,15 @@
1
1
  import type { SortDirection } from '../../content/types.js';
2
+ import type { PropertyDescriptor } from '../../edit/types.js';
2
3
  interface Props {
3
4
  value: SortDirection;
4
5
  onchange: (value: SortDirection) => void;
6
+ /**
7
+ * An <option> cannot hold a caret, so the option labels edit through
8
+ * the frame's panel — one text row per direction, descriptors from the
9
+ * host (chrome keys on the foundation site). The visible label keeps
10
+ * its ActionLabel swap. Inert without an adapter.
11
+ */
12
+ optionsEdit?: (direction: SortDirection) => PropertyDescriptor | undefined;
5
13
  }
6
14
  declare const SortSelect: import("svelte").Component<Props, {}, "">;
7
15
  type SortSelect = ReturnType<typeof SortSelect>;
@@ -1,10 +1,17 @@
1
1
  <script module lang="ts">
2
- import type { EditDescriptor } from '../../edit/types.js';
2
+ import type { EditDescriptor, PropertyDescriptor } from '../../edit/types.js';
3
3
 
4
- /** Which of the card's localized fields are editable at this render site. */
4
+ /**
5
+ * Which of the card's fields are editable at this render site: localized
6
+ * text inline, the cover image through the frame's panel. NO collection
7
+ * ops — weeklies keep their own authoring flow in the CMS.
8
+ */
5
9
  export interface WeeklyEditMap {
6
10
  title?: EditDescriptor;
7
11
  excerpt?: EditDescriptor;
12
+ image?: PropertyDescriptor;
13
+ /** Accessible name for the frame, e.g. "Weekly #12". */
14
+ label?: string;
8
15
  }
9
16
  </script>
10
17
 
@@ -13,7 +20,10 @@
13
20
  import type { WeeklyCardData } from '../../content/types.js';
14
21
  import { getEditAdapter } from '../../edit/context.js';
15
22
  import Editable from '../../edit/Editable.svelte';
23
+ import EditFrame from '../../edit/chrome/EditFrame.svelte';
24
+ import EditPanel from '../../edit/chrome/EditPanel.svelte';
16
25
  import CardMedia from '../ui/CardMedia.svelte';
26
+ import DraftBadge from '../../edit/chrome/DraftBadge.svelte';
17
27
  import CardTitle from '../ui/CardTitle.svelte';
18
28
  import DateText from '../ui/DateText.svelte';
19
29
  import Link from '../ui/Link.svelte';
@@ -33,26 +43,43 @@
33
43
  // a contenteditable inside an anchor still navigates on click, which
34
44
  // would throw the editor off the page mid-draft.
35
45
  const titleEditing = $derived(edit?.title !== undefined && (adapter?.isEditing ?? false));
46
+
47
+ const panelRows = $derived(
48
+ [edit?.image && { descriptor: edit.image, value: weekly.imageUrl }].filter(
49
+ (row) => row !== undefined
50
+ )
51
+ );
52
+ const frameSpec = $derived(
53
+ edit && panelRows.length > 0 ? { label: edit.label ?? weekly.title, hasPanel: true } : undefined
54
+ );
36
55
  </script>
37
56
 
38
57
  <article>
39
- <header>
40
- <span class="number">{config.messages.weeklie_number({ number: weekly.number })}</span>
41
- <DateText value={weekly.publishedOn} />
42
- </header>
43
- <CardMedia src={weekly.imageUrl} alt="" ratio="1 / 1" width="600" height="600" />
44
- <CardTitle>
45
- {#if titleEditing}
46
- <Editable edit={edit?.title} value={weekly.title}>
47
- {#snippet children(text, attrs)}<span {...attrs}>{text}</span>{/snippet}
48
- </Editable>
49
- {:else}
50
- <Link href={`/weeklies/${weekly.slug}`}>{weekly.title}</Link>
51
- {/if}
52
- </CardTitle>
53
- <Editable edit={edit?.excerpt} value={weekly.excerpt}>
54
- {#snippet children(text, attrs)}<p {...attrs}>{text}</p>{/snippet}
55
- </Editable>
58
+ <!-- Inside the article, so the grid the host lays cards into never gains
59
+ an unexpected child; inactive it renders the content alone. -->
60
+ <EditFrame spec={frameSpec}>
61
+ {#snippet panel()}
62
+ <EditPanel rows={panelRows} />
63
+ {/snippet}
64
+ <header>
65
+ <span class="number">{config.messages.weeklie_number({ number: weekly.number })}</span>
66
+ {#if weekly.draft}<DraftBadge />{/if}
67
+ <DateText value={weekly.publishedOn} />
68
+ </header>
69
+ <CardMedia src={weekly.imageUrl} alt="" ratio="1 / 1" width="600" height="600" />
70
+ <CardTitle>
71
+ {#if titleEditing}
72
+ <Editable edit={edit?.title} value={weekly.title}>
73
+ {#snippet children(text, attrs)}<span {...attrs}>{text}</span>{/snippet}
74
+ </Editable>
75
+ {:else}
76
+ <Link href={`/weeklies/${weekly.slug}`}>{weekly.title}</Link>
77
+ {/if}
78
+ </CardTitle>
79
+ <Editable edit={edit?.excerpt} value={weekly.excerpt}>
80
+ {#snippet children(text, attrs)}<p {...attrs}>{text}</p>{/snippet}
81
+ </Editable>
82
+ </EditFrame>
56
83
  </article>
57
84
 
58
85
  <style>
@@ -1,8 +1,15 @@
1
- import type { EditDescriptor } from '../../edit/types.js';
2
- /** Which of the card's localized fields are editable at this render site. */
1
+ import type { EditDescriptor, PropertyDescriptor } from '../../edit/types.js';
2
+ /**
3
+ * Which of the card's fields are editable at this render site: localized
4
+ * text inline, the cover image through the frame's panel. NO collection
5
+ * ops — weeklies keep their own authoring flow in the CMS.
6
+ */
3
7
  export interface WeeklyEditMap {
4
8
  title?: EditDescriptor;
5
9
  excerpt?: EditDescriptor;
10
+ image?: PropertyDescriptor;
11
+ /** Accessible name for the frame, e.g. "Weekly #12". */
12
+ label?: string;
6
13
  }
7
14
  import type { WeeklyCardData } from '../../content/types.js';
8
15
  interface Props {
@@ -36,6 +36,8 @@ export interface EditMessages {
36
36
  edit_addFailed(): string;
37
37
  /** Property rows. */
38
38
  edit_uploadImage(): string;
39
+ /** The chip a card wears while its row is unpublished. */
40
+ edit_draftBadge(): string;
39
41
  edit_clearValue(): string;
40
42
  edit_emptyRequired(): string;
41
43
  }
@@ -19,6 +19,7 @@ export const defaultEditMessages = {
19
19
  edit_add: ({ label }) => `Afegeix ${label}`,
20
20
  edit_addFailed: () => 'No s’ha pogut afegir',
21
21
  edit_uploadImage: () => 'Puja una imatge',
22
+ edit_draftBadge: () => 'Esborrany',
22
23
  edit_clearValue: () => 'Buida el valor',
23
24
  edit_emptyRequired: () => 'Aquest camp no pot quedar buit'
24
25
  };
@@ -12,8 +12,12 @@ export type Locale = (typeof LOCALES)[number];
12
12
  export declare const BASE_LOCALE: Locale;
13
13
  /** One primary-navigation entry, rendered by Nav and repeated by Footer. */
14
14
  export interface SiteLink {
15
+ /** Present only where the nav is editable — structural ops need identity. */
16
+ id?: string | number;
15
17
  href: string;
16
18
  label: string;
19
+ /** The row's sort_order, for the CMS's order panel row. */
20
+ order?: number;
17
21
  }
18
22
  /**
19
23
  * The strings the components render, as message *functions* so the app's
@@ -18,6 +18,8 @@ export type SortDirection = 'asc' | 'desc';
18
18
  /** A weekly as its listing card shows it. */
19
19
  export interface WeeklyCardData {
20
20
  id: number;
21
+ /** CMS-only: true renders the «Esborrany» badge. The public site never sets it. */
22
+ draft?: boolean;
21
23
  number: number;
22
24
  slug: string;
23
25
  /** ISO date (2026-08-30). */
@@ -30,6 +32,8 @@ export type ProjectKind = 'collaboration' | 'passion';
30
32
  /** A project as its card shows it. */
31
33
  export interface ProjectCardData {
32
34
  id: number;
35
+ /** CMS-only: true renders the «Esborrany» badge. The public site never sets it. */
36
+ draft?: boolean;
33
37
  slug: string;
34
38
  kind: ProjectKind;
35
39
  publishedOn: string;
@@ -43,6 +47,8 @@ export type MilestoneCategory = 'education' | 'lab' | 'foundation' | 'collaborat
43
47
  /** One timeline milestone. */
44
48
  export interface MilestoneData {
45
49
  id: number;
50
+ /** CMS-only: true renders the «Esborrany» badge. The public site never sets it. */
51
+ draft?: boolean;
46
52
  occurredOn: string;
47
53
  category: MilestoneCategory;
48
54
  title: string;
@@ -59,11 +65,17 @@ export interface TeamMemberData {
59
65
  isBoard: boolean;
60
66
  }
61
67
  export interface CollaboratorData {
68
+ /** Present only where the list is editable — remove ops need an identity. */
69
+ id?: string | number;
62
70
  personName: string;
63
71
  affiliation: string;
64
72
  url: string | null;
65
73
  }
66
74
  export interface JobOpeningData {
75
+ /** Present only where the list is editable — remove ops need an identity. */
76
+ id?: string | number;
77
+ /** CMS-only: true renders the «Esborrany» badge. The public site never sets it. */
78
+ draft?: boolean;
67
79
  slug: string;
68
80
  title: string;
69
81
  description: string | null;
@@ -0,0 +1,30 @@
1
+ <script lang="ts">
2
+ import { getUiConfig } from '../../config/context.js';
3
+
4
+ /**
5
+ * The «Esborrany» chip a card wears while its row is unpublished. The flag
6
+ * arrives on the DATA (`draft?: boolean`), set only by a CMS host — the
7
+ * public site never marks anything draft, so its cards render without this
8
+ * component ever mounting. Wording comes from editMessages for the same
9
+ * reason: it is editor-facing, never catalog copy.
10
+ */
11
+ const config = getUiConfig();
12
+ </script>
13
+
14
+ <span class="draft">{config.editMessages.edit_draftBadge()}</span>
15
+
16
+ <style>
17
+ .draft {
18
+ display: inline-block;
19
+ align-self: start;
20
+ border: 1px solid var(--color-ink-muted);
21
+ border-radius: 999px;
22
+ padding: 0.1em 0.7em;
23
+ font-size: var(--text-sm);
24
+ font-weight: 600;
25
+ letter-spacing: 0.04em;
26
+ text-transform: uppercase;
27
+ color: var(--color-ink-muted);
28
+ background: var(--color-surface);
29
+ }
30
+ </style>
@@ -0,0 +1,18 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const DraftBadge: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type DraftBadge = InstanceType<typeof DraftBadge>;
18
+ export default DraftBadge;
@@ -15,7 +15,7 @@ export declare function localize(value: LocalizedText, locale: Locale): string;
15
15
  * The entities whose fields the components can mark editable. Names match
16
16
  * the content tables so an adapter can route a save without a mapping layer.
17
17
  */
18
- export type EditableEntity = 'weeklies' | 'projects' | 'milestones' | 'team_members' | 'job_openings' | 'themes';
18
+ export type EditableEntity = 'weeklies' | 'projects' | 'milestones' | 'team_members' | 'job_openings' | 'themes' | 'collaborators' | 'site_links';
19
19
  /**
20
20
  * The identity of one editable string in the content model: a page copy
21
21
  * block (`page_content` row), one localized field of a domain entity, or one
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vit-foundation/ui",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",