@morphemecms/cms 0.5.0 → 0.7.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/CHANGELOG.md +16 -0
- package/astro.mjs +3 -0
- package/bin/morpheme.mjs +3 -1
- package/defaults/blockTexts.ts +2 -2
- package/defaults/collections.ts +10 -0
- package/defaults/editor.ts +57 -53
- package/defaults/privacy.ts +4 -1
- package/defaults/rules.ts +36 -38
- package/defaults/standardPages.ts +0 -3
- package/defaults/texts.ts +11 -2
- package/demo/src/content/pages/about.md +2 -0
- package/icons/lucide/browser-fill.svg +1 -0
- package/icons/lucide/buildings-fill.svg +1 -0
- package/icons/lucide/certificate-fill.svg +1 -0
- package/icons/lucide/chart-line-up-fill.svg +1 -0
- package/icons/lucide/chats-fill.svg +1 -0
- package/icons/lucide/envelope-simple-fill.svg +1 -0
- package/icons/lucide/file-text-fill.svg +1 -0
- package/icons/lucide/folder-simple-fill.svg +1 -0
- package/icons/lucide/gauge.svg +1 -1
- package/icons/lucide/gear-six.svg +1 -0
- package/icons/lucide/globe-simple.svg +1 -0
- package/icons/lucide/heart-fill.svg +1 -0
- package/icons/lucide/layout.svg +1 -0
- package/icons/lucide/list-checks-fill.svg +1 -0
- package/icons/lucide/magnifying-glass-fill.svg +1 -0
- package/icons/lucide/megaphone-fill.svg +1 -0
- package/icons/lucide/newspaper-fill.svg +1 -0
- package/icons/lucide/paint-brush-broad-fill.svg +1 -0
- package/icons/lucide/puzzle-piece-fill.svg +1 -0
- package/icons/lucide/shield-check-fill.svg +1 -0
- package/icons/lucide/sidebar-simple.svg +1 -1
- package/icons/lucide/sidebar.svg +1 -0
- package/icons/lucide/square-half-bottom-fill.svg +1 -0
- package/icons/lucide/square-half.svg +1 -0
- package/icons/lucide/squares-four-fill.svg +1 -0
- package/icons/lucide/storefront-fill.svg +1 -0
- package/icons/lucide/tag-fill.svg +1 -0
- package/icons/lucide/toggle-right-fill.svg +1 -0
- package/icons/lucide/trash-fill.svg +1 -0
- package/icons/lucide/user-fill.svg +1 -0
- package/icons/lucide/wrench-fill.svg +1 -0
- package/icons/material-symbols/browser-fill.svg +1 -0
- package/icons/material-symbols/buildings-fill.svg +1 -0
- package/icons/material-symbols/certificate-fill.svg +1 -0
- package/icons/material-symbols/chart-line-up-fill.svg +1 -0
- package/icons/material-symbols/chats-fill.svg +1 -0
- package/icons/material-symbols/envelope-simple-fill.svg +1 -0
- package/icons/material-symbols/file-text-fill.svg +1 -0
- package/icons/material-symbols/folder-simple-fill.svg +1 -0
- package/icons/material-symbols/gauge.svg +1 -1
- package/icons/material-symbols/gear-six.svg +1 -0
- package/icons/material-symbols/globe-simple.svg +1 -0
- package/icons/material-symbols/heart-fill.svg +1 -0
- package/icons/material-symbols/layout.svg +1 -0
- package/icons/material-symbols/list-checks-fill.svg +1 -0
- package/icons/material-symbols/magnifying-glass-fill.svg +1 -0
- package/icons/material-symbols/megaphone-fill.svg +1 -0
- package/icons/material-symbols/newspaper-fill.svg +1 -0
- package/icons/material-symbols/paint-brush-broad-fill.svg +1 -0
- package/icons/material-symbols/puzzle-piece-fill.svg +1 -0
- package/icons/material-symbols/shield-check-fill.svg +1 -0
- package/icons/material-symbols/sidebar-simple.svg +1 -1
- package/icons/material-symbols/sidebar.svg +1 -0
- package/icons/material-symbols/square-half-bottom-fill.svg +1 -0
- package/icons/material-symbols/square-half.svg +1 -0
- package/icons/material-symbols/squares-four-fill.svg +1 -0
- package/icons/material-symbols/storefront-fill.svg +1 -0
- package/icons/material-symbols/tag-fill.svg +1 -0
- package/icons/material-symbols/toggle-right-fill.svg +1 -0
- package/icons/material-symbols/trash-fill.svg +1 -0
- package/icons/material-symbols/user-fill.svg +1 -0
- package/icons/material-symbols/wrench-fill.svg +1 -0
- package/icons/phosphor/browser-fill.svg +1 -0
- package/icons/phosphor/buildings-fill.svg +1 -0
- package/icons/phosphor/certificate-fill.svg +1 -0
- package/icons/phosphor/chart-line-up-fill.svg +1 -0
- package/icons/phosphor/chats-fill.svg +1 -0
- package/icons/phosphor/envelope-simple-fill.svg +1 -0
- package/icons/phosphor/file-text-fill.svg +1 -0
- package/icons/phosphor/folder-simple-fill.svg +1 -0
- package/icons/phosphor/gear-six.svg +1 -0
- package/icons/phosphor/globe-simple.svg +1 -0
- package/icons/phosphor/heart-fill.svg +1 -0
- package/icons/phosphor/layout.svg +1 -0
- package/icons/phosphor/list-checks-fill.svg +1 -0
- package/icons/phosphor/magnifying-glass-fill.svg +1 -0
- package/icons/phosphor/megaphone-fill.svg +1 -0
- package/icons/phosphor/newspaper-fill.svg +1 -0
- package/icons/phosphor/paint-brush-broad-fill.svg +1 -0
- package/icons/phosphor/puzzle-piece-fill.svg +1 -0
- package/icons/phosphor/shield-check-fill.svg +1 -0
- package/icons/phosphor/sidebar.svg +1 -0
- package/icons/phosphor/square-half-bottom-fill.svg +1 -0
- package/icons/phosphor/square-half.svg +1 -0
- package/icons/phosphor/squares-four-fill.svg +1 -0
- package/icons/phosphor/storefront-fill.svg +1 -0
- package/icons/phosphor/tag-fill.svg +1 -0
- package/icons/phosphor/toggle-right-fill.svg +1 -0
- package/icons/phosphor/trash-fill.svg +1 -0
- package/icons/phosphor/user-fill.svg +1 -0
- package/icons/phosphor/wrench-fill.svg +1 -0
- package/lib/breakpoints.d.mts +13 -0
- package/lib/breakpoints.mjs +20 -0
- package/lib/breakpoints.test.mjs +39 -0
- package/lib/build-lane.d.mts +15 -0
- package/lib/build-lane.mjs +23 -0
- package/lib/build-lane.test.mjs +74 -0
- package/lib/build-settings.d.mts +16 -0
- package/lib/build-settings.mjs +39 -0
- package/lib/build-settings.test.mjs +41 -0
- package/lib/contracts.d.mts +3 -2
- package/lib/contracts.mjs +3 -2
- package/lib/drawer-swipe.d.mts +10 -0
- package/lib/drawer-swipe.mjs +17 -0
- package/lib/drawer-swipe.test.mjs +33 -0
- package/lib/drawerKeys.d.mts +9 -0
- package/lib/drawerKeys.mjs +26 -0
- package/lib/drawerKeys.test.mjs +21 -0
- package/lib/eyebrow.d.mts +6 -0
- package/lib/eyebrow.mjs +24 -0
- package/lib/eyebrow.test.mjs +20 -0
- package/lib/front-sections.test.mjs +82 -0
- package/lib/heading-levels.d.mts +7 -0
- package/lib/heading-levels.mjs +16 -0
- package/lib/heading-levels.test.mjs +37 -0
- package/lib/inline.ts +3 -2
- package/lib/markdown.ts +2 -0
- package/lib/materialIcon.d.mts +21 -0
- package/lib/materialIcon.mjs +29 -0
- package/lib/materialIcon.test.mjs +45 -0
- package/lib/menu-layout.d.mts +11 -0
- package/lib/menu-layout.mjs +14 -0
- package/lib/menu-layout.test.mjs +23 -0
- package/lib/menu.d.mts +59 -0
- package/lib/menu.mjs +129 -0
- package/lib/menu.test.mjs +291 -0
- package/lib/rehype-emphasize.d.mts +4 -0
- package/lib/rehype-emphasize.mjs +20 -0
- package/lib/rehype-emphasize.test.mjs +39 -0
- package/lib/section-in-view.d.mts +8 -0
- package/lib/section-in-view.mjs +25 -0
- package/lib/section-in-view.test.mjs +35 -0
- package/lib/section-marker.d.mts +5 -0
- package/lib/section-marker.mjs +18 -0
- package/lib/section-marker.test.mjs +22 -0
- package/lib/section-ranges.d.mts +12 -0
- package/lib/section-ranges.mjs +43 -0
- package/lib/section-ranges.test.mjs +61 -0
- package/lib/sheet-stops.d.mts +68 -0
- package/lib/sheet-stops.mjs +198 -0
- package/lib/sheet-stops.test.mjs +155 -0
- package/lib/still-timer.d.mts +5 -0
- package/lib/still-timer.mjs +17 -0
- package/lib/still-timer.test.mjs +42 -0
- package/lib/text-sections.d.mts +10 -0
- package/lib/text-sections.mjs +45 -0
- package/lib/text-sections.test.mjs +78 -0
- package/lib/video.mjs +2 -1
- package/lib/without-h1.d.mts +1 -0
- package/lib/without-h1.mjs +7 -0
- package/lib/without-h1.test.mjs +26 -0
- package/lib/word-at.d.mts +6 -0
- package/lib/word-at.mjs +20 -0
- package/lib/word-at.test.mjs +19 -0
- package/package.json +3 -1
- package/pages/editor/version.json.js +22 -2
- package/scripts/already-checked.mjs +11 -0
- package/scripts/already-checked.test.mjs +18 -0
- package/scripts/build-settings-problems.mjs +27 -0
- package/scripts/build-settings-problems.test.mjs +35 -0
- package/scripts/check-content.mjs +42 -4
- package/scripts/check-worker.mjs +96 -4
- package/scripts/ci-build.mjs +85 -0
- package/scripts/ci-build.test.mjs +137 -0
- package/scripts/editor-roundtrip.mjs +62 -3
- package/scripts/editor-screens.mjs +21 -7
- package/scripts/git-env.mjs +6 -0
- package/scripts/step-command.mjs +34 -0
- package/scripts/step-command.test.mjs +50 -0
- package/scripts/text-problems.mjs +83 -0
- package/scripts/text-problems.test.mjs +74 -0
- package/scripts/worker-modules.mjs +11 -1
- package/site/breakpoints.d.mts +6 -0
- package/site/breakpoints.mjs +52 -0
- package/site/breakpoints.test.mjs +36 -0
- package/site/casing.d.mts +3 -0
- package/site/casing.mjs +26 -0
- package/site/content.config.ts +2 -0
- package/site/content.ts +2 -4
- package/site/defineSite.d.mts +1 -0
- package/site/defineSite.mjs +3 -0
- package/site/home.ts +3 -2
- package/site/icons.d.mts +116 -4
- package/site/icons.mjs +62 -6
- package/site/images.mjs +1 -1
- package/site/live-diff.d.mts +22 -0
- package/site/live-diff.mjs +98 -0
- package/site/live-diff.test.mjs +235 -0
- package/site/materialSymbols.d.mts +5 -0
- package/site/materialSymbols.mjs +19 -0
- package/site/sections.ts +1 -1
- package/site/theme.d.mts +1 -1
- package/site/theme.mjs +7 -1
- package/src/ActionParts.tsx +1 -1
- package/src/Advanced.tsx +101 -20
- package/src/App.tsx +47 -121
- package/src/AppList.tsx +1 -1
- package/src/BlockParts.tsx +3 -3
- package/src/Colors.tsx +1 -1
- package/src/CommunicationParts.tsx +1 -1
- package/src/ContainmentParts.tsx +80 -28
- package/src/Deleted.tsx +18 -4
- package/src/EditorFooter.tsx +14 -0
- package/src/EntryForm.tsx +75 -39
- package/src/FormFrame.tsx +3 -8
- package/src/Library.tsx +46 -51
- package/src/Licenses.tsx +27 -11
- package/src/MainDrawerParts.tsx +60 -0
- package/src/MainMenu.tsx +136 -0
- package/src/MarkdownEditor.tsx +175 -32
- package/src/Media.tsx +69 -16
- package/src/MediaTags.tsx +91 -0
- package/src/ModalSheetSample.tsx +47 -0
- package/src/NavigationParts.tsx +67 -21
- package/src/NavigationRestParts.tsx +1 -1
- package/src/Parts.tsx +71 -0
- package/src/PartsShown.tsx +1 -1
- package/src/PickerParts.tsx +2 -2
- package/src/Redirects.tsx +3 -2
- package/src/Screen.tsx +93 -90
- package/src/SelectionParts.tsx +22 -6
- package/src/SiteLogoLink.tsx +14 -0
- package/src/StartFab.tsx +22 -0
- package/src/StartStatus.tsx +1 -1
- package/src/autoTags.d.mts +28 -0
- package/src/autoTags.mjs +68 -0
- package/src/autoTags.test.mjs +109 -0
- package/src/buildSettings.test.mjs +20 -0
- package/src/buildSettings.ts +20 -0
- package/src/checkSite.ts +14 -1
- package/src/deploy.ts +7 -9
- package/src/editor.css +150 -17
- package/src/eyebrow.d.mts +5 -0
- package/src/eyebrow.mjs +53 -0
- package/src/eyebrowState.d.mts +21 -0
- package/src/eyebrowState.mjs +62 -0
- package/src/eyebrowState.test.mjs +78 -0
- package/src/fileRecords.d.mts +115 -0
- package/src/fileRecords.mjs +224 -0
- package/src/fileRecords.test.mjs +224 -0
- package/src/fileTagFilter.d.mts +30 -0
- package/src/fileTagFilter.mjs +44 -0
- package/src/fileTagFilter.test.mjs +57 -0
- package/src/fileTags.d.mts +56 -0
- package/src/fileTags.mjs +100 -0
- package/src/fileTags.test.mjs +127 -0
- package/src/firstHeading.d.mts +6 -0
- package/src/firstHeading.mjs +37 -0
- package/src/i18n/da-DK/advanced.ts +27 -12
- package/src/i18n/da-DK/app.ts +9 -7
- package/src/i18n/da-DK/apps.ts +16 -16
- package/src/i18n/da-DK/blockSettings.ts +9 -9
- package/src/i18n/da-DK/blocks.ts +2 -2
- package/src/i18n/da-DK/check.ts +5 -2
- package/src/i18n/da-DK/cms.ts +14 -12
- package/src/i18n/da-DK/colors.ts +5 -5
- package/src/i18n/da-DK/common.ts +2 -2
- package/src/i18n/da-DK/company.ts +3 -3
- package/src/i18n/da-DK/consents.ts +6 -6
- package/src/i18n/da-DK/forms.ts +6 -6
- package/src/i18n/da-DK/history.ts +6 -6
- package/src/i18n/da-DK/keywords.ts +2 -2
- package/src/i18n/da-DK/library.ts +17 -15
- package/src/i18n/da-DK/licenses.ts +4 -4
- package/src/i18n/da-DK/media.ts +10 -4
- package/src/i18n/da-DK/parts.ts +62 -25
- package/src/i18n/da-DK/preview.ts +5 -6
- package/src/i18n/da-DK/redirects.ts +7 -7
- package/src/i18n/da-DK/seo.ts +1 -1
- package/src/i18n/da-DK/text.ts +2 -0
- package/src/i18n/da-DK/theme.ts +5 -5
- package/src/i18n/en-US/advanced.ts +34 -18
- package/src/i18n/en-US/app.ts +7 -5
- package/src/i18n/en-US/apps.ts +9 -9
- package/src/i18n/en-US/blockSettings.ts +4 -4
- package/src/i18n/en-US/blocks.ts +6 -6
- package/src/i18n/en-US/check.ts +6 -3
- package/src/i18n/en-US/cms.ts +15 -14
- package/src/i18n/en-US/colors.ts +18 -18
- package/src/i18n/en-US/common.ts +3 -3
- package/src/i18n/en-US/consents.ts +4 -4
- package/src/i18n/en-US/forms.ts +5 -5
- package/src/i18n/en-US/history.ts +2 -2
- package/src/i18n/en-US/library.ts +21 -15
- package/src/i18n/en-US/licenses.ts +6 -6
- package/src/i18n/en-US/media.ts +7 -1
- package/src/i18n/en-US/parts.ts +60 -21
- package/src/i18n/en-US/preview.ts +5 -6
- package/src/i18n/en-US/redirects.ts +3 -3
- package/src/i18n/en-US/seo.ts +1 -1
- package/src/i18n/en-US/text.ts +3 -0
- package/src/i18n/en-US/theme.ts +6 -6
- package/src/icon.tsx +90 -27
- package/src/licenseData.test.mjs +28 -0
- package/src/licenseData.ts +12 -1
- package/src/markdown.d.mts +2 -0
- package/src/markdown.mjs +73 -5
- package/src/markdown.test.mjs +74 -0
- package/src/materialIcons.ts +8 -0
- package/src/menuChildren.ts +36 -0
- package/src/menuOpen.ts +34 -0
- package/src/pagePreview.tsx +23 -19
- package/src/r2Caching.ts +14 -0
- package/src/sectionFrame.ts +39 -0
- package/src/sectionState.d.mts +19 -0
- package/src/sectionState.mjs +31 -0
- package/src/sectionState.test.mjs +59 -0
- package/src/sections.d.mts +9 -7
- package/src/sections.mjs +74 -27
- package/src/sections.test.mjs +59 -0
- package/src/sidebar.ts +2 -2
- package/src/site.ts +27 -17
- package/src/stillCursor.d.mts +8 -0
- package/src/stillCursor.mjs +19 -0
- package/src/stillCursor.test.mjs +26 -0
- package/src/tagUses.d.mts +47 -0
- package/src/tagUses.mjs +121 -0
- package/src/tagUses.test.mjs +141 -0
- package/src/themeUi.ts +10 -0
- package/src/topBarSlot.tsx +37 -0
- package/src/ui.tsx +654 -150
- package/src/virtual.d.ts +5 -0
- package/starter/README.md +2 -1
- package/starter/package.json +2 -1
- package/starter/wrangler.jsonc +3 -2
- package/theme/blocks/cards/LinkCards.tsx +2 -1
- package/theme/blocks/hero/View.tsx +2 -1
- package/theme/blocks/video/View.tsx +2 -1
- package/theme/components/BlogIndex.astro +2 -2
- package/theme/components/BlogPosts.astro +1 -1
- package/theme/components/BlogPosts.tsx +9 -6
- package/theme/components/Container.astro +1 -1
- package/theme/components/CookieConsent.astro +1 -1
- package/theme/components/EndBlock.astro +1 -1
- package/theme/components/Footer.astro +5 -5
- package/theme/components/Header.astro +3 -3
- package/theme/components/Icon.astro +2 -2
- package/theme/components/MobileMenu.tsx +72 -61
- package/theme/components/MobileNav.astro +2 -2
- package/theme/components/PageHeader.astro +2 -2
- package/theme/components/PageSection.astro +1 -1
- package/theme/components/PopupButton.tsx +2 -2
- package/theme/components/PopupView.tsx +2 -2
- package/theme/components/PostList.tsx +7 -3
- package/theme/components/RichText.astro +22 -7
- package/theme/components/Section.astro +17 -10
- package/theme/components/SectionHeading.tsx +1 -1
- package/theme/components/home/PageBlock.astro +4 -4
- package/theme/components/home/Text.astro +3 -3
- package/theme/layouts/PageLayout.astro +3 -3
- package/theme/layouts/PostLayout.astro +9 -8
- package/theme/styles/base.css +13 -3
- package/theme/styles/blocks.css +97 -10
- package/theme/styles/components.css +9 -9
- package/theme/styles/elevation.test.mjs +168 -0
- package/theme/styles/global.css +65 -59
- package/theme/styles/ui.css +460 -131
- package/theme/templates/Home.astro +1 -1
- package/theme/templates/New.astro +5 -5
- package/theme/templates/NotFound.astro +2 -2
- package/theme/ui/BottomSheet.tsx +118 -21
- package/theme/ui/BottomSheetResizeIcon.tsx +13 -0
- package/theme/ui/Button.tsx +4 -3
- package/theme/ui/Dialog.tsx +1 -1
- package/theme/ui/FabMenu.tsx +13 -12
- package/theme/ui/LoadingIndicator.tsx +1 -1
- package/theme/ui/NavigationDrawer.tsx +116 -15
- package/theme/ui/NavigationRail.tsx +22 -13
- package/theme/ui/Search.tsx +1 -1
- package/theme/ui/SettingRow.tsx +31 -0
- package/theme/ui/Switch.tsx +11 -11
- package/theme/ui/TextField.tsx +17 -6
- package/theme/ui/bottomSheet.script.ts +3 -2
- package/theme/ui/bottomSheetControl.ts +131 -46
- package/theme/ui/fabMenu.script.ts +69 -53
- package/theme/ui/modalSheetSize.ts +214 -0
- package/theme/ui/navigationDrawerControl.ts +35 -0
- package/theme/ui/search.script.ts +4 -2
- package/worker/index.js +3 -0
- package/worker/plan-status.js +46 -0
- package/worker/push.js +7 -5
- package/worker/r2-status.js +2 -1
- package/icons/lucide/desktop.svg +0 -1
- package/icons/material-symbols/desktop.svg +0 -1
- package/icons/phosphor/desktop.svg +0 -1
package/src/Parts.tsx
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
import { useMemo, useRef, useState } from 'react';
|
|
8
8
|
import {
|
|
9
9
|
AppWindow,
|
|
10
|
+
Article,
|
|
11
|
+
Eye,
|
|
12
|
+
SlidersHorizontal,
|
|
10
13
|
Gear,
|
|
11
14
|
ArrowDown,
|
|
12
15
|
ArrowsDownUp,
|
|
@@ -29,6 +32,8 @@ import {
|
|
|
29
32
|
Trash,
|
|
30
33
|
} from '@phosphor-icons/react';
|
|
31
34
|
import Screen from './Screen';
|
|
35
|
+
import EditorFooter from './EditorFooter';
|
|
36
|
+
import { MainDrawerSample } from './MainDrawerParts';
|
|
32
37
|
import { Part, PartGroup, PartsShown, useGrouped, useSearched } from './PartsShown';
|
|
33
38
|
import {
|
|
34
39
|
EmptyNote,
|
|
@@ -89,6 +94,11 @@ import {
|
|
|
89
94
|
ChoiceRow,
|
|
90
95
|
IconSwatches,
|
|
91
96
|
ListItem,
|
|
97
|
+
MenuRow,
|
|
98
|
+
SectionList,
|
|
99
|
+
SectionPane,
|
|
100
|
+
SectionSheet,
|
|
101
|
+
SectionsButton,
|
|
92
102
|
} from './ui';
|
|
93
103
|
import { FieldList, Fields, useWrongField, WrongField, type FieldContext } from './fields';
|
|
94
104
|
import { newValue, problemAt } from './check';
|
|
@@ -100,6 +110,7 @@ import type { Field } from './schema';
|
|
|
100
110
|
import { defaultColors } from '@site/editor';
|
|
101
111
|
import { BASE_COLORS, canSetBaseColor, wheelColor, wheelColors, wheelDots, withBaseColor, type Dynamic } from '../lib/dynamic-colors';
|
|
102
112
|
import { iconPacks } from './iconPacks';
|
|
113
|
+
import { jurisdictionName } from './r2Caching';
|
|
103
114
|
import type { Env } from './App';
|
|
104
115
|
import { t } from './i18n';
|
|
105
116
|
|
|
@@ -125,6 +136,7 @@ export default function Parts({ env }: { env: Env }) {
|
|
|
125
136
|
const [phone, setPhone] = useState({ code: '+45', number: examples.contact.phone });
|
|
126
137
|
const [tile, setTile] = useState(0);
|
|
127
138
|
const [placement, setPlacement] = useState('top');
|
|
139
|
+
const [menuPick, setMenuPick] = useState('auto');
|
|
128
140
|
const [unfolded, setUnfolded] = useState(false);
|
|
129
141
|
const [front, setFront] = useState(true);
|
|
130
142
|
// An e-mail address without its domain: the field says what is wrong.
|
|
@@ -134,6 +146,13 @@ export default function Parts({ env }: { env: Env }) {
|
|
|
134
146
|
const [picture, setPicture] = useState(examplePicture);
|
|
135
147
|
const [folded, setFolded] = useState<number | null>(0);
|
|
136
148
|
const [sectionOpen, setSectionOpen] = useState<number | null>(0);
|
|
149
|
+
const [sectionsSheet, setSectionsSheet] = useState(false);
|
|
150
|
+
const [sectionPicked, setSectionPicked] = useState('content');
|
|
151
|
+
const sectionItems = [
|
|
152
|
+
{ id: 'content', label: t.forms.sections.content, icon: Article },
|
|
153
|
+
{ id: 'status', label: t.forms.sections.status, icon: Eye, badge: 2 },
|
|
154
|
+
{ id: 'other', label: t.forms.otherFields, icon: SlidersHorizontal },
|
|
155
|
+
];
|
|
137
156
|
const [tags, setTags] = useState(['a', 'b']);
|
|
138
157
|
const dialog = useRef<HTMLDialogElement>(null);
|
|
139
158
|
const fieldsDialog = useRef<HTMLDialogElement>(null);
|
|
@@ -423,6 +442,26 @@ export default function Parts({ env }: { env: Env }) {
|
|
|
423
442
|
</section>
|
|
424
443
|
</Part>
|
|
425
444
|
|
|
445
|
+
<Part name="MenuRow" use={use.menuRow}>
|
|
446
|
+
<MenuRow
|
|
447
|
+
label={t.advanced.place}
|
|
448
|
+
hint={t.advanced.placeHint}
|
|
449
|
+
value={menuPick}
|
|
450
|
+
options={[
|
|
451
|
+
{ value: 'auto', label: t.advanced.placeAuto },
|
|
452
|
+
{ value: 'eu', label: jurisdictionName('eu') },
|
|
453
|
+
]}
|
|
454
|
+
onChange={setMenuPick}
|
|
455
|
+
/>
|
|
456
|
+
<MenuRow
|
|
457
|
+
label={t.advanced.place}
|
|
458
|
+
value="auto"
|
|
459
|
+
options={[{ value: 'auto', label: t.advanced.placeAuto }]}
|
|
460
|
+
disabled
|
|
461
|
+
onChange={() => {}}
|
|
462
|
+
/>
|
|
463
|
+
</Part>
|
|
464
|
+
|
|
426
465
|
<Part name="UnfoldButton, subListClass" use={use.unfold}>
|
|
427
466
|
<ul className={listClass}>
|
|
428
467
|
<ListItem
|
|
@@ -645,6 +684,14 @@ export default function Parts({ env }: { env: Env }) {
|
|
|
645
684
|
/>
|
|
646
685
|
</Part>
|
|
647
686
|
|
|
687
|
+
<Part name="MainDrawer, MainDrawerSection, MainDrawerItem, MenuButton" use={use.mainDrawer}>
|
|
688
|
+
<MainDrawerSample />
|
|
689
|
+
</Part>
|
|
690
|
+
|
|
691
|
+
<Part name="EditorFooter" use={use.editorFooter}>
|
|
692
|
+
<EditorFooter />
|
|
693
|
+
</Part>
|
|
694
|
+
|
|
648
695
|
<Part name="FoldSection" use={use.foldSection}>
|
|
649
696
|
<div>
|
|
650
697
|
{[t.forms.sections.content, t.forms.otherFields].map((title, i) => (
|
|
@@ -660,6 +707,30 @@ export default function Parts({ env }: { env: Env }) {
|
|
|
660
707
|
</div>
|
|
661
708
|
</Part>
|
|
662
709
|
|
|
710
|
+
<Part name="SectionList, SectionPane, SectionSheet, SectionsButton" use={use.sectionList}>
|
|
711
|
+
<div className="space-y-4">
|
|
712
|
+
<div className={row}>
|
|
713
|
+
<SectionsButton label={t.forms.sections.title} open={sectionsSheet} onClick={() => setSectionsSheet(true)} />
|
|
714
|
+
</div>
|
|
715
|
+
<div className="max-w-sm">
|
|
716
|
+
<SectionPane title={t.forms.sections.title} open>
|
|
717
|
+
<SectionList title={t.forms.sections.title} items={sectionItems} value={sectionPicked} onChange={setSectionPicked} />
|
|
718
|
+
</SectionPane>
|
|
719
|
+
</div>
|
|
720
|
+
</div>
|
|
721
|
+
<SectionSheet title={t.forms.sections.title} open={sectionsSheet} onClose={() => setSectionsSheet(false)}>
|
|
722
|
+
<SectionList
|
|
723
|
+
title={t.forms.sections.title}
|
|
724
|
+
items={sectionItems}
|
|
725
|
+
value={sectionPicked}
|
|
726
|
+
onChange={(id) => {
|
|
727
|
+
setSectionPicked(id);
|
|
728
|
+
setSectionsSheet(false);
|
|
729
|
+
}}
|
|
730
|
+
/>
|
|
731
|
+
</SectionSheet>
|
|
732
|
+
</Part>
|
|
733
|
+
|
|
663
734
|
<Part name="SharePreview" use={use.sharePreview}>
|
|
664
735
|
<SharePreview image={examplePicture} domain={sample.domain} title={examples.pages[0]} description={examples.text} />
|
|
665
736
|
</Part>
|
package/src/PartsShown.tsx
CHANGED
|
@@ -31,7 +31,7 @@ export const useSearched = () => useContext(Shown).query.trim();
|
|
|
31
31
|
export const useGrouped = () => useContext(Shown).sort === 'group';
|
|
32
32
|
|
|
33
33
|
/** The search, the order and the filter over the parts (`children`, PartGroup), with "Ingen dele passer
|
|
34
|
-
* til søgningen
|
|
34
|
+
* til søgningen" (or `none`) when none is shown. `keep`: the key they are kept under; `search`: the search's name. */
|
|
35
35
|
export function PartsShown({
|
|
36
36
|
groups,
|
|
37
37
|
keep,
|
package/src/PickerParts.tsx
CHANGED
|
@@ -38,7 +38,7 @@ export default function PickerParts() {
|
|
|
38
38
|
<Part name="DatePicker" use={say.datePicker}>
|
|
39
39
|
<Preview>
|
|
40
40
|
<div className="space-y-6 p-4">
|
|
41
|
-
<div className="grid gap-6
|
|
41
|
+
<div className="grid gap-6 medium:grid-cols-2" role="group" aria-label={say.fields}>
|
|
42
42
|
<SiteDatePicker label={say.date} name="date" />
|
|
43
43
|
<SiteDatePicker label={say.date} name="date-modal" kind="modal" value={picked} />
|
|
44
44
|
<SiteDatePicker label={say.period} name="from" endName="to" range value={start} endValue={end} />
|
|
@@ -126,7 +126,7 @@ export default function PickerParts() {
|
|
|
126
126
|
</Part>
|
|
127
127
|
<Part name="TimePicker" use={say.timePicker}>
|
|
128
128
|
<Preview>
|
|
129
|
-
<div className="grid gap-6 p-4
|
|
129
|
+
<div className="grid gap-6 p-4 medium:grid-cols-2" role="group" aria-label={say.fields}>
|
|
130
130
|
<SiteTimePicker label={say.time} name="time" />
|
|
131
131
|
<SiteTimePicker label={say.time} name="time-set" value="14:30" />
|
|
132
132
|
<SiteTimePicker label={`${say.time} (${say.twelveHours})`} name="time-12" value="14:30" timeFormat="12h" />
|
package/src/Redirects.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// "Videresendelser" under
|
|
1
|
+
// "Videresendelser" under Indhold (#seo/videresendelser): two rows with how many
|
|
2
2
|
// each has, each opening a screen of its own.
|
|
3
3
|
// "Alle videresendelser" (#seo/videresendelser/alle): every address of a page the site sends on elsewhere
|
|
4
4
|
// (redirectData.ts), by address, with where it sends to, and search. Tapping one opens a sheet where
|
|
@@ -45,6 +45,7 @@ import { byText } from './schema';
|
|
|
45
45
|
import { savedText } from './deploy';
|
|
46
46
|
import { config, whatIsAt } from '@site/editor';
|
|
47
47
|
import { siteT, t } from './i18n';
|
|
48
|
+
import { backFrom } from './site';
|
|
48
49
|
|
|
49
50
|
// An address as typed, as one: "/ny-side/", "/" for the front page, a #part kept, the site's own
|
|
50
51
|
// domain left out; a link to another site as it is. '' when nothing is typed.
|
|
@@ -251,7 +252,7 @@ export default function Redirects({ env, tab }: { env: Env; tab?: Tab }) {
|
|
|
251
252
|
{ tab: 'missing' as const, label: t.redirects.missing.title, icon: LinkBreak, count: missing && files ? stillMissing.length : null },
|
|
252
253
|
];
|
|
253
254
|
return (
|
|
254
|
-
<Screen title={t.common.parts.redirects} wide back={
|
|
255
|
+
<Screen title={t.common.parts.redirects} wide back={backFrom('videresendelser')}>
|
|
255
256
|
{problem && <ProblemNote>{problem}</ProblemNote>}
|
|
256
257
|
<Group>
|
|
257
258
|
{rows.map((r) => (
|
package/src/Screen.tsx
CHANGED
|
@@ -1,22 +1,26 @@
|
|
|
1
|
-
// A screen of the editor: a top bar like in an app
|
|
2
|
-
//
|
|
3
|
-
// the
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// and the status
|
|
8
|
-
//
|
|
9
|
-
// the
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// (
|
|
1
|
+
// A screen of the editor: a top bar like in an app, beside the navigation rail and the drawer (it is drawn into
|
|
2
|
+
// topBarSlot.tsx's place, at the top of the content), above the content. Below medium, where there is no rail, the panel
|
|
3
|
+
// button at its start opens and closes the main drawer (MainMenu.tsx, menuOpen.ts; from medium it is the rail's); at expanded and wider the logo and the site's name after it (one link to the start screen,
|
|
4
|
+
// through `back.guard`), below expanded the back arrow, the title and the status as in an app (the logo only on the start
|
|
5
|
+
// screen); at the end the actions, the notifications and the account, on every screen (on a phone the account only on the
|
|
6
|
+
// start screen, `home`). At expanded and wider the way here as breadcrumbs (the screen above, when that is not the start
|
|
7
|
+
// screen), the title and the status stand in a row under the bar, at the top of the content. The bar stays at the top while
|
|
8
|
+
// scrolling and keeps clear of a phone's notch. A screen's tabs (`tabs`) are the bottom of the bar, under the title, as wide
|
|
9
|
+
// as the content, and stay with it. The back arrow goes to the screen above, back in the history when that is the screen
|
|
10
|
+
// before (nav.ts up). The content is as wide as a form reads well, or wider for lists and the grid of files (`wide`) and for
|
|
11
|
+
// a form with its fields beside the text (`panel`).
|
|
12
|
+
// Under the content of every screen is the editor's footer (EditorFooter.tsx).
|
|
13
13
|
import { useId, type ReactNode } from 'react';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
14
|
+
import { createPortal } from 'react-dom';
|
|
15
|
+
import { ArrowLeft, Bell, CaretRight, UserCircle } from '@phosphor-icons/react';
|
|
16
16
|
import { t } from './i18n';
|
|
17
|
-
import {
|
|
17
|
+
import { IconLink, MenuButton, TabPanel, TabRow, TopAppBar, useMenuLayout, type TabChoice } from './ui';
|
|
18
18
|
import { up } from './nav';
|
|
19
|
+
import EditorFooter from './EditorFooter';
|
|
20
|
+
import { useMenuOpen } from './menuOpen';
|
|
19
21
|
import { unseen, useNotifications } from './notificationData';
|
|
22
|
+
import { SiteLogoLink } from './SiteLogoLink';
|
|
23
|
+
import { useTopBarSlot } from './topBarSlot';
|
|
20
24
|
|
|
21
25
|
// `label` is the screen back goes to, named by the back arrow and the breadcrumbs. `guard` takes every
|
|
22
26
|
// way out of the screen (back, the breadcrumbs and the logo) and goes it, after warning about changes
|
|
@@ -25,9 +29,9 @@ export type Back = { href: string; label: string; guard?: (go: () => void) => vo
|
|
|
25
29
|
|
|
26
30
|
// `status`: more about the screen, e.g. when it was last saved: under the title, on a computer after it.
|
|
27
31
|
// `wide`: the content is wider on a computer (lists, the files); `panel`: a form with its other fields
|
|
28
|
-
// beside the text on a wide computer screen (FormColumns in ui.tsx). `drawer`: the
|
|
29
|
-
//
|
|
30
|
-
// with the
|
|
32
|
+
// beside the text on a wide computer screen (FormColumns in ui.tsx). `drawer`: a button at the end of the
|
|
33
|
+
// bar, after the account (a form's SectionsButton). `home`: the start screen,
|
|
34
|
+
// with the account on a phone too. `tabs`: the screen's tabs, in the bar, with the open one's content.
|
|
31
35
|
type Props<T extends string> = {
|
|
32
36
|
title: string;
|
|
33
37
|
back?: Back;
|
|
@@ -56,7 +60,7 @@ export function AccountLink({ className = '' }: { className?: string }) {
|
|
|
56
60
|
);
|
|
57
61
|
}
|
|
58
62
|
|
|
59
|
-
/** The bell in the top bar,
|
|
63
|
+
/** The bell in the top bar, before the account: the notifications (#notifikationer, Notifications.tsx), with a dot
|
|
60
64
|
* while some are not seen yet. */
|
|
61
65
|
export function NotificationsLink({ className = '' }: { className?: string }) {
|
|
62
66
|
const count = unseen(useNotifications());
|
|
@@ -74,24 +78,6 @@ export function NotificationsLink({ className = '' }: { className?: string }) {
|
|
|
74
78
|
);
|
|
75
79
|
}
|
|
76
80
|
|
|
77
|
-
/** The gear in the top bar, at the left of the account: "Indstillinger" (#indstillinger, Library.tsx Settings), marked
|
|
78
|
-
* there and on the settings it opens ("Udseende", "Apps", "Analytics" … and their screens). Only when the site has any. */
|
|
79
|
-
export function SettingsLink({ className = '' }: { className?: string }) {
|
|
80
|
-
if (!settings.length) return null;
|
|
81
|
-
const route = location.hash.slice(1);
|
|
82
|
-
const current = route === 'indstillinger' || settings.some((id) => route === id || route.startsWith(`${id}/`));
|
|
83
|
-
return (
|
|
84
|
-
<IconLink
|
|
85
|
-
href="#indstillinger"
|
|
86
|
-
icon={Gear}
|
|
87
|
-
label={t.common.parts.settings}
|
|
88
|
-
aria-current={current ? 'page' : undefined}
|
|
89
|
-
selected={current}
|
|
90
|
-
className={className}
|
|
91
|
-
/>
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
81
|
export default function Screen<T extends string>({
|
|
96
82
|
title,
|
|
97
83
|
back,
|
|
@@ -105,7 +91,9 @@ export default function Screen<T extends string>({
|
|
|
105
91
|
children,
|
|
106
92
|
}: Props<T>) {
|
|
107
93
|
const id = useId();
|
|
108
|
-
const
|
|
94
|
+
const menu = useMenuOpen();
|
|
95
|
+
const { menuButtonInTopBar } = useMenuLayout();
|
|
96
|
+
const width = wide ? 'max-w-3xl medium:max-w-5xl' : panel ? 'max-w-3xl large:max-w-6xl' : 'max-w-3xl';
|
|
109
97
|
// A link out of the screen, through `guard`: the back arrow goes up (nav.ts), the other links as links.
|
|
110
98
|
const leave = (go?: () => void) => (e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
111
99
|
if (!go && !back?.guard) return;
|
|
@@ -115,62 +103,76 @@ export default function Screen<T extends string>({
|
|
|
115
103
|
if (back?.guard) back.guard(way);
|
|
116
104
|
else way();
|
|
117
105
|
};
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
106
|
+
const slot = useTopBarSlot();
|
|
107
|
+
// The way here, without the start screen: the logo in the bar is the way there.
|
|
108
|
+
const crumbs = back && back.href !== '#' ? [back] : [];
|
|
109
|
+
const bar = (
|
|
110
|
+
<TopAppBar>
|
|
111
|
+
<div className="flex h-16 items-center gap-1 px-2 medium:gap-3 medium:px-6">
|
|
112
|
+
{menuButtonInTopBar && (
|
|
113
|
+
<MenuButton
|
|
114
|
+
label={menu.open ? t.library.closeMenu : t.library.openMenu}
|
|
115
|
+
open={menu.open}
|
|
116
|
+
onClick={menu.toggle}
|
|
117
|
+
className="shrink-0"
|
|
118
|
+
/>
|
|
119
|
+
)}
|
|
120
|
+
<SiteLogoLink onClick={leave()} className={home ? '' : 'hidden expanded:flex'} />
|
|
121
|
+
{back && (
|
|
122
|
+
<a
|
|
123
|
+
href={back.href}
|
|
124
|
+
onClick={leave(() => up(back.href))}
|
|
125
|
+
title={t.common.backTo(back.label)}
|
|
126
|
+
aria-label={t.common.backTo(back.label)}
|
|
127
|
+
className="flex size-12 shrink-0 items-center justify-center rounded-full hover:bg-tint expanded:hidden"
|
|
128
|
+
>
|
|
129
|
+
<ArrowLeft size={24} />
|
|
130
|
+
</a>
|
|
131
|
+
)}
|
|
132
|
+
<div className="flex min-w-0 flex-1 flex-col pl-1 expanded:hidden">
|
|
133
|
+
<h1 className="truncate text-xl leading-tight">{title}</h1>
|
|
134
|
+
{status && (
|
|
135
|
+
<p data-subtitle className="truncate text-xs text-muted">
|
|
136
|
+
{status}
|
|
137
|
+
</p>
|
|
135
138
|
)}
|
|
136
|
-
<div className="flex min-w-0 flex-1 flex-col pl-1 md:hidden">
|
|
137
|
-
<h1 className="truncate text-xl leading-tight">{title}</h1>
|
|
138
|
-
{status && (
|
|
139
|
-
<p data-subtitle className="truncate text-xs text-muted">
|
|
140
|
-
{status}
|
|
141
|
-
</p>
|
|
142
|
-
)}
|
|
143
|
-
</div>
|
|
144
|
-
{/* On a computer: the way here, the title and the status. */}
|
|
145
|
-
<div className="hidden min-w-0 flex-1 items-center gap-x-2 md:flex">
|
|
146
|
-
{back && (
|
|
147
|
-
<nav aria-label={t.library.breadcrumbs} className="flex shrink-0 items-center gap-2 text-muted">
|
|
148
|
-
{(back.href === '#' ? [back] : [{ href: '#', label: siteName }, back]).map((c) => (
|
|
149
|
-
<span key={c.href} className="flex items-center gap-2">
|
|
150
|
-
<a href={c.href} onClick={leave()} className="max-w-48 truncate hover:text-accent hover:underline">
|
|
151
|
-
{c.label}
|
|
152
|
-
</a>
|
|
153
|
-
<CaretRight size={14} className="shrink-0" />
|
|
154
|
-
</span>
|
|
155
|
-
))}
|
|
156
|
-
</nav>
|
|
157
|
-
)}
|
|
158
|
-
<h1 className="truncate font-medium">{title}</h1>
|
|
159
|
-
{status && <p className="shrink-0 text-sm text-muted">{status}</p>}
|
|
160
|
-
</div>
|
|
161
|
-
<div className="flex shrink-0 items-center justify-end gap-1">{actions}</div>
|
|
162
|
-
<NotificationsLink />
|
|
163
|
-
<SettingsLink className={home ? '' : 'hidden md:inline-grid'} />
|
|
164
|
-
<AccountLink className={home ? '' : 'hidden md:inline-grid'} />
|
|
165
|
-
{drawer}
|
|
166
139
|
</div>
|
|
167
|
-
|
|
168
|
-
|
|
140
|
+
<div className="hidden min-w-0 flex-1 expanded:block" />
|
|
141
|
+
<div className="flex shrink-0 items-center justify-end gap-1">{actions}</div>
|
|
142
|
+
<NotificationsLink />
|
|
143
|
+
<AccountLink className={home ? '' : 'hidden medium:inline-grid'} />
|
|
144
|
+
{drawer}
|
|
145
|
+
</div>
|
|
146
|
+
{tabs && (
|
|
147
|
+
<div className="editor-bar-tabs">
|
|
148
|
+
<div className={`mx-auto medium:px-6 ${width}`}>
|
|
169
149
|
<TabRow id={id} {...tabs} />
|
|
170
150
|
</div>
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
151
|
+
</div>
|
|
152
|
+
)}
|
|
153
|
+
</TopAppBar>
|
|
154
|
+
);
|
|
155
|
+
return (
|
|
156
|
+
<div className="min-h-dvh">
|
|
157
|
+
{slot ? createPortal(bar, slot) : bar}
|
|
158
|
+
<main className={`editor-screen mx-auto px-4 pt-6 pb-[calc(6rem+env(safe-area-inset-bottom))] medium:px-6 medium:pt-8 ${width}`}>
|
|
159
|
+
{/* At expanded and wider: the way here, the title and the status, under the bar. */}
|
|
160
|
+
<div className="mb-6 hidden min-w-0 items-center gap-x-2 expanded:flex">
|
|
161
|
+
{crumbs.length > 0 && (
|
|
162
|
+
<nav aria-label={t.library.breadcrumbs} className="flex shrink-0 items-center gap-2 text-muted">
|
|
163
|
+
{crumbs.map((c) => (
|
|
164
|
+
<span key={c.href} className="flex items-center gap-2">
|
|
165
|
+
<a href={c.href} onClick={leave()} className="max-w-48 truncate hover:text-accent hover:underline">
|
|
166
|
+
{c.label}
|
|
167
|
+
</a>
|
|
168
|
+
<CaretRight size={14} className="shrink-0" />
|
|
169
|
+
</span>
|
|
170
|
+
))}
|
|
171
|
+
</nav>
|
|
172
|
+
)}
|
|
173
|
+
<h1 className="truncate font-medium">{title}</h1>
|
|
174
|
+
{status && <p className="shrink-0 text-sm text-muted">{status}</p>}
|
|
175
|
+
</div>
|
|
174
176
|
{tabs ? (
|
|
175
177
|
<TabPanel id={id} value={tabs.value}>
|
|
176
178
|
{children}
|
|
@@ -178,6 +180,7 @@ export default function Screen<T extends string>({
|
|
|
178
180
|
) : (
|
|
179
181
|
children
|
|
180
182
|
)}
|
|
183
|
+
<EditorFooter />
|
|
181
184
|
</main>
|
|
182
185
|
</div>
|
|
183
186
|
);
|
package/src/SelectionParts.tsx
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
// "Dele" › "UI-komponenter" (BlockParts.tsx), the group "Kontakter, valg og sideark": the theme's switches, radio buttons,
|
|
1
|
+
// "Dele" › "UI-komponenter" (BlockParts.tsx), the group "Kontakter, valg og sideark": the theme's switches, setting rows, radio buttons,
|
|
2
2
|
// sliders, dividers and side sheets (packages/editor/theme/ui/, through @site/editor) as the site draws them, in every
|
|
3
3
|
// kind and state (the sliders also centered, in each size, with an icon and vertical), with made-up texts. The
|
|
4
4
|
// switches, radio buttons and sliders can be tried (a slider's tracks and labels follow it here as on the site);
|
|
5
5
|
// nothing is saved, and the side sheets are only drawn (on the site a modal one opens over the page with sideSheet()).
|
|
6
6
|
// On a phone the standard one is drawn without the page beside it, as there is no room for both.
|
|
7
|
-
import { SiteButton, SiteDivider, SiteRadioButton, SiteRadioGroup, SiteSideSheet, SiteSlider, SiteSwitch } from './themeUi';
|
|
7
|
+
import { SiteButton, SiteDivider, SiteRadioButton, SiteRadioGroup, SiteSettingRow, SiteSideSheet, SiteSlider, SiteSwitch } from './themeUi';
|
|
8
8
|
import { Part } from './PartsShown';
|
|
9
9
|
import { Preview } from './ui';
|
|
10
10
|
import { t } from './i18n';
|
|
@@ -14,7 +14,7 @@ const dividerKinds = ['full-width', 'inset', 'middle-inset'] as const;
|
|
|
14
14
|
const sliderSizes = ['xsmall', 'small', 'medium', 'large', 'xlarge'] as const;
|
|
15
15
|
|
|
16
16
|
export default function SelectionParts() {
|
|
17
|
-
const grid = 'grid gap-6 p-4
|
|
17
|
+
const grid = 'grid gap-6 p-4 medium:grid-cols-2';
|
|
18
18
|
const sheet = (kind: 'standard' | 'modal') => (
|
|
19
19
|
<SiteSideSheet
|
|
20
20
|
kind={kind}
|
|
@@ -44,6 +44,22 @@ export default function SelectionParts() {
|
|
|
44
44
|
</div>
|
|
45
45
|
</Preview>
|
|
46
46
|
</Part>
|
|
47
|
+
<Part name="SettingRow" use={say.settingRow}>
|
|
48
|
+
<Preview>
|
|
49
|
+
<div className={grid}>
|
|
50
|
+
<SiteSettingRow label={say.switchLabel} supporting={say.switchSupporting}>
|
|
51
|
+
<SiteButton kind="text" size="small">
|
|
52
|
+
{say.choices[0]}
|
|
53
|
+
</SiteButton>
|
|
54
|
+
</SiteSettingRow>
|
|
55
|
+
<SiteSettingRow label={say.switchLabel}>
|
|
56
|
+
<SiteButton kind="text" size="small" disabled>
|
|
57
|
+
{say.choices[1]}
|
|
58
|
+
</SiteButton>
|
|
59
|
+
</SiteSettingRow>
|
|
60
|
+
</div>
|
|
61
|
+
</Preview>
|
|
62
|
+
</Part>
|
|
47
63
|
<Part name="RadioButton" use={say.radioButton}>
|
|
48
64
|
<Preview>
|
|
49
65
|
<div className={grid}>
|
|
@@ -69,7 +85,7 @@ export default function SelectionParts() {
|
|
|
69
85
|
</Part>
|
|
70
86
|
<Part name="Slider" use={say.slider}>
|
|
71
87
|
<Preview>
|
|
72
|
-
<div className="grid gap-8 p-4
|
|
88
|
+
<div className="grid gap-8 p-4 medium:grid-cols-2">
|
|
73
89
|
<SiteSlider label={say.volume} value={40} valueLabel />
|
|
74
90
|
<SiteSlider label={say.count} max={10} value={6} stops valueLabel="always" />
|
|
75
91
|
<SiteSlider label={say.price} max={1000} step={50} range={[200, 700]} handleLabels={say.handles} valueLabel="always" />
|
|
@@ -77,7 +93,7 @@ export default function SelectionParts() {
|
|
|
77
93
|
<SiteSlider label={say.balance} min={-50} max={50} value={20} centered valueLabel />
|
|
78
94
|
<SiteSlider label={say.balance} min={-50} max={50} step={10} value={-30} centered stops valueLabel="always" />
|
|
79
95
|
</div>
|
|
80
|
-
<div className="grid gap-8 p-4
|
|
96
|
+
<div className="grid gap-8 p-4 medium:grid-cols-2">
|
|
81
97
|
{sliderSizes.map((size) => (
|
|
82
98
|
<SiteSlider key={size} label={say.sliderSizes[size]} size={size} value={60} />
|
|
83
99
|
))}
|
|
@@ -118,7 +134,7 @@ export default function SelectionParts() {
|
|
|
118
134
|
<div className="space-y-2">
|
|
119
135
|
<p className="px-2 text-sm text-muted">{say.sheetKinds.standard}</p>
|
|
120
136
|
<div className="flex h-80 overflow-hidden">
|
|
121
|
-
<p className="hidden min-w-0 flex-1 p-4 text-muted
|
|
137
|
+
<p className="hidden min-w-0 flex-1 p-4 text-muted medium:block">{say.page}</p>
|
|
122
138
|
{sheet('standard')}
|
|
123
139
|
</div>
|
|
124
140
|
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// The logo and the site's name at the start of the top bar, after the menu button (Screen.tsx), and in the modal drawer after its close button (ui.tsx): one link to the start screen.
|
|
2
|
+
// Exposes SiteLogoLink; the link's click goes through `onClick` (Screen's `leave`, so the unsaved-changes warning shows).
|
|
3
|
+
import type { MouseEventHandler } from 'react';
|
|
4
|
+
import { siteName } from '@site/editor';
|
|
5
|
+
|
|
6
|
+
/** The site's logo (/editor/icon-192.png) and the site's name (at expanded and wider; for screen readers below), as a link to `#`. */
|
|
7
|
+
export function SiteLogoLink({ onClick, className = '' }: { onClick?: MouseEventHandler<HTMLAnchorElement>; className?: string }) {
|
|
8
|
+
return (
|
|
9
|
+
<a href="#" onClick={onClick} className={`flex min-w-0 shrink-0 items-center gap-3 font-semibold ${className}`}>
|
|
10
|
+
<img src="/editor/icon-192.png" alt="" className="size-7 shrink-0 rounded-lg" />
|
|
11
|
+
<span className="max-w-48 truncate max-expanded:sr-only">{siteName}</span>
|
|
12
|
+
</a>
|
|
13
|
+
);
|
|
14
|
+
}
|
package/src/StartFab.tsx
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// The start screen's FAB menu (Start in Library.tsx): a new page and a new blog post, each opening its empty form
|
|
2
|
+
// (#ny/<list>), as the + on its list does. Exposes newOnStart (the actions) and StartFab. Uses FabMenu (ui.tsx) and the
|
|
3
|
+
// collections and icons of site.ts.
|
|
4
|
+
import { Plus } from '@phosphor-icons/react';
|
|
5
|
+
import { t } from './i18n';
|
|
6
|
+
import { collections, icons } from './site';
|
|
7
|
+
import { FabMenu, type FabAction } from './ui';
|
|
8
|
+
|
|
9
|
+
/** The lists a new entry can be made in from the start screen, in this order. */
|
|
10
|
+
const ON_START = ['pages', 'blog'];
|
|
11
|
+
|
|
12
|
+
/** The start screen's actions: the lists of ON_START the site has and can make a new entry in (`create`). */
|
|
13
|
+
export function newOnStart(): FabAction[] {
|
|
14
|
+
return ON_START.flatMap((id) => {
|
|
15
|
+
const c = collections.find((x) => x.id === id);
|
|
16
|
+
return c?.create ? [{ icon: icons[id] ?? Plus, label: c.create, href: `#ny/${id}` }] : [];
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function StartFab() {
|
|
21
|
+
return <FabMenu icon={Plus} label={t.library.createNew} items={newOnStart()} />;
|
|
22
|
+
}
|
package/src/StartStatus.tsx
CHANGED
|
@@ -14,7 +14,7 @@ export function StartStatus({ builtAt }: { builtAt: Date | null }) {
|
|
|
14
14
|
const updated = builtAt ? t.library.siteUpdated(when(builtAt, { short: true })) : null;
|
|
15
15
|
return (
|
|
16
16
|
<>
|
|
17
|
-
<span className="expanded:inline
|
|
17
|
+
<span className="hidden expanded:inline">
|
|
18
18
|
<CmsVersion />
|
|
19
19
|
{updated && `${t.library.statusSeparator}${updated}`}
|
|
20
20
|
</span>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('./fileRecords.mjs').StoredRecord} StoredRecord
|
|
3
|
+
* @typedef {import('./fileRecords.mjs').RecordChange} RecordChange
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* @param {string} text
|
|
7
|
+
* @param {Iterable<string>} srcs
|
|
8
|
+
* @returns {string[]}
|
|
9
|
+
*/
|
|
10
|
+
export function filesIn(text: string, srcs: Iterable<string>): string[];
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} before
|
|
13
|
+
* @param {string} after
|
|
14
|
+
* @param {Iterable<string>} srcs
|
|
15
|
+
* @returns {string[]}
|
|
16
|
+
*/
|
|
17
|
+
export function newlyUsed(before: string, after: string, srcs: Iterable<string>): string[];
|
|
18
|
+
/**
|
|
19
|
+
* @param {StoredRecord[]} records
|
|
20
|
+
* @param {string} folder
|
|
21
|
+
* @param {string[]} srcs
|
|
22
|
+
* @param {string[]} pageTags
|
|
23
|
+
* @param {() => number} [random]
|
|
24
|
+
* @returns {RecordChange[]}
|
|
25
|
+
*/
|
|
26
|
+
export function autoTagChanges(records: StoredRecord[], folder: string, srcs: string[], pageTags: string[], random?: () => number): RecordChange[];
|
|
27
|
+
export type StoredRecord = import("./fileRecords.mjs").StoredRecord;
|
|
28
|
+
export type RecordChange = import("./fileRecords.mjs").RecordChange;
|
package/src/autoTags.mjs
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// The tags a file gets when it is newly used in a page or post (EntryForm.tsx, at "Gem"): every Filer file the saved page refers to and its last saved version did not gets the page's tags,
|
|
2
|
+
// except those removed from the file before (tagsFor), saved in the same commit as the page. Pure, so node --test can run it.
|
|
3
|
+
|
|
4
|
+
import { inherit } from './fileTags.mjs';
|
|
5
|
+
import { recordFor, writeRecords } from './fileRecords.mjs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {import('./fileRecords.mjs').StoredRecord} StoredRecord
|
|
9
|
+
* @typedef {import('./fileRecords.mjs').RecordChange} RecordChange
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} text
|
|
14
|
+
* @param {Iterable<string>} srcs
|
|
15
|
+
* @returns {string[]}
|
|
16
|
+
*/
|
|
17
|
+
export function filesIn(text, srcs) {
|
|
18
|
+
const results = [];
|
|
19
|
+
for (const src of srcs) {
|
|
20
|
+
if (results.includes(src)) continue;
|
|
21
|
+
const escaped = src.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
22
|
+
const regex = new RegExp(`${escaped}(?![\\w.-])`);
|
|
23
|
+
if (regex.test(text)) {
|
|
24
|
+
results.push(src);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return results;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {string} before
|
|
32
|
+
* @param {string} after
|
|
33
|
+
* @param {Iterable<string>} srcs
|
|
34
|
+
* @returns {string[]}
|
|
35
|
+
*/
|
|
36
|
+
export function newlyUsed(before, after, srcs) {
|
|
37
|
+
const srcArray = Array.from(srcs);
|
|
38
|
+
const beforeSet = new Set(filesIn(before, srcArray));
|
|
39
|
+
return filesIn(after, srcArray).filter((src) => !beforeSet.has(src));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {StoredRecord[]} records
|
|
44
|
+
* @param {string} folder
|
|
45
|
+
* @param {string[]} srcs
|
|
46
|
+
* @param {string[]} pageTags
|
|
47
|
+
* @param {() => number} [random]
|
|
48
|
+
* @returns {RecordChange[]}
|
|
49
|
+
*/
|
|
50
|
+
export function autoTagChanges(records, folder, srcs, pageTags, random) {
|
|
51
|
+
if (pageTags.length === 0 || srcs.length === 0) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const updates = [];
|
|
56
|
+
const seen = new Set();
|
|
57
|
+
for (const src of srcs) {
|
|
58
|
+
if (seen.has(src)) continue;
|
|
59
|
+
seen.add(src);
|
|
60
|
+
const record = recordFor(records, src);
|
|
61
|
+
updates.push({ src, next: inherit(record?.record, src, pageTags) });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (random) {
|
|
65
|
+
return writeRecords(records, folder, updates, random);
|
|
66
|
+
}
|
|
67
|
+
return writeRecords(records, folder, updates);
|
|
68
|
+
}
|