@vit-foundation/ui 0.25.0 → 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 +1 -1
- package/dist/community.d.ts +12 -3
- package/dist/community.js +7 -3
- package/dist/components/pages/ProjectPage.svelte +3 -9
- package/dist/components/pages/ProjectPage.svelte.d.ts +3 -5
- package/dist/components/pages/WeeklyPage.svelte +7 -12
- package/dist/components/pages/WeeklyPage.svelte.d.ts +7 -9
- 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/content-components.d.ts +6 -3
- package/dist/content-components.js +5 -1
- package/dist/contract.d.ts +8 -4
- package/dist/contract.js +7 -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 -6
- package/dist/content/index.js +0 -3
- /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
|
@@ -29,7 +29,7 @@ Everything exports flat from the root, and again grouped by role:
|
|
|
29
29
|
| [`/chrome`](./docs/components/chrome.md) | PageShell, Nav, Footer |
|
|
30
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
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
|
|
32
|
+
| [`/edit`](./docs/edit-mode.md) | Editable, setEditAdapter(adapter, EDIT_CHROME)/getEditAdapter, descriptors and helpers, collectionEditing, LocalizedText |
|
|
33
33
|
| [`/config`](./docs/getting-started.md#wiring-an-app-uiprovider) | UiProvider, UiConfig, the locale set, the default Catalan messages |
|
|
34
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
35
|
| `/testing` | createRemoteFormMock — the remote-form stand-in for stories and tests |
|
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';
|
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
<script module lang="ts">
|
|
2
|
-
import type { ArticleEdit } from '../../content/types.js';
|
|
3
|
-
|
|
4
|
-
/** What a CMS may open on a project's own page: title, excerpt, and the body as rich text. */
|
|
5
|
-
export type ProjectPageEdit = ArticleEdit;
|
|
6
|
-
</script>
|
|
7
|
-
|
|
8
1
|
<script lang="ts">
|
|
9
2
|
import { getUiConfig } from '../../config/context.js';
|
|
10
|
-
import type { ProjectArticleData } from '../../content/types.js';
|
|
3
|
+
import type { ArticleEdit, ProjectArticleData } from '../../content/types.js';
|
|
11
4
|
import ActionLabel from '../../edit/ActionLabel.svelte';
|
|
12
5
|
import Editable from '../../edit/Editable.svelte';
|
|
13
6
|
import PageShell from '../layout/PageShell.svelte';
|
|
@@ -24,7 +17,8 @@
|
|
|
24
17
|
*/
|
|
25
18
|
interface Props {
|
|
26
19
|
project: ProjectArticleData;
|
|
27
|
-
|
|
20
|
+
/** What a CMS may open on a project's own page: title, excerpt, and the body as rich text. */
|
|
21
|
+
edit?: ArticleEdit;
|
|
28
22
|
}
|
|
29
23
|
|
|
30
24
|
let { project, edit }: Props = $props();
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import type { ArticleEdit } from '../../content/types.js';
|
|
2
|
-
/** What a CMS may open on a project's own page: title, excerpt, and the body as rich text. */
|
|
3
|
-
export type ProjectPageEdit = ArticleEdit;
|
|
4
|
-
import type { ProjectArticleData } from '../../content/types.js';
|
|
1
|
+
import type { ArticleEdit, ProjectArticleData } from '../../content/types.js';
|
|
5
2
|
/**
|
|
6
3
|
* A project's story: date, title, excerpt, hero image, the rich-text body
|
|
7
4
|
* with a share row, and — when the project points somewhere — a preview
|
|
@@ -10,7 +7,8 @@ import type { ProjectArticleData } from '../../content/types.js';
|
|
|
10
7
|
*/
|
|
11
8
|
interface Props {
|
|
12
9
|
project: ProjectArticleData;
|
|
13
|
-
|
|
10
|
+
/** What a CMS may open on a project's own page: title, excerpt, and the body as rich text. */
|
|
11
|
+
edit?: ArticleEdit;
|
|
14
12
|
}
|
|
15
13
|
declare const ProjectPage: import("svelte").Component<Props, {}, "">;
|
|
16
14
|
type ProjectPage = ReturnType<typeof ProjectPage>;
|
|
@@ -1,17 +1,7 @@
|
|
|
1
|
-
<script module lang="ts">
|
|
2
|
-
import type { ArticleEdit } from '../../content/types.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* What a CMS may open on a weekly's own page: title, excerpt, and the body
|
|
6
|
-
* as rich text. The «Dades i fonts» and «Segueix explorant» headings and
|
|
7
|
-
* the back link edit through `config.messageEdit` over their own keys.
|
|
8
|
-
*/
|
|
9
|
-
export type WeeklyPageEdit = ArticleEdit;
|
|
10
|
-
</script>
|
|
11
|
-
|
|
12
1
|
<script lang="ts">
|
|
13
2
|
import { getUiConfig } from '../../config/context.js';
|
|
14
3
|
import type {
|
|
4
|
+
ArticleEdit,
|
|
15
5
|
CommentThreadData,
|
|
16
6
|
ReactionSummary,
|
|
17
7
|
WeeklyArticleData,
|
|
@@ -48,7 +38,12 @@
|
|
|
48
38
|
/** One preflighted instance per reply box; see CommentSection. */
|
|
49
39
|
replyFormFor: (threadId: string) => CommentFormInstance;
|
|
50
40
|
reactionForms: ReactionBarForms;
|
|
51
|
-
|
|
41
|
+
/**
|
|
42
|
+
* What a CMS may open on a weekly's own page: title, excerpt, and the body
|
|
43
|
+
* as rich text. The «Dades i fonts» and «Segueix explorant» headings and
|
|
44
|
+
* the back link edit through `config.messageEdit` over their own keys.
|
|
45
|
+
*/
|
|
46
|
+
edit?: ArticleEdit;
|
|
52
47
|
}
|
|
53
48
|
|
|
54
49
|
let {
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import type { ArticleEdit } from '../../content/types.js';
|
|
2
|
-
/**
|
|
3
|
-
* What a CMS may open on a weekly's own page: title, excerpt, and the body
|
|
4
|
-
* as rich text. The «Dades i fonts» and «Segueix explorant» headings and
|
|
5
|
-
* the back link edit through `config.messageEdit` over their own keys.
|
|
6
|
-
*/
|
|
7
|
-
export type WeeklyPageEdit = ArticleEdit;
|
|
8
|
-
import type { CommentThreadData, ReactionSummary, WeeklyArticleData, WeeklyCardData } from '../../content/types.js';
|
|
1
|
+
import type { ArticleEdit, CommentThreadData, ReactionSummary, WeeklyArticleData, WeeklyCardData } from '../../content/types.js';
|
|
9
2
|
import { type CommentFormInstance } from '../weeklies/CommentSection.svelte';
|
|
10
3
|
import { type ReactionBarForms } from '../weeklies/ReactionBar.svelte';
|
|
11
4
|
/**
|
|
@@ -28,7 +21,12 @@ interface Props {
|
|
|
28
21
|
/** One preflighted instance per reply box; see CommentSection. */
|
|
29
22
|
replyFormFor: (threadId: string) => CommentFormInstance;
|
|
30
23
|
reactionForms: ReactionBarForms;
|
|
31
|
-
|
|
24
|
+
/**
|
|
25
|
+
* What a CMS may open on a weekly's own page: title, excerpt, and the body
|
|
26
|
+
* as rich text. The «Dades i fonts» and «Segueix explorant» headings and
|
|
27
|
+
* the back link edit through `config.messageEdit` over their own keys.
|
|
28
|
+
*/
|
|
29
|
+
edit?: ArticleEdit;
|
|
32
30
|
}
|
|
33
31
|
declare const WeeklyPage: import("svelte").Component<Props, {}, "">;
|
|
34
32
|
type WeeklyPage = ReturnType<typeof WeeklyPage>;
|
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type
|
|
2
|
+
import { onMount, type Snippet } from 'svelte';
|
|
3
3
|
import IconButton from './IconButton.svelte';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* A modal over the native <dialog>: showModal() traps focus and layers the
|
|
7
|
+
* backdrop, the cancel event covers Escape, a backdrop click closes.
|
|
8
|
+
*
|
|
9
|
+
* The element is NOT part of the server-rendered document. A <dialog> is
|
|
10
|
+
* flow content, so wherever a caller mounts one inside phrasing content —
|
|
11
|
+
* a LinkEdit inside a <p>, which NewsletterSignup does — the HTML parser
|
|
12
|
+
* closes the paragraph early, Svelte reports `node_invalid_placement_ssr`
|
|
13
|
+
* on the way out and a hydration mismatch on the way back in, on every
|
|
14
|
+
* page that renders the caller. No dialog is open at first paint (opening
|
|
15
|
+
* one takes a click), so the server has nothing to say about it; the
|
|
16
|
+
* element is created after mount, by script, which the content model does
|
|
17
|
+
* not police. Placement is this module's to own — a caller cannot know
|
|
18
|
+
* what its parent is — and owning it here fixes every caller at once,
|
|
19
|
+
* ConfirmDialog included.
|
|
20
|
+
*/
|
|
21
|
+
|
|
5
22
|
interface Props {
|
|
6
23
|
open: boolean;
|
|
7
24
|
title: string;
|
|
@@ -16,9 +33,11 @@
|
|
|
16
33
|
let { open, title, onclose, closeLabel = 'Tanca', children }: Props = $props();
|
|
17
34
|
|
|
18
35
|
let dialog: HTMLDialogElement | undefined = $state();
|
|
36
|
+
let mounted = $state(false);
|
|
37
|
+
onMount(() => {
|
|
38
|
+
mounted = true;
|
|
39
|
+
});
|
|
19
40
|
|
|
20
|
-
// Native <dialog>: showModal() traps focus and layers the backdrop; the
|
|
21
|
-
// cancel event covers Escape.
|
|
22
41
|
$effect(() => {
|
|
23
42
|
if (!dialog) return;
|
|
24
43
|
if (open && !dialog.open) dialog.showModal();
|
|
@@ -32,15 +51,17 @@
|
|
|
32
51
|
}
|
|
33
52
|
</script>
|
|
34
53
|
|
|
35
|
-
|
|
36
|
-
<
|
|
37
|
-
<div class="
|
|
38
|
-
<
|
|
39
|
-
|
|
54
|
+
{#if mounted}
|
|
55
|
+
<dialog bind:this={dialog} oncancel={onclose} {onclick}>
|
|
56
|
+
<div class="panel">
|
|
57
|
+
<div class="top">
|
|
58
|
+
<h2>{title}</h2>
|
|
59
|
+
<IconButton icon="close" label={closeLabel} onclick={onclose} />
|
|
60
|
+
</div>
|
|
61
|
+
{@render children()}
|
|
40
62
|
</div>
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
</dialog>
|
|
63
|
+
</dialog>
|
|
64
|
+
{/if}
|
|
44
65
|
|
|
45
66
|
<style>
|
|
46
67
|
dialog {
|
|
@@ -1,4 +1,20 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Snippet } from 'svelte';
|
|
2
|
+
/**
|
|
3
|
+
* A modal over the native <dialog>: showModal() traps focus and layers the
|
|
4
|
+
* backdrop, the cancel event covers Escape, a backdrop click closes.
|
|
5
|
+
*
|
|
6
|
+
* The element is NOT part of the server-rendered document. A <dialog> is
|
|
7
|
+
* flow content, so wherever a caller mounts one inside phrasing content —
|
|
8
|
+
* a LinkEdit inside a <p>, which NewsletterSignup does — the HTML parser
|
|
9
|
+
* closes the paragraph early, Svelte reports `node_invalid_placement_ssr`
|
|
10
|
+
* on the way out and a hydration mismatch on the way back in, on every
|
|
11
|
+
* page that renders the caller. No dialog is open at first paint (opening
|
|
12
|
+
* one takes a click), so the server has nothing to say about it; the
|
|
13
|
+
* element is created after mount, by script, which the content model does
|
|
14
|
+
* not police. Placement is this module's to own — a caller cannot know
|
|
15
|
+
* what its parent is — and owning it here fixes every caller at once,
|
|
16
|
+
* ConfirmDialog included.
|
|
17
|
+
*/
|
|
2
18
|
interface Props {
|
|
3
19
|
open: boolean;
|
|
4
20
|
title: string;
|
package/dist/config/context.d.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
import { type UiConfig, type UiConfigInput } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* The provider-less config: identity hrefs, Catalan, no router. Complete on
|
|
4
|
-
* purpose — every component must render without a provider, which is what
|
|
5
|
-
* keeps stories and component tests free of app wiring.
|
|
6
|
-
*/
|
|
7
|
-
export declare const DEFAULT_UI_CONFIG: UiConfig;
|
|
8
2
|
/**
|
|
9
3
|
* Installs the app's config for the subtree. Takes a *thunk* so a provider
|
|
10
4
|
* whose input is a reactive prop stays live: the resolved object delegates
|
package/dist/config/context.js
CHANGED
|
@@ -8,7 +8,7 @@ const KEY = Symbol.for('@vit-foundation/ui:config');
|
|
|
8
8
|
* purpose — every component must render without a provider, which is what
|
|
9
9
|
* keeps stories and component tests free of app wiring.
|
|
10
10
|
*/
|
|
11
|
-
|
|
11
|
+
const DEFAULT_UI_CONFIG = {
|
|
12
12
|
href: (path) => path,
|
|
13
13
|
locale: () => BASE_LOCALE,
|
|
14
14
|
locales: LOCALES,
|
package/dist/config/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { getUiConfig, setUiConfig } from './context.js';
|
|
2
2
|
export { defaultEditMessages } from './edit-messages.js';
|
|
3
3
|
export type { EditMessages } from './edit-messages.js';
|
|
4
4
|
export { defaultMessages } from './messages.js';
|
|
5
5
|
export { default as UiProvider } from './UiProvider.svelte';
|
|
6
6
|
export { BASE_LOCALE, LOCALES } from './types.js';
|
|
7
|
-
export type { Locale, NotParameterized,
|
|
7
|
+
export type { Locale, NotParameterized, ParameterlessKey, SiteLink, UiConfig, UiConfigInput, UiMessages } from './types.js';
|
package/dist/config/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { getUiConfig, setUiConfig } from './context.js';
|
|
2
2
|
export { defaultEditMessages } from './edit-messages.js';
|
|
3
3
|
export { defaultMessages } from './messages.js';
|
|
4
4
|
export { default as UiProvider } from './UiProvider.svelte';
|
|
@@ -32,10 +32,13 @@ export type { LegalPageEdit } from './components/pages/LegalPage.svelte';
|
|
|
32
32
|
export { default as WeekliesPage } from './components/pages/WeekliesPage.svelte';
|
|
33
33
|
export type { WeekliesPageEdit } from './components/pages/WeekliesPage.svelte';
|
|
34
34
|
export { default as ProjectPage } from './components/pages/ProjectPage.svelte';
|
|
35
|
-
export type { ProjectPageEdit } from './components/pages/ProjectPage.svelte';
|
|
36
35
|
export { default as WeeklyPage } from './components/pages/WeeklyPage.svelte';
|
|
37
|
-
export
|
|
38
|
-
export
|
|
36
|
+
export { renderBody } from './content/richtext.js';
|
|
37
|
+
export type { RichTextBlock } from './content/richtext.js';
|
|
38
|
+
export { GET_INVOLVED_REASON_KEYS, PAGE_COPY_KEYS } from './content/pages.js';
|
|
39
|
+
export type { CopyEditFor, CopyKey, PageCopy, PageId } from './content/pages.js';
|
|
40
|
+
export { COMMENT_STATUSES, CONTACT_CATEGORIES, MILESTONE_CATEGORIES, PROJECT_KINDS, REACTIONS } from './content/types.js';
|
|
41
|
+
export type { ArticleEdit, CollaboratorData, CommentData, CommentThreadData, CommentStatus, ContactCategory, FieldConstraint, FormFailReason, JobOpeningData, MilestoneCategory, MilestoneData, ProjectArticleData, ProjectCardData, ProjectKind, Reaction, ReactionSummary, ReactionTarget, SortDirection, TeamMemberData, ThemeData, WeeklyArticleData, WeeklyCardData, WeeklySourceData } from './content/types.js';
|
|
39
42
|
export { MILESTONE_CATEGORY_COLOR, matchesMilestoneFilter, milestoneCategoryLabel } from './utils/milestones.js';
|
|
40
43
|
export { createUrlFilters } from './utils/url-filters.svelte.js';
|
|
41
44
|
export type { UrlFilters, UrlFiltersConfig } from './utils/url-filters.svelte.js';
|
|
@@ -23,7 +23,11 @@ export { default as LegalPage } from './components/pages/LegalPage.svelte';
|
|
|
23
23
|
export { default as WeekliesPage } from './components/pages/WeekliesPage.svelte';
|
|
24
24
|
export { default as ProjectPage } from './components/pages/ProjectPage.svelte';
|
|
25
25
|
export { default as WeeklyPage } from './components/pages/WeeklyPage.svelte';
|
|
26
|
-
|
|
26
|
+
// The content vocabularies and data shapes, beside the renderers that consume
|
|
27
|
+
// them. `./contract` names the component-free ones a second time, by design.
|
|
28
|
+
export { renderBody } from './content/richtext.js';
|
|
29
|
+
export { GET_INVOLVED_REASON_KEYS, PAGE_COPY_KEYS } from './content/pages.js';
|
|
30
|
+
export { COMMENT_STATUSES, CONTACT_CATEGORIES, MILESTONE_CATEGORIES, PROJECT_KINDS, REACTIONS } from './content/types.js';
|
|
27
31
|
export { MILESTONE_CATEGORY_COLOR, matchesMilestoneFilter, milestoneCategoryLabel } from './utils/milestones.js';
|
|
28
32
|
// The two list rules: URL-mirrored filters, and the weeklies index over them.
|
|
29
33
|
export { createUrlFilters } from './utils/url-filters.svelte.js';
|
package/dist/contract.d.ts
CHANGED
|
@@ -17,9 +17,13 @@
|
|
|
17
17
|
* is what makes it a promise rather than a description: adding a `.svelte`
|
|
18
18
|
* anywhere beneath it fails here rather than in a consumer's server build.
|
|
19
19
|
*
|
|
20
|
-
* It adds nothing. Every name below is already exported from a
|
|
21
|
-
* subpath and stays there — this is a second door into the same
|
|
22
|
-
* new floor, and the barrel remains the surface a component host
|
|
20
|
+
* It adds almost nothing. Every name below is already exported from a
|
|
21
|
+
* semantic subpath and stays there — this is a second door into the same
|
|
22
|
+
* rooms, not a new floor, and the barrel remains the surface a component host
|
|
23
|
+
* reads. `contract-names.test.ts` holds that to be true, name by name and from
|
|
24
|
+
* the same source module; its `CONTRACT_ONLY` list names the three exceptions
|
|
25
|
+
* (the two destination classifiers and the title format), each with its
|
|
26
|
+
* reason, so a fourth cannot arrive unexplained.
|
|
23
27
|
*
|
|
24
28
|
* It was cut to fit ONE consumer, and the second one did not fit through it.
|
|
25
29
|
* vit-brain's four restatements were the motive, so the first version carried
|
|
@@ -38,7 +42,7 @@
|
|
|
38
42
|
* the guard walks them like the rest.
|
|
39
43
|
*/
|
|
40
44
|
export { LOCALES, BASE_LOCALE } from './config/types.js';
|
|
41
|
-
export type { Locale, NotParameterized,
|
|
45
|
+
export type { Locale, NotParameterized, ParameterlessKey, SiteLink, UiMessages } from './config/types.js';
|
|
42
46
|
export { localize } from './edit/types.js';
|
|
43
47
|
export type { CollectionRef, ContentRef, EditableEntity, EditDescriptor, EntityOp, LocalizedText, PropertyDescriptor, PropertyOption, PropertyType, PropertyValue } from './edit/types.js';
|
|
44
48
|
export { REACTIONS } from './content/types.js';
|
package/dist/contract.js
CHANGED
|
@@ -17,9 +17,13 @@
|
|
|
17
17
|
* is what makes it a promise rather than a description: adding a `.svelte`
|
|
18
18
|
* anywhere beneath it fails here rather than in a consumer's server build.
|
|
19
19
|
*
|
|
20
|
-
* It adds nothing. Every name below is already exported from a
|
|
21
|
-
* subpath and stays there — this is a second door into the same
|
|
22
|
-
* new floor, and the barrel remains the surface a component host
|
|
20
|
+
* It adds almost nothing. Every name below is already exported from a
|
|
21
|
+
* semantic subpath and stays there — this is a second door into the same
|
|
22
|
+
* rooms, not a new floor, and the barrel remains the surface a component host
|
|
23
|
+
* reads. `contract-names.test.ts` holds that to be true, name by name and from
|
|
24
|
+
* the same source module; its `CONTRACT_ONLY` list names the three exceptions
|
|
25
|
+
* (the two destination classifiers and the title format), each with its
|
|
26
|
+
* reason, so a fourth cannot arrive unexplained.
|
|
23
27
|
*
|
|
24
28
|
* It was cut to fit ONE consumer, and the second one did not fit through it.
|
|
25
29
|
* vit-brain's four restatements were the motive, so the first version carried
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
4
|
-
import { getEditAdapter } from './context.js';
|
|
5
|
-
import { commitState } from './commit.svelte.js';
|
|
6
|
-
import type { EditDescriptor } from './types.js';
|
|
2
|
+
import type { EditableProps } from './chrome-props.js';
|
|
3
|
+
import { getEditAdapter, getEditChrome } from './context.js';
|
|
7
4
|
|
|
8
5
|
/**
|
|
9
|
-
* The inline-edit primitive. It renders no element of its own: the
|
|
10
|
-
* snippet receives the text and a bag of attributes, and spreads
|
|
11
|
-
* whatever semantic element the call site owns —
|
|
6
|
+
* The inline-edit primitive's GATE. It renders no element of its own: the
|
|
7
|
+
* child snippet receives the text and a bag of attributes, and spreads
|
|
8
|
+
* both onto whatever semantic element the call site owns —
|
|
12
9
|
*
|
|
13
10
|
* <Editable edit={descriptor} value={title}>
|
|
14
11
|
* {#snippet children(text, attrs)}<h3 {...attrs}>{text}</h3>{/snippet}
|
|
@@ -20,164 +17,23 @@
|
|
|
20
17
|
*
|
|
21
18
|
* Inactive — no `edit` descriptor, no adapter in context (a read-only
|
|
22
19
|
* app), or the adapter's `isEditing` off — it renders the child with an
|
|
23
|
-
* empty attribute bag: no listeners, no wrapper, no cost.
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* 'multiline'), Escape reverts to the last saved value, blur commits a
|
|
29
|
-
* dirty draft. A failed save keeps the draft on screen in the error state
|
|
30
|
-
* so nothing typed is lost. State is announced through a visually hidden
|
|
31
|
-
* `role="status"` region, the pattern ShareRow set for its copy action.
|
|
32
|
-
*
|
|
33
|
-
* Styling for the affordance lives in base.css (`[data-vit-editing]`):
|
|
34
|
-
* the element is rendered by the *caller's* markup, which scoped styles
|
|
35
|
-
* here could never reach.
|
|
20
|
+
* empty attribute bag: no listeners, no wrapper, no cost. Active, it
|
|
21
|
+
* mounts the LIVE half the host installed (`edit/live/Editable.svelte`,
|
|
22
|
+
* through `setEditAdapter`'s chrome), which owns the contenteditable, the
|
|
23
|
+
* commit lifecycle and the announcements. This file imports none of that,
|
|
24
|
+
* so a renderer that imports it ships none of that (`chrome-props.ts`).
|
|
36
25
|
*/
|
|
37
|
-
|
|
38
|
-
contenteditable?: 'plaintext-only' | 'true';
|
|
39
|
-
role?: 'textbox';
|
|
40
|
-
'aria-label'?: string;
|
|
41
|
-
'aria-multiline'?: 'true';
|
|
42
|
-
'data-vit-editing'?: 'idle' | 'dirty' | 'saving' | 'error';
|
|
43
|
-
onbeforeinput?: (event: InputEvent) => void;
|
|
44
|
-
oninput?: (event: Event) => void;
|
|
45
|
-
onblur?: (event: FocusEvent) => void;
|
|
46
|
-
onkeydown?: (event: KeyboardEvent) => void;
|
|
47
|
-
onpaste?: (event: ClipboardEvent) => void;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
interface Props {
|
|
51
|
-
/** Identity of the string. Omit it and this is a passthrough. */
|
|
52
|
-
edit?: EditDescriptor;
|
|
53
|
-
value: string;
|
|
54
|
-
children: Snippet<[string, EditableAttrs]>;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
let { edit, value, children }: Props = $props();
|
|
26
|
+
let { edit, value, children }: EditableProps = $props();
|
|
58
27
|
|
|
59
28
|
const adapter = getEditAdapter();
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* The commit lifecycle — the four states, the announcements, the last
|
|
64
|
-
* persisted value and the follow-the-prop rule — is shared with
|
|
65
|
-
* `PropertyRow` (see `./commit.svelte.js`). What stays here is where the
|
|
66
|
-
* DRAFT lives: in the DOM, because this is a contenteditable.
|
|
67
|
-
*/
|
|
68
|
-
// svelte-ignore state_referenced_locally
|
|
69
|
-
const commit_ = commitState(value, config.editMessages);
|
|
70
|
-
/** What the child snippet renders. Only rewritten while the DOM is not
|
|
71
|
-
* being typed in — Svelte and the reader must not fight over the node. */
|
|
72
|
-
// svelte-ignore state_referenced_locally
|
|
73
|
-
let renderText = $state(value);
|
|
74
|
-
|
|
75
|
-
$effect(() => {
|
|
76
|
-
const adopted = commit_.follow(value);
|
|
77
|
-
if (adopted !== null) renderText = adopted;
|
|
78
|
-
});
|
|
29
|
+
const chrome = getEditChrome();
|
|
79
30
|
|
|
80
31
|
const active = $derived(edit !== undefined && (adapter?.isEditing ?? false));
|
|
81
|
-
const multiline = $derived(edit?.format === 'multiline' || edit?.format === 'richtext');
|
|
82
|
-
|
|
83
|
-
/** contenteditable text, innerText keeps line breaks; NBSPs become spaces. */
|
|
84
|
-
function textOf(element: HTMLElement): string {
|
|
85
|
-
return (element.innerText ?? element.textContent ?? '').replace(/\u00a0/g, ' ');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function handleBeforeInput(event: InputEvent): void {
|
|
89
|
-
// 'plaintext-only' is not universal (Firefox); strip rich insertions.
|
|
90
|
-
if (event.inputType === 'insertFromPaste' || event.inputType === 'insertFromDrop') return;
|
|
91
|
-
if (!multiline && event.inputType === 'insertParagraph') event.preventDefault();
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function handlePaste(event: ClipboardEvent): void {
|
|
95
|
-
// Plain text only, whatever the clipboard holds.
|
|
96
|
-
event.preventDefault();
|
|
97
|
-
const text = event.clipboardData?.getData('text/plain') ?? '';
|
|
98
|
-
const selection = window.getSelection();
|
|
99
|
-
if (!selection || selection.rangeCount === 0) return;
|
|
100
|
-
const range = selection.getRangeAt(0);
|
|
101
|
-
range.deleteContents();
|
|
102
|
-
range.insertNode(document.createTextNode(multiline ? text : text.replace(/\s*\n\s*/g, ' ')));
|
|
103
|
-
selection.collapseToEnd();
|
|
104
|
-
commit_.markDirty();
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function handleInput(): void {
|
|
108
|
-
commit_.markDirty();
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async function commit(element: HTMLElement): Promise<void> {
|
|
112
|
-
if (!edit || !adapter) return;
|
|
113
|
-
const draft = textOf(element).trim();
|
|
114
|
-
if (draft === commit_.saved) {
|
|
115
|
-
commit_.settle();
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
// A failed save leaves the draft in the DOM; the reader decides whether
|
|
119
|
-
// to retry.
|
|
120
|
-
await commit_.commit(draft, () => adapter.save(edit, draft));
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function handleBlur(event: FocusEvent): void {
|
|
124
|
-
const status = commit_.status;
|
|
125
|
-
if (status === 'dirty' || status === 'error') void commit(event.currentTarget as HTMLElement);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function handleKeydown(event: KeyboardEvent): void {
|
|
129
|
-
const element = event.currentTarget as HTMLElement;
|
|
130
|
-
if (event.key === 'Escape') {
|
|
131
|
-
// Svelte's cached text still equals renderText, so a state write
|
|
132
|
-
// alone cannot repaint a node the reader has mutated: restore the
|
|
133
|
-
// DOM directly, then settle.
|
|
134
|
-
element.textContent = commit_.saved;
|
|
135
|
-
commit_.revert();
|
|
136
|
-
element.blur();
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
const commitKey = multiline
|
|
140
|
-
? event.key === 'Enter' && (event.metaKey || event.ctrlKey)
|
|
141
|
-
: event.key === 'Enter';
|
|
142
|
-
if (commitKey) {
|
|
143
|
-
event.preventDefault();
|
|
144
|
-
void commit(element);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const attrs = $derived<EditableAttrs>(
|
|
149
|
-
active
|
|
150
|
-
? {
|
|
151
|
-
contenteditable: 'plaintext-only',
|
|
152
|
-
role: 'textbox',
|
|
153
|
-
'aria-label': edit?.label,
|
|
154
|
-
'aria-multiline': multiline ? 'true' : undefined,
|
|
155
|
-
'data-vit-editing': commit_.status,
|
|
156
|
-
onbeforeinput: handleBeforeInput,
|
|
157
|
-
oninput: handleInput,
|
|
158
|
-
onblur: handleBlur,
|
|
159
|
-
onkeydown: handleKeydown,
|
|
160
|
-
onpaste: handlePaste
|
|
161
|
-
}
|
|
162
|
-
: {}
|
|
163
|
-
);
|
|
164
32
|
</script>
|
|
165
33
|
|
|
166
|
-
{
|
|
167
|
-
{
|
|
168
|
-
<
|
|
34
|
+
{#if active && chrome}
|
|
35
|
+
{@const Live = chrome.Editable}
|
|
36
|
+
<Live {edit} {value} {children} />
|
|
37
|
+
{:else}
|
|
38
|
+
{@render children(value, {})}
|
|
169
39
|
{/if}
|
|
170
|
-
|
|
171
|
-
<style>
|
|
172
|
-
.status {
|
|
173
|
-
position: absolute;
|
|
174
|
-
width: 1px;
|
|
175
|
-
height: 1px;
|
|
176
|
-
padding: 0;
|
|
177
|
-
margin: -1px;
|
|
178
|
-
overflow: hidden;
|
|
179
|
-
clip-path: inset(50%);
|
|
180
|
-
white-space: nowrap;
|
|
181
|
-
border: 0;
|
|
182
|
-
}
|
|
183
|
-
</style>
|
|
@@ -1,52 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* The inline-edit primitive. It renders no element of its own: the child
|
|
5
|
-
* snippet receives the text and a bag of attributes, and spreads both onto
|
|
6
|
-
* whatever semantic element the call site owns —
|
|
7
|
-
*
|
|
8
|
-
* <Editable edit={descriptor} value={title}>
|
|
9
|
-
* {#snippet children(text, attrs)}<h3 {...attrs}>{text}</h3>{/snippet}
|
|
10
|
-
* </Editable>
|
|
11
|
-
*
|
|
12
|
-
* — the same contract Field uses for its control attrs, and for the same
|
|
13
|
-
* reason: the element (h1, p, span) is part of the document outline and
|
|
14
|
-
* belongs to the caller, not behind a prop.
|
|
15
|
-
*
|
|
16
|
-
* Inactive — no `edit` descriptor, no adapter in context (a read-only
|
|
17
|
-
* app), or the adapter's `isEditing` off — it renders the child with an
|
|
18
|
-
* empty attribute bag: no listeners, no wrapper, no cost.
|
|
19
|
-
*
|
|
20
|
-
* Active, it makes the element a plain-text `contenteditable` textbox.
|
|
21
|
-
* The DOM owns the draft while the reader types; this component reads it
|
|
22
|
-
* back on commit. Enter commits single-line fields (Cmd/Ctrl+Enter for
|
|
23
|
-
* 'multiline'), Escape reverts to the last saved value, blur commits a
|
|
24
|
-
* dirty draft. A failed save keeps the draft on screen in the error state
|
|
25
|
-
* so nothing typed is lost. State is announced through a visually hidden
|
|
26
|
-
* `role="status"` region, the pattern ShareRow set for its copy action.
|
|
27
|
-
*
|
|
28
|
-
* Styling for the affordance lives in base.css (`[data-vit-editing]`):
|
|
29
|
-
* the element is rendered by the *caller's* markup, which scoped styles
|
|
30
|
-
* here could never reach.
|
|
31
|
-
*/
|
|
32
|
-
interface EditableAttrs {
|
|
33
|
-
contenteditable?: 'plaintext-only' | 'true';
|
|
34
|
-
role?: 'textbox';
|
|
35
|
-
'aria-label'?: string;
|
|
36
|
-
'aria-multiline'?: 'true';
|
|
37
|
-
'data-vit-editing'?: 'idle' | 'dirty' | 'saving' | 'error';
|
|
38
|
-
onbeforeinput?: (event: InputEvent) => void;
|
|
39
|
-
oninput?: (event: Event) => void;
|
|
40
|
-
onblur?: (event: FocusEvent) => void;
|
|
41
|
-
onkeydown?: (event: KeyboardEvent) => void;
|
|
42
|
-
onpaste?: (event: ClipboardEvent) => void;
|
|
43
|
-
}
|
|
44
|
-
interface Props {
|
|
45
|
-
/** Identity of the string. Omit it and this is a passthrough. */
|
|
46
|
-
edit?: EditDescriptor;
|
|
47
|
-
value: string;
|
|
48
|
-
children: Snippet<[string, EditableAttrs]>;
|
|
49
|
-
}
|
|
50
|
-
declare const Editable: import("svelte").Component<Props, {}, "">;
|
|
1
|
+
import type { EditableProps } from './chrome-props.js';
|
|
2
|
+
declare const Editable: import("svelte").Component<EditableProps, {}, "">;
|
|
51
3
|
type Editable = ReturnType<typeof Editable>;
|
|
52
4
|
export default Editable;
|