@drawnagency/primitives 0.1.117 → 0.1.119
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/dist/{chunk-O37AV27P.js → chunk-5JYHTHVR.js} +28 -0
- package/dist/{chunk-W72BPGR3.js → chunk-BZKHHUIN.js} +44 -8
- package/dist/{chunk-CEFFP3CH.js → chunk-G6O3TYPJ.js} +297 -83
- package/dist/{chunk-L7WTERU5.js → chunk-HFHM4MNK.js} +33 -10
- package/dist/{chunk-RIITGTV3.js → chunk-LQHBC7C6.js} +37 -2
- package/dist/{chunk-VY3IFGE3.js → chunk-QAMAIDXZ.js} +1 -1
- package/dist/{chunk-LNQUUC2S.js → chunk-RIN2QCYJ.js} +9 -11
- package/dist/{chunk-HUOW3ZV2.js → chunk-UNFPATDE.js} +8 -10
- package/dist/{chunk-MSBHMY3C.js → chunk-YKKGVSJH.js} +35 -37
- package/dist/{chunk-67P34WNW.js → chunk-ZKVQMIMJ.js} +3 -20
- package/dist/collab/rebase.js +4 -5
- package/dist/components/chat/ChatComposer.d.ts +0 -4
- package/dist/components/chat/ChatComposer.d.ts.map +1 -1
- package/dist/components/chat/ChatPanel.d.ts +2 -3
- package/dist/components/chat/ChatPanel.d.ts.map +1 -1
- package/dist/components/chat/markdown.d.ts +2 -3
- package/dist/components/chat/markdown.d.ts.map +1 -1
- package/dist/components/editor/PagesModal.d.ts +0 -2
- package/dist/components/editor/PagesModal.d.ts.map +1 -1
- package/dist/components/editor/index.js +15 -19
- package/dist/components/primitives/EditablePlainText.d.ts.map +1 -1
- package/dist/components/primitives/index.js +11 -15
- package/dist/components/sections/brand-guide.js +10 -13
- package/dist/components/sections/register-schemas.js +16 -21
- package/dist/components/sections/register.js +16 -21
- package/dist/components/shell/AudienceChips.d.ts +1 -3
- package/dist/components/shell/AudienceChips.d.ts.map +1 -1
- package/dist/components/shell/AudienceDetailsPanel.d.ts +3 -1
- package/dist/components/shell/AudienceDetailsPanel.d.ts.map +1 -1
- package/dist/components/shell/EditorShell.d.ts +0 -34
- package/dist/components/shell/EditorShell.d.ts.map +1 -1
- package/dist/components/shell/SiteSettingsModal.d.ts.map +1 -1
- package/dist/hooks/useSortableNavRow.d.ts +1 -1
- package/dist/hooks/useSortableRow.d.ts +1 -1
- package/dist/index.js +83 -90
- package/dist/lib/index-ops.js +5 -6
- package/dist/lib/index.js +27 -32
- package/dist/lib/pages.d.ts +0 -2
- package/dist/lib/pages.d.ts.map +1 -1
- package/dist/lib/sidecars.js +2 -2
- package/dist/lib/site-validation.js +13 -14
- package/dist/lib/storage-tier.js +4 -4
- package/dist/schemas/index.js +42 -45
- package/dist/schemas/site-config.d.ts +1 -277
- package/dist/schemas/site-config.d.ts.map +1 -1
- package/package.json +2 -42
- package/src/components/chat/ChatComposer.tsx +0 -4
- package/src/components/chat/ChatPanel.tsx +2 -3
- package/src/components/chat/markdown.ts +2 -3
- package/src/components/editor/PagesModal.tsx +1 -15
- package/src/components/editor/StatusIndicator.tsx +4 -4
- package/src/components/primitives/EditablePlainText.tsx +27 -3
- package/src/components/shell/AudienceChips.tsx +1 -3
- package/src/components/shell/AudienceDetailsPanel.tsx +6 -4
- package/src/components/shell/EditorShell.tsx +18 -114
- package/src/components/shell/SiteSettingsModal.tsx +0 -11
- package/src/lib/pages.ts +0 -22
- package/src/schemas/index.ts +0 -4
- package/src/schemas/site-config.ts +4 -32
- package/dist/chunk-4TZJZU2T.js +0 -92
- package/dist/chunk-DIMDRBFK.js +0 -11
- package/dist/chunk-FV7JQRCN.js +0 -94
- package/dist/chunk-IIDKW7AV.js +0 -41
- package/dist/chunk-JCDZAFX2.js +0 -224
- package/dist/chunk-MM7KZS5C.js +0 -30
- package/dist/chunk-QQ3S3P5P.js +0 -38
- package/dist/chunk-TEOAKGZA.js +0 -784
- package/dist/chunk-VAYZJKVR.js +0 -209
- package/dist/components/shell/DocInspector.d.ts +0 -18
- package/dist/components/shell/DocInspector.d.ts.map +0 -1
- package/dist/components/shell/DocumentEditorOverlay.d.ts +0 -29
- package/dist/components/shell/DocumentEditorOverlay.d.ts.map +0 -1
- package/dist/components/shell/DocumentPresentOverlay.d.ts +0 -30
- package/dist/components/shell/DocumentPresentOverlay.d.ts.map +0 -1
- package/dist/components/shell/SiteSettingsDecks.d.ts +0 -13
- package/dist/components/shell/SiteSettingsDecks.d.ts.map +0 -1
- package/dist/deck/DeckComponentView.d.ts +0 -13
- package/dist/deck/DeckComponentView.d.ts.map +0 -1
- package/dist/deck/DeckPlayer.d.ts +0 -26
- package/dist/deck/DeckPlayer.d.ts.map +0 -1
- package/dist/deck/DeckSlideView.d.ts +0 -17
- package/dist/deck/DeckSlideView.d.ts.map +0 -1
- package/dist/deck/DeckStage.d.ts +0 -16
- package/dist/deck/DeckStage.d.ts.map +0 -1
- package/dist/deck/components/DeckImage.d.ts +0 -14
- package/dist/deck/components/DeckImage.d.ts.map +0 -1
- package/dist/deck/components/DeckImageGrid.d.ts +0 -27
- package/dist/deck/components/DeckImageGrid.d.ts.map +0 -1
- package/dist/deck/components/DeckRichText.d.ts +0 -22
- package/dist/deck/components/DeckRichText.d.ts.map +0 -1
- package/dist/deck/components/DeckVideo.d.ts +0 -15
- package/dist/deck/components/DeckVideo.d.ts.map +0 -1
- package/dist/deck/editing.d.ts +0 -50
- package/dist/deck/editing.d.ts.map +0 -1
- package/dist/deck/flip.d.ts +0 -11
- package/dist/deck/flip.d.ts.map +0 -1
- package/dist/deck/index.d.ts +0 -24
- package/dist/deck/index.d.ts.map +0 -1
- package/dist/deck/index.js +0 -51
- package/dist/deck/layouts/CenteredTextLayout.d.ts +0 -12
- package/dist/deck/layouts/CenteredTextLayout.d.ts.map +0 -1
- package/dist/deck/layouts/ColumnSplitLayout.d.ts +0 -14
- package/dist/deck/layouts/ColumnSplitLayout.d.ts.map +0 -1
- package/dist/deck/layouts/FullBleedImageLayout.d.ts +0 -14
- package/dist/deck/layouts/FullBleedImageLayout.d.ts.map +0 -1
- package/dist/deck/media.d.ts +0 -33
- package/dist/deck/media.d.ts.map +0 -1
- package/dist/deck/tokens.d.ts +0 -22
- package/dist/deck/tokens.d.ts.map +0 -1
- package/dist/deck/useStageScale.d.ts +0 -10
- package/dist/deck/useStageScale.d.ts.map +0 -1
- package/dist/deck-builder/DeckBuilderApp.d.ts +0 -30
- package/dist/deck-builder/DeckBuilderApp.d.ts.map +0 -1
- package/dist/deck-builder/DeckBuilderTopBar.d.ts +0 -24
- package/dist/deck-builder/DeckBuilderTopBar.d.ts.map +0 -1
- package/dist/deck-builder/DeckChatPanel.d.ts +0 -45
- package/dist/deck-builder/DeckChatPanel.d.ts.map +0 -1
- package/dist/deck-builder/DeckEditor.d.ts +0 -37
- package/dist/deck-builder/DeckEditor.d.ts.map +0 -1
- package/dist/deck-builder/DeckLibrary.d.ts +0 -22
- package/dist/deck-builder/DeckLibrary.d.ts.map +0 -1
- package/dist/deck-builder/DeckSlideSurface.d.ts +0 -31
- package/dist/deck-builder/DeckSlideSurface.d.ts.map +0 -1
- package/dist/deck-builder/deck-api.d.ts +0 -76
- package/dist/deck-builder/deck-api.d.ts.map +0 -1
- package/dist/deck-builder/deck-chat-client.d.ts +0 -44
- package/dist/deck-builder/deck-chat-client.d.ts.map +0 -1
- package/dist/deck-builder/deck-chat-history.d.ts +0 -10
- package/dist/deck-builder/deck-chat-history.d.ts.map +0 -1
- package/dist/deck-builder/index.d.ts +0 -13
- package/dist/deck-builder/index.d.ts.map +0 -1
- package/dist/deck-builder/index.js +0 -1498
- package/dist/deck-builder/starter-deck.d.ts +0 -23
- package/dist/deck-builder/starter-deck.d.ts.map +0 -1
- package/dist/deck-builder/types.d.ts +0 -29
- package/dist/deck-builder/types.d.ts.map +0 -1
- package/dist/deck-builder/useSortableSlide.d.ts +0 -29
- package/dist/deck-builder/useSortableSlide.d.ts.map +0 -1
- package/dist/document/assets/fonts/albra-grotesk-bold.woff +0 -0
- package/dist/document/assets/fonts/albra-grotesk-regular.woff +0 -0
- package/dist/document/assets/fonts/kapra-neue-expanded-medium.woff +0 -0
- package/dist/document/assets/fonts/kapra-neue-expanded-regular.woff +0 -0
- package/dist/document/components/DocCoverPage.d.ts +0 -11
- package/dist/document/components/DocCoverPage.d.ts.map +0 -1
- package/dist/document/components/DocPage.d.ts +0 -10
- package/dist/document/components/DocPage.d.ts.map +0 -1
- package/dist/document/components/DocRail.d.ts +0 -28
- package/dist/document/components/DocRail.d.ts.map +0 -1
- package/dist/document/components/DocSectionPage.d.ts +0 -12
- package/dist/document/components/DocSectionPage.d.ts.map +0 -1
- package/dist/document/components/DocViewer.d.ts +0 -21
- package/dist/document/components/DocViewer.d.ts.map +0 -1
- package/dist/document/components/Wordmark.d.ts +0 -4
- package/dist/document/components/Wordmark.d.ts.map +0 -1
- package/dist/document/components/measure.d.ts +0 -7
- package/dist/document/components/measure.d.ts.map +0 -1
- package/dist/document/components/useDocPages.d.ts +0 -12
- package/dist/document/components/useDocPages.d.ts.map +0 -1
- package/dist/document/engine/blocks.d.ts +0 -2
- package/dist/document/engine/blocks.d.ts.map +0 -1
- package/dist/document/engine/harvest.d.ts +0 -3
- package/dist/document/engine/harvest.d.ts.map +0 -1
- package/dist/document/engine/masonry.d.ts +0 -26
- package/dist/document/engine/masonry.d.ts.map +0 -1
- package/dist/document/engine/paginate.d.ts +0 -43
- package/dist/document/engine/paginate.d.ts.map +0 -1
- package/dist/document/engine/settings.d.ts +0 -91
- package/dist/document/engine/settings.d.ts.map +0 -1
- package/dist/document/engine/types.d.ts +0 -44
- package/dist/document/engine/types.d.ts.map +0 -1
- package/dist/document/index.d.ts +0 -13
- package/dist/document/index.d.ts.map +0 -1
- package/dist/document/index.js +0 -1030
- package/dist/document/styles/document.css +0 -217
- package/dist/lib/deck-access.d.ts +0 -86
- package/dist/lib/deck-access.d.ts.map +0 -1
- package/dist/lib/deck-access.js +0 -58
- package/dist/lib/deck-dexie.d.ts +0 -88
- package/dist/lib/deck-dexie.d.ts.map +0 -1
- package/dist/lib/deck-dexie.js +0 -22
- package/dist/lib/deck-edit.d.ts +0 -67
- package/dist/lib/deck-edit.d.ts.map +0 -1
- package/dist/lib/deck-media-assets.d.ts +0 -27
- package/dist/lib/deck-media-assets.d.ts.map +0 -1
- package/dist/lib/deck-media-assets.js +0 -25
- package/dist/lib/deck-media-refs.d.ts +0 -35
- package/dist/lib/deck-media-refs.d.ts.map +0 -1
- package/dist/lib/deck-media-refs.js +0 -36
- package/dist/lib/deck-tool.d.ts +0 -25
- package/dist/lib/deck-tool.d.ts.map +0 -1
- package/dist/lib/deck-tool.js +0 -58
- package/dist/lib/document-input.d.ts +0 -25
- package/dist/lib/document-input.d.ts.map +0 -1
- package/dist/schemas/deck-meta.d.ts +0 -85
- package/dist/schemas/deck-meta.d.ts.map +0 -1
- package/dist/schemas/deck-meta.js +0 -64
- package/dist/schemas/deck.d.ts +0 -954
- package/dist/schemas/deck.d.ts.map +0 -1
- package/dist/schemas/deck.js +0 -65
- package/src/components/shell/DocInspector.tsx +0 -314
- package/src/components/shell/DocumentEditorOverlay.tsx +0 -179
- package/src/components/shell/DocumentPresentOverlay.tsx +0 -87
- package/src/components/shell/SiteSettingsDecks.tsx +0 -41
- package/src/deck/DeckComponentView.tsx +0 -28
- package/src/deck/DeckPlayer.tsx +0 -166
- package/src/deck/DeckSlideView.tsx +0 -38
- package/src/deck/DeckStage.tsx +0 -39
- package/src/deck/components/DeckImage.tsx +0 -38
- package/src/deck/components/DeckImageGrid.tsx +0 -254
- package/src/deck/components/DeckRichText.tsx +0 -111
- package/src/deck/components/DeckVideo.tsx +0 -81
- package/src/deck/editing.tsx +0 -201
- package/src/deck/flip.ts +0 -70
- package/src/deck/index.ts +0 -30
- package/src/deck/layouts/CenteredTextLayout.tsx +0 -29
- package/src/deck/layouts/ColumnSplitLayout.tsx +0 -69
- package/src/deck/layouts/FullBleedImageLayout.tsx +0 -58
- package/src/deck/media.tsx +0 -46
- package/src/deck/tokens.ts +0 -57
- package/src/deck/useStageScale.ts +0 -44
- package/src/deck-builder/DeckBuilderApp.tsx +0 -572
- package/src/deck-builder/DeckBuilderTopBar.tsx +0 -62
- package/src/deck-builder/DeckChatPanel.tsx +0 -334
- package/src/deck-builder/DeckEditor.tsx +0 -258
- package/src/deck-builder/DeckLibrary.tsx +0 -195
- package/src/deck-builder/DeckSlideSurface.tsx +0 -55
- package/src/deck-builder/deck-api.ts +0 -177
- package/src/deck-builder/deck-chat-client.ts +0 -129
- package/src/deck-builder/deck-chat-history.ts +0 -62
- package/src/deck-builder/index.ts +0 -12
- package/src/deck-builder/starter-deck.ts +0 -86
- package/src/deck-builder/types.ts +0 -24
- package/src/deck-builder/useSortableSlide.ts +0 -102
- package/src/document/assets/fonts/albra-grotesk-bold.woff +0 -0
- package/src/document/assets/fonts/albra-grotesk-regular.woff +0 -0
- package/src/document/assets/fonts/kapra-neue-expanded-medium.woff +0 -0
- package/src/document/assets/fonts/kapra-neue-expanded-regular.woff +0 -0
- package/src/document/components/DocCoverPage.tsx +0 -45
- package/src/document/components/DocPage.tsx +0 -40
- package/src/document/components/DocRail.tsx +0 -189
- package/src/document/components/DocSectionPage.tsx +0 -48
- package/src/document/components/DocViewer.tsx +0 -313
- package/src/document/components/Wordmark.tsx +0 -18
- package/src/document/components/measure.tsx +0 -133
- package/src/document/components/useDocPages.ts +0 -95
- package/src/document/engine/blocks.ts +0 -116
- package/src/document/engine/harvest.ts +0 -95
- package/src/document/engine/masonry.ts +0 -158
- package/src/document/engine/paginate.ts +0 -232
- package/src/document/engine/settings.ts +0 -125
- package/src/document/engine/types.ts +0 -64
- package/src/document/index.ts +0 -18
- package/src/document/styles/document.css +0 -217
- package/src/lib/deck-access.ts +0 -132
- package/src/lib/deck-dexie.ts +0 -190
- package/src/lib/deck-edit.ts +0 -196
- package/src/lib/deck-media-assets.ts +0 -57
- package/src/lib/deck-media-refs.ts +0 -67
- package/src/lib/deck-tool.ts +0 -95
- package/src/lib/document-input.ts +0 -57
- package/src/schemas/deck-meta.ts +0 -88
- package/src/schemas/deck.ts +0 -478
- package/dist/{chunk-DJABADOO.js → chunk-AJ2QOAZI.js} +3 -3
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { DECK_SCHEMA_VERSION, DeckSchema, type Deck } from "../schemas/deck";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* A new deck's seed. Three slides using the two text-only Layouts, so a New
|
|
5
|
-
* Deck is publishable and presentable before the author has said anything —
|
|
6
|
-
* and so the starter never cites media that a given site may not have (a
|
|
7
|
-
* reference the manifest cannot resolve renders as a gap, which is a poor
|
|
8
|
-
* first impression of the runtime).
|
|
9
|
-
*
|
|
10
|
-
* Parsed rather than cast: this is the one deck the product itself authors, so
|
|
11
|
-
* a schema change that breaks it should break a test, not a client's first
|
|
12
|
-
* deck.
|
|
13
|
-
*/
|
|
14
|
-
export function starterDeck(id: string, title: string): Deck {
|
|
15
|
-
return DeckSchema.parse({
|
|
16
|
-
schemaVersion: DECK_SCHEMA_VERSION,
|
|
17
|
-
id,
|
|
18
|
-
title,
|
|
19
|
-
slides: [
|
|
20
|
-
{
|
|
21
|
-
id: "cover",
|
|
22
|
-
title: "Cover",
|
|
23
|
-
layout: {
|
|
24
|
-
type: "centeredText",
|
|
25
|
-
background: "brand",
|
|
26
|
-
content: {
|
|
27
|
-
type: "richText",
|
|
28
|
-
kicker: "New deck",
|
|
29
|
-
heading: title,
|
|
30
|
-
subheading: "Rename this slide's heading, or ask the assistant to rewrite the whole deck.",
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
id: "context",
|
|
36
|
-
title: "Context",
|
|
37
|
-
layout: {
|
|
38
|
-
type: "centeredText",
|
|
39
|
-
content: {
|
|
40
|
-
type: "richText",
|
|
41
|
-
kicker: "Context",
|
|
42
|
-
heading: "What this deck is for",
|
|
43
|
-
paragraphs: [
|
|
44
|
-
"Every slide uses one of the portal's prebuilt Layouts, so it inherits the brand's type, color and spacing automatically.",
|
|
45
|
-
],
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
id: "next",
|
|
51
|
-
title: "Next",
|
|
52
|
-
layout: {
|
|
53
|
-
type: "centeredText",
|
|
54
|
-
content: {
|
|
55
|
-
type: "richText",
|
|
56
|
-
kicker: "Next",
|
|
57
|
-
heading: "Three things to try",
|
|
58
|
-
bullets: [
|
|
59
|
-
"Click a thumbnail below to move through the deck",
|
|
60
|
-
"Publish when you want the deck kept for you",
|
|
61
|
-
"Everything you change stays on this device until you do",
|
|
62
|
-
],
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
],
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* A deck id for a new deck: a slug of the title plus a short random suffix.
|
|
72
|
-
*
|
|
73
|
-
* Random rather than sequential because the id is a directory name on a shared
|
|
74
|
-
* branch that this browser cannot enumerate (a viewer only ever sees their own
|
|
75
|
-
* decks), so "next free number" is not something the builder can know. The
|
|
76
|
-
* charset is `DeckIdSchema`'s.
|
|
77
|
-
*/
|
|
78
|
-
export function newDeckId(title: string): string {
|
|
79
|
-
const slug = title
|
|
80
|
-
.toLowerCase()
|
|
81
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
82
|
-
.replace(/^-+|-+$/g, "")
|
|
83
|
-
.slice(0, 40);
|
|
84
|
-
const suffix = Math.random().toString(36).slice(2, 8).replace(/[^a-z0-9]/g, "0").padEnd(6, "0");
|
|
85
|
-
return slug ? `${slug}-${suffix}` : `deck-${suffix}`;
|
|
86
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { Deck } from "../schemas/deck";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* One row of the builder's deck grid: whatever this browser has staged, joined
|
|
5
|
-
* to whatever the decks branch holds.
|
|
6
|
-
*
|
|
7
|
-
* The join is by deck id and both sides are optional, because the three states
|
|
8
|
-
* are all real: a draft never published (published side absent), a deck
|
|
9
|
-
* published from another device (draft side absent), and the normal case where
|
|
10
|
-
* both exist and may differ.
|
|
11
|
-
*/
|
|
12
|
-
export interface DeckGridEntry {
|
|
13
|
-
id: string;
|
|
14
|
-
title: string;
|
|
15
|
-
slideCount: number;
|
|
16
|
-
/** ISO timestamp behind the card's "… ago" line. */
|
|
17
|
-
updatedAt: string;
|
|
18
|
-
/** Present when this browser has the deck staged. */
|
|
19
|
-
draft: { deck: Deck; baseVersion: string | null } | null;
|
|
20
|
-
/** Present when the decks branch holds it. */
|
|
21
|
-
published: { version: string } | null;
|
|
22
|
-
/** The draft differs from what was published (or was never published). */
|
|
23
|
-
unpublishedChanges: boolean;
|
|
24
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from "react";
|
|
2
|
-
import { computeReorderTarget } from "../lib/reorder";
|
|
3
|
-
|
|
4
|
-
interface Options {
|
|
5
|
-
index: number;
|
|
6
|
-
onReorder: (fromIndex: number, toIndex: number) => void;
|
|
7
|
-
/** False while a chat turn streams — the strip goes inert, like the stage. */
|
|
8
|
-
enabled: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const DRAG_TYPE = "deck-filmstrip-slide";
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Drag-to-reorder for one filmstrip thumbnail.
|
|
15
|
-
*
|
|
16
|
-
* A sibling of `useSortableRow` rather than an option on it: the filmstrip is
|
|
17
|
-
* HORIZONTAL, so it reads left/right edges where every existing consumer reads
|
|
18
|
-
* top/bottom, and widening that hook's `closestEdge` type would change the two
|
|
19
|
-
* shipped editor modals that render `DropEdgeIndicator` from it. The Deck
|
|
20
|
-
* Builder is a sealed subtree; a second small hook here costs less than a type
|
|
21
|
-
* change under two surfaces this ticket is not touching.
|
|
22
|
-
*
|
|
23
|
-
* The pointer-math is NOT duplicated: `computeReorderTarget` is written in
|
|
24
|
-
* before/after terms, so left maps to "top" and right to "bottom" and the
|
|
25
|
-
* unit-tested decrement stays the one implementation. The whole atlaskit
|
|
26
|
-
* adapter is imported lazily, as it is everywhere else — jsdom cannot run it,
|
|
27
|
-
* which is why `moveDeckSlide` and the move affordances carry the coverage.
|
|
28
|
-
*/
|
|
29
|
-
export function useSortableSlide<E extends HTMLElement>({ index, onReorder, enabled }: Options) {
|
|
30
|
-
const ref = useRef<E>(null);
|
|
31
|
-
// Held in a ref so the effect does not depend on it. `onReorder` is rebuilt
|
|
32
|
-
// whenever the draft refreshes — i.e. after every single edit — and depending
|
|
33
|
-
// on it tore down and re-registered the drag adapter on every thumbnail in
|
|
34
|
-
// the strip each time.
|
|
35
|
-
const onReorderRef = useRef(onReorder);
|
|
36
|
-
onReorderRef.current = onReorder;
|
|
37
|
-
const [isDragging, setIsDragging] = useState(false);
|
|
38
|
-
const [closestEdge, setClosestEdge] = useState<"left" | "right" | null>(null);
|
|
39
|
-
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
if (!enabled) return;
|
|
42
|
-
const element = ref.current;
|
|
43
|
-
if (!element) return;
|
|
44
|
-
|
|
45
|
-
let cleanup: (() => void) | undefined;
|
|
46
|
-
let cancelled = false;
|
|
47
|
-
|
|
48
|
-
void Promise.all([
|
|
49
|
-
import("@atlaskit/pragmatic-drag-and-drop/element/adapter"),
|
|
50
|
-
import("@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge"),
|
|
51
|
-
]).then(([{ draggable, dropTargetForElements }, { attachClosestEdge, extractClosestEdge }]) => {
|
|
52
|
-
if (cancelled) return;
|
|
53
|
-
|
|
54
|
-
const edgeOf = (data: Record<string | symbol, unknown>): "left" | "right" | null => {
|
|
55
|
-
const edge = extractClosestEdge(data);
|
|
56
|
-
return edge === "left" || edge === "right" ? edge : null;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const cleanupDraggable = draggable({
|
|
60
|
-
element,
|
|
61
|
-
getInitialData: () => ({ dragType: DRAG_TYPE, index }),
|
|
62
|
-
onGenerateDragPreview: () => {
|
|
63
|
-
element.style.opacity = "0.4";
|
|
64
|
-
requestAnimationFrame(() => {
|
|
65
|
-
element.style.opacity = "";
|
|
66
|
-
});
|
|
67
|
-
},
|
|
68
|
-
onDragStart: () => setIsDragging(true),
|
|
69
|
-
onDrop: () => setIsDragging(false),
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
const cleanupDropTarget = dropTargetForElements({
|
|
73
|
-
element,
|
|
74
|
-
canDrop: ({ source }) => source.data.dragType === DRAG_TYPE,
|
|
75
|
-
getData: ({ input, element: target }) =>
|
|
76
|
-
attachClosestEdge({ index }, { input, element: target, allowedEdges: ["left", "right"] }),
|
|
77
|
-
onDragEnter: ({ self }) => setClosestEdge(edgeOf(self.data)),
|
|
78
|
-
onDrag: ({ self }) => setClosestEdge(edgeOf(self.data)),
|
|
79
|
-
onDragLeave: () => setClosestEdge(null),
|
|
80
|
-
onDrop: ({ source, self }) => {
|
|
81
|
-
setClosestEdge(null);
|
|
82
|
-
const fromIndex = source.data.index as number;
|
|
83
|
-
const edge = edgeOf(self.data);
|
|
84
|
-
const toIndex = computeReorderTarget(fromIndex, index, edge === "right" ? "bottom" : edge === "left" ? "top" : null);
|
|
85
|
-
if (toIndex !== null) onReorderRef.current(fromIndex, toIndex);
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
cleanup = () => {
|
|
90
|
-
cleanupDraggable();
|
|
91
|
-
cleanupDropTarget();
|
|
92
|
-
};
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
return () => {
|
|
96
|
-
cancelled = true;
|
|
97
|
-
cleanup?.();
|
|
98
|
-
};
|
|
99
|
-
}, [index, enabled]);
|
|
100
|
-
|
|
101
|
-
return { ref, isDragging, closestEdge };
|
|
102
|
-
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import type { DocCover } from "../engine/settings";
|
|
2
|
-
import type { DocImage, DocMeta } from "../engine/types";
|
|
3
|
-
import { DocPage } from "./DocPage";
|
|
4
|
-
import type { DocHost } from "./DocSectionPage";
|
|
5
|
-
import { Wordmark } from "./Wordmark";
|
|
6
|
-
|
|
7
|
-
// Absorbs the harness's TitleCardLayout inline instead of wrapping it: the
|
|
8
|
-
// portal-native cover has no client-logo slot (the harness only ever passed
|
|
9
|
-
// null) and cover text is edited in the inspector, never on-canvas, so there
|
|
10
|
-
// is no EditableText/deck-context machinery here — this always renders the
|
|
11
|
-
// harness's non-editable, null-logo branch.
|
|
12
|
-
export function DocCoverPage({
|
|
13
|
-
cover,
|
|
14
|
-
meta,
|
|
15
|
-
image,
|
|
16
|
-
host,
|
|
17
|
-
}: {
|
|
18
|
-
cover: DocCover;
|
|
19
|
-
meta: DocMeta;
|
|
20
|
-
/** Resolved from source.images[cover.imageId] by the caller; null/undefined = solid cover. */
|
|
21
|
-
image?: DocImage | null;
|
|
22
|
-
host?: DocHost;
|
|
23
|
-
}) {
|
|
24
|
-
return (
|
|
25
|
-
<DocPage meta={meta} bleed chrome={false}>
|
|
26
|
-
<div className="layout-titlecard" style={{ background: cover.bgColor }}>
|
|
27
|
-
<header className="layout-titlecard__masthead">
|
|
28
|
-
<Wordmark className="layout-titlecard__wordmark" />
|
|
29
|
-
<span className="layout-titlecard__rule" />
|
|
30
|
-
</header>
|
|
31
|
-
{image && host ? (
|
|
32
|
-
<div className="layout-titlecard__image">
|
|
33
|
-
<img src={host.imageSrc(image, host.variant)} alt={image.alt} />
|
|
34
|
-
</div>
|
|
35
|
-
) : null}
|
|
36
|
-
<div className="layout-titlecard__lockup" style={{ color: cover.accentColor }}>
|
|
37
|
-
<h1 className="layout-titlecard__title">{cover.title}</h1>
|
|
38
|
-
<span className="layout-titlecard__titlerule" style={{ background: cover.accentColor }} />
|
|
39
|
-
<p className="layout-titlecard__label">{cover.label}</p>
|
|
40
|
-
<p className="layout-titlecard__date">{cover.date || meta.dateLabel}</p>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
</DocPage>
|
|
44
|
-
);
|
|
45
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
2
|
-
import type { DocMeta } from "../engine/types";
|
|
3
|
-
import { Wordmark } from "./Wordmark";
|
|
4
|
-
|
|
5
|
-
// Re-exported so index.ts's `export { DocPage, type DocMeta } from
|
|
6
|
-
// "./components/DocPage"` has a local name — the single definition still
|
|
7
|
-
// lives in engine/types.ts.
|
|
8
|
-
export type { DocMeta };
|
|
9
|
-
|
|
10
|
-
export function DocPage({
|
|
11
|
-
meta,
|
|
12
|
-
bleed = false,
|
|
13
|
-
chrome = true,
|
|
14
|
-
children,
|
|
15
|
-
}: {
|
|
16
|
-
meta: DocMeta;
|
|
17
|
-
bleed?: boolean;
|
|
18
|
-
chrome?: boolean;
|
|
19
|
-
children: ReactNode;
|
|
20
|
-
}) {
|
|
21
|
-
const crumb = [meta.client, meta.project, meta.dateLabel]
|
|
22
|
-
.map((s) => s.trim().toUpperCase())
|
|
23
|
-
.filter(Boolean)
|
|
24
|
-
.join(" :: ");
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<section className="deck-page">
|
|
28
|
-
{bleed ? <div className="deck-bleed">{children}</div> : null}
|
|
29
|
-
{chrome ? (
|
|
30
|
-
<header className="deck-chrome">
|
|
31
|
-
<span className="deck-crumb">{crumb}</span>
|
|
32
|
-
<div className="deck-wordmark-clip">
|
|
33
|
-
<Wordmark className="deck-wordmark" />
|
|
34
|
-
</div>
|
|
35
|
-
</header>
|
|
36
|
-
) : null}
|
|
37
|
-
{!bleed ? <div className="deck-body">{children}</div> : null}
|
|
38
|
-
</section>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef, useState, type ReactNode } from "react";
|
|
2
|
-
import type { DerivedPage } from "../engine/paginate";
|
|
3
|
-
import type { DocCover } from "../engine/settings";
|
|
4
|
-
import type { DocImage, DocMeta } from "../engine/types";
|
|
5
|
-
import { DocCoverPage } from "./DocCoverPage";
|
|
6
|
-
import { DocSectionPage, type DocHost } from "./DocSectionPage";
|
|
7
|
-
|
|
8
|
-
// 17in @96dpi = 1632px wide; thumbnails render the real page scaled to fill the
|
|
9
|
-
// rail width at a locked 17:11 aspect ratio (mirrors the harness PageRail).
|
|
10
|
-
const PAGE_W = 1632;
|
|
11
|
-
const PAGE_H = 1056;
|
|
12
|
-
|
|
13
|
-
export type RailEntry =
|
|
14
|
-
| { kind: "cover" }
|
|
15
|
-
| { kind: "page"; page: DerivedPage }
|
|
16
|
-
| { kind: "ghost"; chunkId: string; title: string; reason: "excluded" | "empty"; depth?: 1 };
|
|
17
|
-
|
|
18
|
-
export function entryId(entry: RailEntry): string {
|
|
19
|
-
if (entry.kind === "cover") return "cover";
|
|
20
|
-
if (entry.kind === "page") return entry.page.id;
|
|
21
|
-
return `ghost:${entry.chunkId}`;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Rail nesting: sub-pages sit one step in per depth, continuations one more.
|
|
25
|
-
const INDENT = ["", "pl-4", "pl-8"];
|
|
26
|
-
const indentClass = (level: number) => INDENT[Math.min(level, INDENT.length - 1)] || undefined;
|
|
27
|
-
|
|
28
|
-
function ThumbButton({
|
|
29
|
-
id,
|
|
30
|
-
selectedId,
|
|
31
|
-
scale,
|
|
32
|
-
label,
|
|
33
|
-
indentLevel,
|
|
34
|
-
onSelect,
|
|
35
|
-
children,
|
|
36
|
-
}: {
|
|
37
|
-
id: string;
|
|
38
|
-
selectedId: string;
|
|
39
|
-
scale: number;
|
|
40
|
-
label: string;
|
|
41
|
-
indentLevel?: number;
|
|
42
|
-
onSelect: (id: string) => void;
|
|
43
|
-
children: ReactNode;
|
|
44
|
-
}) {
|
|
45
|
-
// Each thumbnail is a REAL page subtree — every image at print resolution,
|
|
46
|
-
// scaled to ~11%. Mounting all of them cost a 40-page document 40 subtrees and
|
|
47
|
-
// a fresh request for every image before the rail was even scrolled. The
|
|
48
|
-
// aspect-ratio box below holds its size whether or not the page is mounted, so
|
|
49
|
-
// deferring costs nothing in layout. Sticky once shown: re-unmounting would
|
|
50
|
-
// re-request the images on every scroll back.
|
|
51
|
-
const liRef = useRef<HTMLLIElement>(null);
|
|
52
|
-
const [shown, setShown] = useState(typeof IntersectionObserver === "undefined");
|
|
53
|
-
useEffect(() => {
|
|
54
|
-
if (shown) return;
|
|
55
|
-
const el = liRef.current;
|
|
56
|
-
if (!el) return;
|
|
57
|
-
const obs = new IntersectionObserver(
|
|
58
|
-
(entries) => {
|
|
59
|
-
if (entries.some((e) => e.isIntersecting)) {
|
|
60
|
-
setShown(true);
|
|
61
|
-
obs.disconnect();
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
{ rootMargin: "200% 0px" },
|
|
65
|
-
);
|
|
66
|
-
obs.observe(el);
|
|
67
|
-
return () => obs.disconnect();
|
|
68
|
-
}, [shown]);
|
|
69
|
-
|
|
70
|
-
return (
|
|
71
|
-
<li ref={liRef} className={indentClass(indentLevel ?? 0)}>
|
|
72
|
-
<button
|
|
73
|
-
type="button"
|
|
74
|
-
onClick={() => onSelect(id)}
|
|
75
|
-
className={`relative block w-full rounded border-2 bg-white ${
|
|
76
|
-
id === selectedId ? "border-teal-600" : "border-neutral-200 hover:border-neutral-300"
|
|
77
|
-
}`}
|
|
78
|
-
>
|
|
79
|
-
<span className="pointer-events-none block w-full overflow-hidden aspect-[17/11]">
|
|
80
|
-
<span
|
|
81
|
-
className="block origin-top-left"
|
|
82
|
-
style={{ transform: `scale(${scale})`, width: PAGE_W, height: PAGE_H }}
|
|
83
|
-
>
|
|
84
|
-
{shown ? children : null}
|
|
85
|
-
</span>
|
|
86
|
-
</span>
|
|
87
|
-
<span className="absolute bottom-0.5 left-1 max-w-[90%] truncate text-[10px] text-neutral-400">
|
|
88
|
-
{label}
|
|
89
|
-
</span>
|
|
90
|
-
</button>
|
|
91
|
-
</li>
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function DocRail({
|
|
96
|
-
entries,
|
|
97
|
-
selectedId,
|
|
98
|
-
meta,
|
|
99
|
-
cover,
|
|
100
|
-
coverImage,
|
|
101
|
-
host,
|
|
102
|
-
onSelect,
|
|
103
|
-
onCollapse,
|
|
104
|
-
}: {
|
|
105
|
-
entries: RailEntry[];
|
|
106
|
-
selectedId: string;
|
|
107
|
-
meta: DocMeta;
|
|
108
|
-
cover: DocCover;
|
|
109
|
-
coverImage?: DocImage | null;
|
|
110
|
-
host: DocHost;
|
|
111
|
-
onSelect: (id: string) => void;
|
|
112
|
-
onCollapse: () => void;
|
|
113
|
-
}) {
|
|
114
|
-
const listRef = useRef<HTMLOListElement>(null);
|
|
115
|
-
const [scale, setScale] = useState(0.11);
|
|
116
|
-
useEffect(() => {
|
|
117
|
-
const el = listRef.current;
|
|
118
|
-
if (!el || typeof ResizeObserver === "undefined") return;
|
|
119
|
-
const obs = new ResizeObserver(() => {
|
|
120
|
-
// content width = list width minus the button's 2px border on each side
|
|
121
|
-
const w = el.clientWidth - 4;
|
|
122
|
-
if (w > 0) setScale(w / PAGE_W);
|
|
123
|
-
});
|
|
124
|
-
obs.observe(el);
|
|
125
|
-
return () => obs.disconnect();
|
|
126
|
-
}, []);
|
|
127
|
-
|
|
128
|
-
return (
|
|
129
|
-
<nav
|
|
130
|
-
data-testid="doc-rail"
|
|
131
|
-
className="flex w-52 shrink-0 flex-col overflow-y-auto border-r border-neutral-200 bg-neutral-50 p-3"
|
|
132
|
-
>
|
|
133
|
-
<button
|
|
134
|
-
type="button"
|
|
135
|
-
aria-label="Collapse page rail"
|
|
136
|
-
onClick={onCollapse}
|
|
137
|
-
className="mb-2 flex w-full items-center justify-between rounded px-2 py-1.5 text-sm font-medium text-neutral-600 hover:bg-neutral-100 hover:text-neutral-900"
|
|
138
|
-
>
|
|
139
|
-
<span>Pages</span>
|
|
140
|
-
<span aria-hidden="true" className="text-lg leading-none">‹</span>
|
|
141
|
-
</button>
|
|
142
|
-
<ol ref={listRef} className="flex flex-col gap-2">
|
|
143
|
-
{entries.map((entry) => {
|
|
144
|
-
const id = entryId(entry);
|
|
145
|
-
if (entry.kind === "cover") {
|
|
146
|
-
return (
|
|
147
|
-
<ThumbButton key={id} id={id} selectedId={selectedId} scale={scale} label="Cover" onSelect={onSelect}>
|
|
148
|
-
<DocCoverPage cover={cover} meta={meta} image={coverImage} host={host} />
|
|
149
|
-
</ThumbButton>
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
if (entry.kind === "page") {
|
|
153
|
-
return (
|
|
154
|
-
<ThumbButton
|
|
155
|
-
key={id}
|
|
156
|
-
id={id}
|
|
157
|
-
selectedId={selectedId}
|
|
158
|
-
scale={scale}
|
|
159
|
-
label={entry.page.title}
|
|
160
|
-
indentLevel={(entry.page.depth ?? 0) + (entry.page.cont ? 1 : 0)}
|
|
161
|
-
onSelect={onSelect}
|
|
162
|
-
>
|
|
163
|
-
<DocSectionPage page={entry.page} meta={meta} host={host} />
|
|
164
|
-
</ThumbButton>
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
return (
|
|
168
|
-
<li key={id} className={indentClass(entry.depth ?? 0)}>
|
|
169
|
-
<button
|
|
170
|
-
type="button"
|
|
171
|
-
onClick={() => onSelect(id)}
|
|
172
|
-
className={`flex w-full items-center justify-between gap-2 rounded border px-2 py-2 text-left text-xs ${
|
|
173
|
-
id === selectedId
|
|
174
|
-
? "border-teal-600 bg-white"
|
|
175
|
-
: "border-dashed border-neutral-300 bg-neutral-50 hover:border-neutral-400"
|
|
176
|
-
}`}
|
|
177
|
-
>
|
|
178
|
-
<span className="truncate text-neutral-400">{entry.title}</span>
|
|
179
|
-
<span className="shrink-0 rounded bg-neutral-200 px-1 text-[10px] uppercase tracking-wide text-neutral-500">
|
|
180
|
-
{entry.reason}
|
|
181
|
-
</span>
|
|
182
|
-
</button>
|
|
183
|
-
</li>
|
|
184
|
-
);
|
|
185
|
-
})}
|
|
186
|
-
</ol>
|
|
187
|
-
</nav>
|
|
188
|
-
);
|
|
189
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import type { DocImage, DocMeta } from "../engine/types";
|
|
2
|
-
import type { DerivedPage } from "../engine/paginate";
|
|
3
|
-
import { DocPage } from "./DocPage";
|
|
4
|
-
|
|
5
|
-
export interface DocHost {
|
|
6
|
-
imageSrc: (image: DocImage, variant: "preview" | "print") => string;
|
|
7
|
-
variant: "preview" | "print";
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const inch = (v: number) => `${v}in`;
|
|
11
|
-
|
|
12
|
-
export function DocSectionPage({ page, meta, host }: { page: DerivedPage; meta: DocMeta; host: DocHost }) {
|
|
13
|
-
return (
|
|
14
|
-
<DocPage meta={meta}>
|
|
15
|
-
<h2 className="pdoc-title">
|
|
16
|
-
{page.kicker ? <span className="pdoc-kicker" aria-hidden="true">{page.kicker}</span> : null}
|
|
17
|
-
{page.title}
|
|
18
|
-
</h2>
|
|
19
|
-
{page.textRegion ? (
|
|
20
|
-
<div
|
|
21
|
-
className="pdoc-text"
|
|
22
|
-
style={{ left: inch(page.textRegion.xIn), top: inch(page.textRegion.yIn), width: inch(page.textRegion.wIn) }}
|
|
23
|
-
>
|
|
24
|
-
{page.textBlocks.map((b, i) => {
|
|
25
|
-
const spacing = i > 0 ? { marginTop: inch(0.15) } : undefined;
|
|
26
|
-
if (b.kind === "heading") {
|
|
27
|
-
return b.level === 2 ? (
|
|
28
|
-
<h3 key={i} className="pdoc-h2" style={spacing}>{b.text}</h3>
|
|
29
|
-
) : (
|
|
30
|
-
<h4 key={i} className="pdoc-h3" style={spacing}>{b.text}</h4>
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
return <div key={i} className="pdoc-prose" style={spacing} dangerouslySetInnerHTML={{ __html: b.html }} />;
|
|
34
|
-
})}
|
|
35
|
-
</div>
|
|
36
|
-
) : null}
|
|
37
|
-
{page.imageBoxes.map((box, i) => (
|
|
38
|
-
<div
|
|
39
|
-
key={`${box.image.imageId}-${i}`}
|
|
40
|
-
className="pdoc-img"
|
|
41
|
-
style={{ left: inch(box.xIn), top: inch(box.yIn), width: inch(box.wIn), height: inch(box.hIn) }}
|
|
42
|
-
>
|
|
43
|
-
<img src={host.imageSrc(box.image, host.variant)} alt={box.image.alt} style={{ objectFit: box.fit }} />
|
|
44
|
-
</div>
|
|
45
|
-
))}
|
|
46
|
-
</DocPage>
|
|
47
|
-
);
|
|
48
|
-
}
|