@phis/ui 0.3.0 → 0.4.1
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/components/cms/phi-cms-layout-renderer.js +1 -1
- package/components/controls/phi-collection-header-control.js +1 -1
- package/components/controls/phi-collection-view-control.js +2 -1
- package/components/controls/phi-drawer-control-adapter.d.ts +1 -1
- package/components/controls/phi-drawer-control-adapter.js +2 -2
- package/components/controls/phi-image-control.d.ts +6 -1
- package/components/controls/phi-image-control.js +23 -4
- package/components/controls/phi-lightbox-image-control-adapter.d.ts +2 -0
- package/components/controls/phi-lightbox-image-control-adapter.js +11 -0
- package/components/controls/phi-lightbox-image-control.d.ts +3 -2
- package/components/controls/phi-lightbox-image-control.js +7 -3
- package/components/controls/phi-modal-control-adapter.d.ts +1 -1
- package/components/controls/phi-modal-control-adapter.js +5 -2
- package/components/controls/phi-overlay-control-contract.d.ts +5 -0
- package/components/controls/phi-pagination-control-adapter.d.ts +2 -0
- package/components/controls/phi-pagination-control-adapter.js +15 -0
- package/components/controls/phi-pagination-control.d.ts +2 -1
- package/components/controls/phi-pagination-control.js +11 -7
- package/components/controls/phi-table-control.d.ts +6 -1
- package/components/controls/phi-table-control.js +17 -5
- package/components/controls/phi-timeline-control-adapter.d.ts +2 -0
- package/components/controls/phi-timeline-control-adapter.js +28 -0
- package/components/controls/phi-timeline-control.d.ts +3 -1
- package/components/controls/phi-timeline-control.js +8 -22
- package/components/controls/phi-tour-control.js +25 -7
- package/components/controls/phi-tour-viewport.d.ts +36 -0
- package/components/controls/phi-tour-viewport.js +86 -0
- package/components/controls/phi-zoom-image-overlay.d.ts +17 -0
- package/components/controls/phi-zoom-image-overlay.js +58 -0
- package/components/layouts/layout-definitions.d.ts +2 -20
- package/components/layouts/layout-definitions.js +13 -2
- package/components/layouts/plugins/carousel-layout-plugin.js +2 -1
- package/components/media/aspect-ratios.d.ts +14 -0
- package/components/media/aspect-ratios.js +13 -0
- package/components/motion/phi-sequence-run.d.ts +27 -0
- package/components/motion/phi-sequence-run.js +87 -0
- package/components/motion/phi-sequence-viewport.d.ts +16 -0
- package/components/motion/phi-sequence-viewport.js +97 -34
- package/components/overlays/phi-overlay-container-client.js +5 -2
- package/components/runtime/runtime-visual-anchors.d.ts +13 -2
- package/components/runtime/runtime-visual-anchors.js +49 -3
- package/components/shell/use-phi-shell-sticky-top.d.ts +5 -0
- package/components/shell/use-phi-shell-sticky-top.js +78 -0
- package/components/widgets/config/image-variant-options.d.ts +3 -5
- package/components/widgets/config/image-variant-options.js +11 -11
- package/components/widgets/config/sequence-settings.d.ts +75 -0
- package/components/widgets/config/sequence-settings.js +173 -0
- package/components/widgets/helpers/semantic-html-translation.d.ts +3 -1
- package/components/widgets/helpers/semantic-html-translation.js +8 -3
- package/components/widgets/label-sets/content-entries.d.ts +9 -0
- package/components/widgets/label-sets/content-entries.js +17 -0
- package/components/widgets/label-types/gallery.d.ts +2 -10
- package/components/widgets/label-types/sequence.d.ts +18 -0
- package/components/widgets/label-types/sequence.js +1 -0
- package/components/widgets/navigation-descriptor-resolver.server.js +6 -1
- package/components/widgets/shared/markdown-render.js +15 -5
- package/helpers/cms-layout-defaults.js +9 -2
- package/helpers/cms-region-keys.d.ts +1 -1
- package/helpers/motion.d.ts +7 -0
- package/helpers/motion.js +7 -0
- package/helpers/text-source.d.ts +19 -0
- package/helpers/text-source.js +20 -0
- package/next/config.mjs +1 -1
- package/package.json +1 -1
- package/plugins/runtime-modules/asset/data-providers.d.ts +4 -0
- package/plugins/runtime-modules/asset/data-providers.js +1 -0
- package/plugins/runtime-modules/asset/definition.d.ts +4 -0
- package/plugins/runtime-modules/asset/presets.d.ts +53 -1
- package/plugins/runtime-modules/asset/presets.js +16 -12
- package/plugins/runtime-modules/asset/tour.d.ts +44 -0
- package/plugins/runtime-modules/asset/tour.js +53 -0
- package/plugins/runtime-modules/auth/trees/phi-default-pub-confirm-page-tree.js +3 -0
- package/plugins/runtime-modules/auth/trees/phi-default-pub-login-page-tree.js +2 -0
- package/plugins/runtime-modules/auth/trees/phi-default-pub-registration-page-tree.js +3 -0
- package/plugins/runtime-modules/auth/trees/phi-default-pub-reset-password-page-tree.js +3 -0
- package/plugins/runtime-modules/builder/clients/inspector-config-field.js +42 -18
- package/plugins/runtime-modules/builder/clients/inspector-image-field.js +2 -2
- package/plugins/runtime-modules/builder/clients/widget-inspector.js +9 -0
- package/plugins/runtime-modules/builder/clients/workspace-scope-boundary.js +10 -0
- package/plugins/runtime-modules/builder/controller/workspace-controller.js +18 -3
- package/plugins/runtime-modules/builder/plugin-meta-labels.js +8 -0
- package/plugins/runtime-modules/builder/presets.js +5 -3
- package/plugins/runtime-modules/builder/tour.d.ts +316 -0
- package/plugins/runtime-modules/builder/tour.js +337 -0
- package/plugins/runtime-modules/builder/trees/phi-builder-inspector-area-overlay-tree.server.js +8 -1
- package/plugins/runtime-modules/builder/trees/phi-default-builder-area-preset-tree.js +4 -0
- package/plugins/runtime-modules/builder/widgets/structure-region/built-in.js +3 -2
- package/plugins/runtime-modules/core/widgets/card/server.js +4 -2
- package/plugins/runtime-modules/core/widgets/collection-view/client.js +10 -0
- package/plugins/runtime-modules/core/widgets/description/config.d.ts +28 -1
- package/plugins/runtime-modules/core/widgets/description/config.js +2 -0
- package/plugins/runtime-modules/core/widgets/description/server.js +10 -12
- package/plugins/runtime-modules/core/widgets/entry-detail/config.d.ts +2 -0
- package/plugins/runtime-modules/core/widgets/entry-detail/config.js +8 -1
- package/plugins/runtime-modules/core/widgets/entry-header/config.d.ts +3 -0
- package/plugins/runtime-modules/core/widgets/entry-header/config.js +8 -1
- package/plugins/runtime-modules/core/widgets/entry-list/client.d.ts +4 -1
- package/plugins/runtime-modules/core/widgets/entry-list/client.js +3 -2
- package/plugins/runtime-modules/core/widgets/entry-list/config.d.ts +392 -93
- package/plugins/runtime-modules/core/widgets/entry-list/config.js +21 -3
- package/plugins/runtime-modules/core/widgets/entry-list/server.js +4 -2
- package/plugins/runtime-modules/core/widgets/gallery/authoring.js +62 -10
- package/plugins/runtime-modules/core/widgets/gallery/client.d.ts +6 -1
- package/plugins/runtime-modules/core/widgets/gallery/client.js +7 -79
- package/plugins/runtime-modules/core/widgets/gallery/config.d.ts +337 -144
- package/plugins/runtime-modules/core/widgets/gallery/config.js +52 -113
- package/plugins/runtime-modules/core/widgets/gallery/defaults.d.ts +15 -0
- package/plugins/runtime-modules/core/widgets/gallery/defaults.js +20 -0
- package/plugins/runtime-modules/core/widgets/html/server.js +1 -1
- package/plugins/runtime-modules/core/widgets/image/client.js +1 -1
- package/plugins/runtime-modules/core/widgets/image/config.d.ts +29 -4
- package/plugins/runtime-modules/core/widgets/image/config.js +19 -2
- package/plugins/runtime-modules/core/widgets/image/editor.js +1 -1
- package/plugins/runtime-modules/core/widgets/markdown-toc/server.js +7 -2
- package/plugins/runtime-modules/core/widgets/quick-links/config.d.ts +3 -0
- package/plugins/runtime-modules/core/widgets/quick-links/config.js +2 -0
- package/plugins/runtime-modules/core/widgets/quick-links/plugin.js +4 -2
- package/plugins/runtime-modules/core/widgets/quick-links/server.d.ts +5 -1
- package/plugins/runtime-modules/core/widgets/quick-links/server.js +4 -2
- package/plugins/runtime-modules/core/widgets/result/config.d.ts +25 -1
- package/plugins/runtime-modules/core/widgets/simple-text/config.d.ts +28 -1
- package/plugins/runtime-modules/core/widgets/simple-text/config.js +2 -0
- package/plugins/runtime-modules/core/widgets/simple-text/plugin.js +1 -1
- package/plugins/runtime-modules/core/widgets/simple-text/server.d.ts +4 -1
- package/plugins/runtime-modules/core/widgets/simple-text/server.js +3 -3
- package/plugins/runtime-modules/core/widgets/table/client.js +1 -1
- package/plugins/runtime-modules/core/widgets/table/config.d.ts +80 -3
- package/plugins/runtime-modules/core/widgets/table/config.js +3 -0
- package/plugins/runtime-modules/core/widgets/tour-button/client.js +1 -1
- package/plugins/runtime-modules/phi-default-site-area-preset-tree.js +12 -4
- package/plugins/runtime-modules/public/trees/phi-default-pub-contact-page-tree.js +2 -0
- package/plugins/runtime-modules/public/trees/phi-default-pub-unsubscribe-page-tree.js +3 -0
- package/plugins/runtime-modules/public/trees/phi-default-pub-welcome-page-tree.js +2 -1
- package/plugins/runtime-modules/revisions/presets.d.ts +53 -2
- package/plugins/runtime-modules/revisions/presets.js +16 -12
- package/plugins/runtime-modules/revisions/tour.d.ts +43 -0
- package/plugins/runtime-modules/revisions/tour.js +51 -0
- package/plugins/runtime-modules/revisions/trees/phi-default-builder-revisions-page-tree.js +4 -0
- package/plugins/runtime-modules/theme/presets.d.ts +36 -4
- package/plugins/runtime-modules/theme/tour.d.ts +45 -11
- package/plugins/runtime-modules/theme/tour.js +39 -8
- package/plugins/runtime-modules/tour/controller/address.js +2 -2
- package/plugins/runtime-modules/tour/controller/client.js +43 -2
- package/plugins/runtime-modules/tour/controller/definition.js +2 -1
- package/server-helpers/cms-request.js +1 -0
- package/server-helpers/page-tour.d.ts +10 -3
- package/server-helpers/page-tour.js +25 -12
- package/server-helpers/translate.d.ts +9 -0
- package/server-helpers/translate.js +14 -0
- package/styles/controls.css +15 -1
- package/styles/root.css +20 -19
- package/types/cms-config.d.ts +3 -1
- package/types/cms-config.js +3 -3
- package/types/cms-plugins.d.ts +31 -4
- package/types/core-widget-placements.d.ts +5 -0
- package/types/table-widget.d.ts +2 -0
- package/types/tour-controller.d.ts +10 -0
- package/types/tour-controller.js +13 -0
- package/types/tours.d.ts +27 -25
- package/types/tours.js +9 -23
- package/types/visual-anchors.d.ts +15 -0
- package/types/visual-anchors.js +12 -0
- package/types.d.ts +2 -0
- package/types.js +4 -1
|
@@ -127,6 +127,11 @@ export async function resolvePhiDescriptorNavigationItems(runtime, navKey, overl
|
|
|
127
127
|
/*
|
|
128
128
|
* One batch per source language. A Module's label names its language and is translated from it; an
|
|
129
129
|
* operator's names none and is read in the Site's source language, which the server supplies.
|
|
130
|
+
*
|
|
131
|
+
* The batch also decides the store. A Module's label is the platform's text, the same on every Site,
|
|
132
|
+
* so it is translated once in the global store, which only the operator edits. A Site that wants
|
|
133
|
+
* other words renames the entry, the label loses its `sourceLocale`, and from then on it is the
|
|
134
|
+
* Site's own text in the Site's store. The Builder's surfaces carry Module labels only.
|
|
130
135
|
*/
|
|
131
136
|
await Promise.all([...collectNavigationLabels(surface.items)].map(async ([sourceLocale, group]) => {
|
|
132
137
|
const labels = [...group];
|
|
@@ -136,7 +141,7 @@ export async function resolvePhiDescriptorNavigationItems(runtime, navKey, overl
|
|
|
136
141
|
locale: runtime.locale.current,
|
|
137
142
|
...(sourceLocale ? { sourceLocale } : {}),
|
|
138
143
|
};
|
|
139
|
-
const translator = runtime.area === "builder"
|
|
144
|
+
const translator = sourceLocale || runtime.area === "builder"
|
|
140
145
|
? createGlobalTranslator(options)
|
|
141
146
|
: createSiteTranslator({ ...options, siteKey: rt.siteKey });
|
|
142
147
|
/*
|
|
@@ -2,7 +2,7 @@ import { cache } from "react";
|
|
|
2
2
|
import { unified } from "unified";
|
|
3
3
|
import remarkParse from "remark-parse";
|
|
4
4
|
import remarkGfm from "remark-gfm";
|
|
5
|
-
import { trBulk } from "../../../server-helpers/translate";
|
|
5
|
+
import { trBulk, trBulkForLocale } from "../../../server-helpers/translate";
|
|
6
6
|
import { resolvePhiCodeLanguage, tokenizePhiCode } from "./markdown-code-tokens";
|
|
7
7
|
import { readPhiInternalReference } from "../../../types/references";
|
|
8
8
|
import { resolvePhiWidgetInternalReferences } from "../helpers/internal-reference-resolver.server";
|
|
@@ -294,11 +294,21 @@ function parseTranslatedMarkdownInlines(translatedHtml, metadata) {
|
|
|
294
294
|
appendText(translatedHtml.slice(cursor));
|
|
295
295
|
return stack.length === 1 ? stack[0]?.children ?? [] : null;
|
|
296
296
|
}
|
|
297
|
-
|
|
297
|
+
/*
|
|
298
|
+
* The render's own locale where there is one. A static render has no request to ask: `trBulk` reads the
|
|
299
|
+
* locale from the request headers, finds none, and answers in the Site's default -- which left every
|
|
300
|
+
* static Public page's body in its source language under a translated header.
|
|
301
|
+
*/
|
|
302
|
+
function translateUnits(locale, msgs, format, sourceLocale) {
|
|
303
|
+
return locale
|
|
304
|
+
? trBulkForLocale(locale, msgs, 0, format, sourceLocale)
|
|
305
|
+
: trBulk(msgs, 0, format, sourceLocale);
|
|
306
|
+
}
|
|
307
|
+
async function buildMarkdownInlineMap(tree, translate, sourceLocale, locale) {
|
|
298
308
|
const units = [];
|
|
299
309
|
collectMarkdownTranslationUnits(tree, units);
|
|
300
310
|
const translated = translate && units.length > 0
|
|
301
|
-
? await
|
|
311
|
+
? await translateUnits(locale, units.map((unit) => unit.html), "html", sourceLocale)
|
|
302
312
|
: units.map((unit) => unit.html);
|
|
303
313
|
/*
|
|
304
314
|
* Image titles are prose and translate as plain text, each on its own: they are separate sentences
|
|
@@ -307,7 +317,7 @@ async function buildMarkdownInlineMap(tree, translate, sourceLocale) {
|
|
|
307
317
|
*/
|
|
308
318
|
const titleSlots = units.flatMap((unit) => unit.metadata.titles.flatMap((title, index) => title ? [{ metadata: unit.metadata, index, title }] : []));
|
|
309
319
|
if (translate && titleSlots.length > 0) {
|
|
310
|
-
const translatedTitles = await
|
|
320
|
+
const translatedTitles = await translateUnits(locale, titleSlots.map((slot) => slot.title), "text", sourceLocale);
|
|
311
321
|
titleSlots.forEach((slot, index) => {
|
|
312
322
|
slot.metadata.titles[slot.index] = translatedTitles[index] || slot.title;
|
|
313
323
|
});
|
|
@@ -508,7 +518,7 @@ async function resolveMarkdownRenderDataFromSource(markdown, translate, headingI
|
|
|
508
518
|
headingIdPrefix,
|
|
509
519
|
headingOffset,
|
|
510
520
|
};
|
|
511
|
-
const inlineMap = await buildMarkdownInlineMap(tree, translate, sourceLocale);
|
|
521
|
+
const inlineMap = await buildMarkdownInlineMap(tree, translate, sourceLocale, runtime?.locale.current);
|
|
512
522
|
const blocks = mapBlockNodes(tree.children, context, inlineMap);
|
|
513
523
|
return { blocks, headings: context.headings };
|
|
514
524
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PHI_SPACE } from "../theme/antd-css-var-contract";
|
|
2
2
|
import { PHI_RENDERABLE_BLOCK_DEFAULT_ANCHOR } from "./renderable-block-defaults";
|
|
3
3
|
import { isPhiRecord } from "./is-record";
|
|
4
|
+
import { PHI_SEQUENCE_DEFAULT_DURATION_MS, PHI_SEQUENCE_DEFAULT_EASING } from "./motion";
|
|
4
5
|
import { PHI_SURFACE_CARD } from "./surface-presets";
|
|
5
6
|
import { PHI_GRID_LAYOUT_DEFAULT_COLUMNS } from "../components/layouts/phi-grid-contract";
|
|
6
7
|
import { PHI_MASONRY_LAYOUT_DEFAULT_COLUMNS } from "../components/layouts/phi-masonry-contract";
|
|
@@ -119,9 +120,13 @@ const PHI_STACK_LAYOUT_PANEL_PRESET = {
|
|
|
119
120
|
};
|
|
120
121
|
const PHI_CAROUSEL_LAYOUT_DEFAULTS = {
|
|
121
122
|
// A slide is what tells somebody the slots are a run rather than a set, so it is the opening
|
|
122
|
-
// impression rather than a preference to be discovered.
|
|
123
|
-
//
|
|
123
|
+
// impression rather than a preference to be discovered. The window shows one slot until
|
|
124
|
+
// somebody widens it. The pace is the sequence's own, stated so the Inspector shows the number
|
|
125
|
+
// in effect rather than an empty box.
|
|
124
126
|
transition: "slide",
|
|
127
|
+
transitionDurationMs: PHI_SEQUENCE_DEFAULT_DURATION_MS,
|
|
128
|
+
transitionEasing: PHI_SEQUENCE_DEFAULT_EASING,
|
|
129
|
+
slotGap: "none",
|
|
125
130
|
visibleSlots: 1,
|
|
126
131
|
windowAnchor: "start",
|
|
127
132
|
/*
|
|
@@ -134,6 +139,8 @@ const PHI_CAROUSEL_LAYOUT_DEFAULTS = {
|
|
|
134
139
|
*/
|
|
135
140
|
controls: "arrows",
|
|
136
141
|
loop: false,
|
|
142
|
+
autoplayMs: 0,
|
|
143
|
+
lookahead: 1,
|
|
137
144
|
};
|
|
138
145
|
const PHI_CAROUSEL_LAYOUT_PANEL_PRESET = {
|
|
139
146
|
padding: PHI_SPACE.base,
|
|
@@ -33,5 +33,5 @@ export declare function resolvePhiCmsRegionOwnership(regionKey: string | null |
|
|
|
33
33
|
* know is not a placement anybody chose -- refusing it would hide Widgets over a typo.
|
|
34
34
|
*/
|
|
35
35
|
export declare function phiCmsRegionAcceptsWidget(regionKey: string | null | undefined, required: PhiCmsRegionOwnership | null | undefined): boolean;
|
|
36
|
-
export declare function resolvePhiCmsRegionType(regionKey: string | null | undefined): 10 | 40 |
|
|
36
|
+
export declare function resolvePhiCmsRegionType(regionKey: string | null | undefined): 10 | 40 | 12 | 50 | 11 | 20 | 21 | 25 | 30 | 39 | 41;
|
|
37
37
|
export declare function resolvePhiCmsRegionKey(regionType: number | null | undefined): "content" | "header_top" | "header_main" | "header_bottom" | "sider_left" | "sider_right" | "hero" | "footer_top" | "footer_main" | "footer_bottom" | "drawer_right" | undefined;
|
package/helpers/motion.d.ts
CHANGED
|
@@ -43,6 +43,13 @@ export declare const PHI_SEQUENCE_TRANSITION_MAX_MS = 600000;
|
|
|
43
43
|
* exactly, so the grid the spinner walks holds every value it offers.
|
|
44
44
|
*/
|
|
45
45
|
export declare const PHI_SEQUENCE_TRANSITION_STEP_MS = 100;
|
|
46
|
+
/**
|
|
47
|
+
* What a sequence that says nothing moves at: long enough to read as movement, short enough to
|
|
48
|
+
* ignore. Exported so that a Widget can state it as its default and the Inspector shows the number
|
|
49
|
+
* that is actually in effect instead of an empty box.
|
|
50
|
+
*/
|
|
51
|
+
export declare const PHI_SEQUENCE_DEFAULT_DURATION_MS = 320;
|
|
52
|
+
export declare const PHI_SEQUENCE_DEFAULT_EASING: PhiMotionEasing;
|
|
46
53
|
/**
|
|
47
54
|
* The two ways of showing a sequence, and which end of a wider window an index means.
|
|
48
55
|
*
|
package/helpers/motion.js
CHANGED
|
@@ -58,6 +58,13 @@ export const PHI_SEQUENCE_TRANSITION_MAX_MS = 600_000;
|
|
|
58
58
|
* exactly, so the grid the spinner walks holds every value it offers.
|
|
59
59
|
*/
|
|
60
60
|
export const PHI_SEQUENCE_TRANSITION_STEP_MS = 100;
|
|
61
|
+
/**
|
|
62
|
+
* What a sequence that says nothing moves at: long enough to read as movement, short enough to
|
|
63
|
+
* ignore. Exported so that a Widget can state it as its default and the Inspector shows the number
|
|
64
|
+
* that is actually in effect instead of an empty box.
|
|
65
|
+
*/
|
|
66
|
+
export const PHI_SEQUENCE_DEFAULT_DURATION_MS = 320;
|
|
67
|
+
export const PHI_SEQUENCE_DEFAULT_EASING = "ease-in-out";
|
|
61
68
|
/**
|
|
62
69
|
* The two ways of showing a sequence, and which end of a wider window an index means.
|
|
63
70
|
*
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whose words a text Widget holds, which decides the store that translates them.
|
|
3
|
+
*
|
|
4
|
+
* A Preset's text is the platform's: the same sentence on every installation, translated once in the
|
|
5
|
+
* global store, which only the operator edits. Text an author wrote on a Site is the Site's and goes to
|
|
6
|
+
* the Site's store. The Preset says so by writing `textSource: "preset"` beside its words; the Builder
|
|
7
|
+
* drops the marker the moment an author changes one of the Widget's texts, and from then on all of them
|
|
8
|
+
* are the Site's.
|
|
9
|
+
*
|
|
10
|
+
* A marker rather than a translation done by the Preset itself: adopting a Preset stores the tree it
|
|
11
|
+
* built, so a Preset that translated its words would freeze one locale into the Site's copy.
|
|
12
|
+
*/
|
|
13
|
+
export declare const PHI_TEXT_SOURCE_PRESET: "preset";
|
|
14
|
+
export type PhiTextSource = typeof PHI_TEXT_SOURCE_PRESET;
|
|
15
|
+
export declare function readPhiTextSource(value: unknown): PhiTextSource | undefined;
|
|
16
|
+
/** Whether a Builder edit of this field ends the Preset's ownership of the Widget's words. */
|
|
17
|
+
export declare function isPhiAuthoredTextField(field: {
|
|
18
|
+
type?: string;
|
|
19
|
+
}): boolean;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whose words a text Widget holds, which decides the store that translates them.
|
|
3
|
+
*
|
|
4
|
+
* A Preset's text is the platform's: the same sentence on every installation, translated once in the
|
|
5
|
+
* global store, which only the operator edits. Text an author wrote on a Site is the Site's and goes to
|
|
6
|
+
* the Site's store. The Preset says so by writing `textSource: "preset"` beside its words; the Builder
|
|
7
|
+
* drops the marker the moment an author changes one of the Widget's texts, and from then on all of them
|
|
8
|
+
* are the Site's.
|
|
9
|
+
*
|
|
10
|
+
* A marker rather than a translation done by the Preset itself: adopting a Preset stores the tree it
|
|
11
|
+
* built, so a Preset that translated its words would freeze one locale into the Site's copy.
|
|
12
|
+
*/
|
|
13
|
+
export const PHI_TEXT_SOURCE_PRESET = "preset";
|
|
14
|
+
export function readPhiTextSource(value) {
|
|
15
|
+
return value === PHI_TEXT_SOURCE_PRESET ? value : undefined;
|
|
16
|
+
}
|
|
17
|
+
/** Whether a Builder edit of this field ends the Preset's ownership of the Widget's words. */
|
|
18
|
+
export function isPhiAuthoredTextField(field) {
|
|
19
|
+
return field.type === "string";
|
|
20
|
+
}
|
package/next/config.mjs
CHANGED
|
@@ -57,7 +57,7 @@ function customRenders(antd, h, Fragment) {
|
|
|
57
57
|
Form: (Form) => h(Form, null, h(Form.Item, { label: "Label" }, h(antd.Input))),
|
|
58
58
|
Dropdown: (Dropdown) => h(Dropdown, { menu: { items: [] } }, h("div")),
|
|
59
59
|
Menu: (Menu) => h(Menu, { items: [] }),
|
|
60
|
-
QRCode: (QRCode) => h(QRCode, { value: "https://
|
|
60
|
+
QRCode: (QRCode) => h(QRCode, { value: "https://phis.dev" }),
|
|
61
61
|
Tree: (Tree) => h(Tree, { treeData: [] }),
|
|
62
62
|
Tag: (Tag) => h(Fragment, null, h(Tag, { color: "blue" }, "Tag"), h(Tag, { color: "success" }, "Tag")),
|
|
63
63
|
Badge: (Badge) => h(Fragment, null, h(Badge), h(Badge.Ribbon)),
|
package/package.json
CHANGED
|
@@ -25,6 +25,7 @@ export declare const PHI_ASSET_RUNTIME_DATA_PROVIDER_DESCRIPTORS: ({
|
|
|
25
25
|
settingsFields: ({
|
|
26
26
|
key: string;
|
|
27
27
|
type: "choice";
|
|
28
|
+
heading: string;
|
|
28
29
|
label: string;
|
|
29
30
|
optionsProvider: {
|
|
30
31
|
providerKey: `${string}/${string}`;
|
|
@@ -38,6 +39,7 @@ export declare const PHI_ASSET_RUNTIME_DATA_PROVIDER_DESCRIPTORS: ({
|
|
|
38
39
|
type: "boolean";
|
|
39
40
|
label: string;
|
|
40
41
|
description: string;
|
|
42
|
+
heading?: undefined;
|
|
41
43
|
optionsProvider?: undefined;
|
|
42
44
|
options?: undefined;
|
|
43
45
|
min?: undefined;
|
|
@@ -50,6 +52,7 @@ export declare const PHI_ASSET_RUNTIME_DATA_PROVIDER_DESCRIPTORS: ({
|
|
|
50
52
|
value: string;
|
|
51
53
|
label: string;
|
|
52
54
|
}[];
|
|
55
|
+
heading?: undefined;
|
|
53
56
|
optionsProvider?: undefined;
|
|
54
57
|
description?: undefined;
|
|
55
58
|
min?: undefined;
|
|
@@ -60,6 +63,7 @@ export declare const PHI_ASSET_RUNTIME_DATA_PROVIDER_DESCRIPTORS: ({
|
|
|
60
63
|
label: string;
|
|
61
64
|
min: number;
|
|
62
65
|
max: number;
|
|
66
|
+
heading?: undefined;
|
|
63
67
|
optionsProvider?: undefined;
|
|
64
68
|
description?: undefined;
|
|
65
69
|
options?: undefined;
|
|
@@ -32,6 +32,7 @@ export declare const PHI_ASSET_RUNTIME_MODULE_DEFINITION: {
|
|
|
32
32
|
settingsFields: ({
|
|
33
33
|
key: string;
|
|
34
34
|
type: "choice";
|
|
35
|
+
heading: string;
|
|
35
36
|
label: string;
|
|
36
37
|
optionsProvider: {
|
|
37
38
|
providerKey: `${string}/${string}`;
|
|
@@ -45,6 +46,7 @@ export declare const PHI_ASSET_RUNTIME_MODULE_DEFINITION: {
|
|
|
45
46
|
type: "boolean";
|
|
46
47
|
label: string;
|
|
47
48
|
description: string;
|
|
49
|
+
heading?: undefined;
|
|
48
50
|
optionsProvider?: undefined;
|
|
49
51
|
options?: undefined;
|
|
50
52
|
min?: undefined;
|
|
@@ -57,6 +59,7 @@ export declare const PHI_ASSET_RUNTIME_MODULE_DEFINITION: {
|
|
|
57
59
|
value: string;
|
|
58
60
|
label: string;
|
|
59
61
|
}[];
|
|
62
|
+
heading?: undefined;
|
|
60
63
|
optionsProvider?: undefined;
|
|
61
64
|
description?: undefined;
|
|
62
65
|
min?: undefined;
|
|
@@ -67,6 +70,7 @@ export declare const PHI_ASSET_RUNTIME_MODULE_DEFINITION: {
|
|
|
67
70
|
label: string;
|
|
68
71
|
min: number;
|
|
69
72
|
max: number;
|
|
73
|
+
heading?: undefined;
|
|
70
74
|
optionsProvider?: undefined;
|
|
71
75
|
description?: undefined;
|
|
72
76
|
options?: undefined;
|
|
@@ -1,2 +1,54 @@
|
|
|
1
1
|
import type { PhiCmsRoutePresetDescriptor } from "../../../types/cms-module-descriptors";
|
|
2
|
-
export declare const PHI_ASSET_RUNTIME_MODULE_ROUTES: PhiCmsRoutePresetDescriptor
|
|
2
|
+
export declare const PHI_ASSET_RUNTIME_MODULE_ROUTES: (PhiCmsRoutePresetDescriptor | {
|
|
3
|
+
tour: {
|
|
4
|
+
readonly id: "@phis/ui/modules/asset/media";
|
|
5
|
+
readonly version: 1;
|
|
6
|
+
readonly steps: readonly [{
|
|
7
|
+
readonly key: "find";
|
|
8
|
+
readonly target: `cms:${import("@phis/contracts/cms").PhiCmsInstanceId}:filters`;
|
|
9
|
+
readonly title: "Finding an asset";
|
|
10
|
+
readonly description: "Kind narrows the library to images or other files, Flags to assets marked Featured, Locked, Mask or Background, Folder to one folder. The plus beside Folder creates a new folder. The search looks through file names, titles and alternative texts.";
|
|
11
|
+
readonly placement: "bottom";
|
|
12
|
+
readonly ifMissing: "skip";
|
|
13
|
+
}, {
|
|
14
|
+
readonly key: "upload";
|
|
15
|
+
readonly target: `cms:${import("@phis/contracts/cms").PhiCmsInstanceId}:toolbar`;
|
|
16
|
+
readonly title: "Adding files";
|
|
17
|
+
readonly description: "Upload opens a field to drop files into. They arrive in the folder and with the flags chosen in the filters, so choosing the folder first saves sorting later. Reset clears the filters, Reload fetches the library again.";
|
|
18
|
+
readonly placement: "bottom";
|
|
19
|
+
readonly ifMissing: "skip";
|
|
20
|
+
}, {
|
|
21
|
+
readonly key: "library";
|
|
22
|
+
readonly target: `cms:${import("@phis/contracts/cms").PhiCmsInstanceId}:items`;
|
|
23
|
+
readonly title: "The Site's library";
|
|
24
|
+
readonly description: "Every image and file the Site has, newest first. It is the same library every image picker of the Site opens, so what is uploaded here can be placed anywhere.";
|
|
25
|
+
readonly placement: "top";
|
|
26
|
+
}, {
|
|
27
|
+
readonly key: "inspector";
|
|
28
|
+
readonly target: `cms:${import("@phis/contracts/cms").PhiCmsInstanceId}:items`;
|
|
29
|
+
readonly title: "What an asset says about itself";
|
|
30
|
+
readonly description: "A click on an asset opens its Inspector: its folder, its title, the alternative text read out to those who cannot see it, its flags, and the focal rectangle -- the part of a picture that stays in view when a variant cuts it to another shape. Saved there, it applies wherever the asset is used.";
|
|
31
|
+
readonly placement: "top";
|
|
32
|
+
}, {
|
|
33
|
+
readonly key: "pages";
|
|
34
|
+
readonly target: `cms:${import("@phis/contracts/cms").PhiCmsInstanceId}:pagination`;
|
|
35
|
+
readonly title: "Page by page";
|
|
36
|
+
readonly description: "The library is shown a page at a time; how many fit on one is chosen here.";
|
|
37
|
+
readonly placement: "top";
|
|
38
|
+
readonly ifMissing: "skip";
|
|
39
|
+
}];
|
|
40
|
+
};
|
|
41
|
+
ownerModuleId: import("../contracts").PhiRuntimeModuleId;
|
|
42
|
+
presetKey: string;
|
|
43
|
+
presetVersion: number;
|
|
44
|
+
area: import("@phis/contracts/cms").PhiCmsAreaKey;
|
|
45
|
+
path: string;
|
|
46
|
+
landingPage?: true;
|
|
47
|
+
title: string;
|
|
48
|
+
defaultPageFlags?: number;
|
|
49
|
+
loadTree: (context: import("../../../types").PhiCmsDescriptorBuildContext & {
|
|
50
|
+
activeModuleIds: ReadonlySet<import("../contracts").PhiRuntimeModuleId>;
|
|
51
|
+
params: Readonly<Record<string, string>>;
|
|
52
|
+
}) => import("../../../types").PhiResolvedCmsPageTree | Promise<import("../../../types").PhiResolvedCmsPageTree>;
|
|
53
|
+
navigation?: readonly import("../../../types").PhiCmsNavigationInjectionDescriptor[];
|
|
54
|
+
})[];
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import { PHI_ASSET_RUNTIME_MODULE_ID } from "./ids";
|
|
2
2
|
import { buildPhiSidebarRoutePresetDescriptor } from "../sidebar-route";
|
|
3
|
+
import { PHI_ASSET_MEDIA_TOUR } from "./tour";
|
|
3
4
|
export const PHI_ASSET_RUNTIME_MODULE_ROUTES = [
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
{
|
|
6
|
+
...buildPhiSidebarRoutePresetDescriptor({
|
|
7
|
+
area: "builder",
|
|
8
|
+
anchor: "main",
|
|
9
|
+
ownerModuleId: PHI_ASSET_RUNTIME_MODULE_ID,
|
|
10
|
+
presetKey: "builder-media-page",
|
|
11
|
+
title: "Media",
|
|
12
|
+
path: "/media",
|
|
13
|
+
itemKey: "@phis/ui/modules/asset/nav/builder/media",
|
|
14
|
+
icon: "antd:picture",
|
|
15
|
+
loadTree: ({ page, runtime }) => import("./trees/phi-default-builder-media-page-tree")
|
|
16
|
+
.then((module) => module.buildPhiDefaultBuilderMediaPageTree({ page, runtime })),
|
|
17
|
+
}),
|
|
18
|
+
tour: PHI_ASSET_MEDIA_TOUR,
|
|
19
|
+
},
|
|
16
20
|
buildPhiSidebarRoutePresetDescriptor({
|
|
17
21
|
area: "admin",
|
|
18
22
|
anchor: "settings",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Media Tour. What the Page does not say is that it is the one library every picker of the Site
|
|
3
|
+
* draws from, and that what is set on an asset here -- its words, its folder, its focal rectangle -- goes
|
|
4
|
+
* wherever the asset is used. The Inspector is described rather than opened: it opens on an asset, and a
|
|
5
|
+
* Tour cannot know which assets a Site has.
|
|
6
|
+
*/
|
|
7
|
+
export declare const PHI_ASSET_MEDIA_TOUR: {
|
|
8
|
+
readonly id: "@phis/ui/modules/asset/media";
|
|
9
|
+
readonly version: 1;
|
|
10
|
+
readonly steps: readonly [{
|
|
11
|
+
readonly key: "find";
|
|
12
|
+
readonly target: `cms:${import("@phis/contracts/cms").PhiCmsInstanceId}:filters`;
|
|
13
|
+
readonly title: "Finding an asset";
|
|
14
|
+
readonly description: "Kind narrows the library to images or other files, Flags to assets marked Featured, Locked, Mask or Background, Folder to one folder. The plus beside Folder creates a new folder. The search looks through file names, titles and alternative texts.";
|
|
15
|
+
readonly placement: "bottom";
|
|
16
|
+
readonly ifMissing: "skip";
|
|
17
|
+
}, {
|
|
18
|
+
readonly key: "upload";
|
|
19
|
+
readonly target: `cms:${import("@phis/contracts/cms").PhiCmsInstanceId}:toolbar`;
|
|
20
|
+
readonly title: "Adding files";
|
|
21
|
+
readonly description: "Upload opens a field to drop files into. They arrive in the folder and with the flags chosen in the filters, so choosing the folder first saves sorting later. Reset clears the filters, Reload fetches the library again.";
|
|
22
|
+
readonly placement: "bottom";
|
|
23
|
+
readonly ifMissing: "skip";
|
|
24
|
+
}, {
|
|
25
|
+
readonly key: "library";
|
|
26
|
+
readonly target: `cms:${import("@phis/contracts/cms").PhiCmsInstanceId}:items`;
|
|
27
|
+
readonly title: "The Site's library";
|
|
28
|
+
readonly description: "Every image and file the Site has, newest first. It is the same library every image picker of the Site opens, so what is uploaded here can be placed anywhere.";
|
|
29
|
+
readonly placement: "top";
|
|
30
|
+
}, {
|
|
31
|
+
readonly key: "inspector";
|
|
32
|
+
readonly target: `cms:${import("@phis/contracts/cms").PhiCmsInstanceId}:items`;
|
|
33
|
+
readonly title: "What an asset says about itself";
|
|
34
|
+
readonly description: "A click on an asset opens its Inspector: its folder, its title, the alternative text read out to those who cannot see it, its flags, and the focal rectangle -- the part of a picture that stays in view when a variant cuts it to another shape. Saved there, it applies wherever the asset is used.";
|
|
35
|
+
readonly placement: "top";
|
|
36
|
+
}, {
|
|
37
|
+
readonly key: "pages";
|
|
38
|
+
readonly target: `cms:${import("@phis/contracts/cms").PhiCmsInstanceId}:pagination`;
|
|
39
|
+
readonly title: "Page by page";
|
|
40
|
+
readonly description: "The library is shown a page at a time; how many fit on one is chosen here.";
|
|
41
|
+
readonly placement: "top";
|
|
42
|
+
readonly ifMissing: "skip";
|
|
43
|
+
}];
|
|
44
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { PHI_ASSET_RUNTIME_MODULE_ID } from "./ids";
|
|
2
|
+
import { PHI_ASSET_MEDIA_PAGE_WIDGET_IDS } from "./media-page-ids";
|
|
3
|
+
const LIBRARY_ID = PHI_ASSET_MEDIA_PAGE_WIDGET_IDS.widgetMediaPreview;
|
|
4
|
+
/**
|
|
5
|
+
* The Media Tour. What the Page does not say is that it is the one library every picker of the Site
|
|
6
|
+
* draws from, and that what is set on an asset here -- its words, its folder, its focal rectangle -- goes
|
|
7
|
+
* wherever the asset is used. The Inspector is described rather than opened: it opens on an asset, and a
|
|
8
|
+
* Tour cannot know which assets a Site has.
|
|
9
|
+
*/
|
|
10
|
+
export const PHI_ASSET_MEDIA_TOUR = {
|
|
11
|
+
id: `${PHI_ASSET_RUNTIME_MODULE_ID}/media`,
|
|
12
|
+
version: 1,
|
|
13
|
+
steps: [
|
|
14
|
+
{
|
|
15
|
+
key: "find",
|
|
16
|
+
target: `cms:${LIBRARY_ID}:filters`,
|
|
17
|
+
title: "Finding an asset",
|
|
18
|
+
description: "Kind narrows the library to images or other files, Flags to assets marked Featured, Locked, Mask or Background, Folder to one folder. The plus beside Folder creates a new folder. The search looks through file names, titles and alternative texts.",
|
|
19
|
+
placement: "bottom",
|
|
20
|
+
ifMissing: "skip",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
key: "upload",
|
|
24
|
+
target: `cms:${LIBRARY_ID}:toolbar`,
|
|
25
|
+
title: "Adding files",
|
|
26
|
+
description: "Upload opens a field to drop files into. They arrive in the folder and with the flags chosen in the filters, so choosing the folder first saves sorting later. Reset clears the filters, Reload fetches the library again.",
|
|
27
|
+
placement: "bottom",
|
|
28
|
+
ifMissing: "skip",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
key: "library",
|
|
32
|
+
target: `cms:${LIBRARY_ID}:items`,
|
|
33
|
+
title: "The Site's library",
|
|
34
|
+
description: "Every image and file the Site has, newest first. It is the same library every image picker of the Site opens, so what is uploaded here can be placed anywhere.",
|
|
35
|
+
placement: "top",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
key: "inspector",
|
|
39
|
+
target: `cms:${LIBRARY_ID}:items`,
|
|
40
|
+
title: "What an asset says about itself",
|
|
41
|
+
description: "A click on an asset opens its Inspector: its folder, its title, the alternative text read out to those who cannot see it, its flags, and the focal rectangle -- the part of a picture that stays in view when a variant cuts it to another shape. Saved there, it applies wherever the asset is used.",
|
|
42
|
+
placement: "top",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
key: "pages",
|
|
46
|
+
target: `cms:${LIBRARY_ID}:pagination`,
|
|
47
|
+
title: "Page by page",
|
|
48
|
+
description: "The library is shown a page at a time; how many fit on one is chosen here.",
|
|
49
|
+
placement: "top",
|
|
50
|
+
ifMissing: "skip",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PHI_TEXT_SOURCE_PRESET } from "../../../../helpers/text-source";
|
|
1
2
|
import { createPhiPresetCmsInstanceIdMap } from "../../../../types/cms-instance-id";
|
|
2
3
|
import { PHI_SPACE } from "../../../../theme/antd-css-var-contract";
|
|
3
4
|
import { PHI_AUTH_RUNTIME_MODULE_ID } from "../ids";
|
|
@@ -97,6 +98,7 @@ export async function buildPhiDefaultPubConfirmPageTree({ page, presetKey, }) {
|
|
|
97
98
|
sortOrder: 0,
|
|
98
99
|
label: "pub confirmation description widget",
|
|
99
100
|
config: {
|
|
101
|
+
textSource: PHI_TEXT_SOURCE_PRESET,
|
|
100
102
|
eyebrow: "Confirm Email",
|
|
101
103
|
title: "Activate your account",
|
|
102
104
|
description: "Review your registration details and confirm your email address to activate access.",
|
|
@@ -160,6 +162,7 @@ export async function buildPhiDefaultPubConfirmPageTree({ page, presetKey, }) {
|
|
|
160
162
|
slotIndex: 2,
|
|
161
163
|
label: "pub confirmation missing token",
|
|
162
164
|
config: {
|
|
165
|
+
textSource: PHI_TEXT_SOURCE_PRESET,
|
|
163
166
|
text: "No confirmation token was provided. Open the confirmation link from your email.",
|
|
164
167
|
tone: "secondary",
|
|
165
168
|
visibleWhen: withoutToken,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PHI_TEXT_SOURCE_PRESET } from "../../../../helpers/text-source";
|
|
1
2
|
import { createPhiPresetCmsInstanceIdMap } from "../../../../types/cms-instance-id";
|
|
2
3
|
import { PHI_SPACE } from "../../../../theme/antd-css-var-contract";
|
|
3
4
|
import { PHI_LAYOUT } from "../../../../theme/phi-tokens";
|
|
@@ -98,6 +99,7 @@ export async function buildPhiDefaultPubLoginPageTree({ page, presetKey, runtime
|
|
|
98
99
|
sortOrder: 0,
|
|
99
100
|
label: "pub login description widget",
|
|
100
101
|
config: {
|
|
102
|
+
textSource: PHI_TEXT_SOURCE_PRESET,
|
|
101
103
|
eyebrow: "Login",
|
|
102
104
|
title: "Sign in to your account",
|
|
103
105
|
description: "Access your customer workspace, orders, and account details.",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PHI_TEXT_SOURCE_PRESET } from "../../../../helpers/text-source";
|
|
1
2
|
import { createPhiPresetCmsInstanceIdMap } from "../../../../types/cms-instance-id";
|
|
2
3
|
import { PHI_SPACE } from "../../../../theme/antd-css-var-contract";
|
|
3
4
|
import { PHI_AUTH_RUNTIME_MODULE_ID } from "../ids";
|
|
@@ -68,6 +69,7 @@ export async function buildPhiDefaultPubRegistrationPageTree({ page, presetKey,
|
|
|
68
69
|
sortOrder: 0,
|
|
69
70
|
label: "pub registration description widget",
|
|
70
71
|
config: {
|
|
72
|
+
textSource: PHI_TEXT_SOURCE_PRESET,
|
|
71
73
|
eyebrow: "Register",
|
|
72
74
|
title: "Create account",
|
|
73
75
|
description: "Create your account to access your personalized workspace.",
|
|
@@ -113,6 +115,7 @@ export async function buildPhiDefaultPubRegistrationPageTree({ page, presetKey,
|
|
|
113
115
|
slotIndex: 1,
|
|
114
116
|
label: "pub registration notice",
|
|
115
117
|
config: {
|
|
118
|
+
textSource: PHI_TEXT_SOURCE_PRESET,
|
|
116
119
|
text: "We only use your details to create and support your account.",
|
|
117
120
|
tone: "secondary",
|
|
118
121
|
},
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PHI_TEXT_SOURCE_PRESET } from "../../../../helpers/text-source";
|
|
1
2
|
import { createPhiPresetCmsInstanceIdMap } from "../../../../types/cms-instance-id";
|
|
2
3
|
import { PHI_SPACE } from "../../../../theme/antd-css-var-contract";
|
|
3
4
|
import { PHI_AUTH_RUNTIME_MODULE_ID } from "../ids";
|
|
@@ -89,6 +90,7 @@ export async function buildPhiDefaultPubResetPasswordPageTree({ page, presetKey,
|
|
|
89
90
|
sortOrder: 0,
|
|
90
91
|
label: "pub reset password description widget",
|
|
91
92
|
config: {
|
|
93
|
+
textSource: PHI_TEXT_SOURCE_PRESET,
|
|
92
94
|
eyebrow: "Reset password",
|
|
93
95
|
title: "Choose a new password",
|
|
94
96
|
description: "Use the reset link from your email to choose a new secure password.",
|
|
@@ -113,6 +115,7 @@ export async function buildPhiDefaultPubResetPasswordPageTree({ page, presetKey,
|
|
|
113
115
|
slotIndex: 0,
|
|
114
116
|
label: "pub reset password intro",
|
|
115
117
|
config: {
|
|
118
|
+
textSource: PHI_TEXT_SOURCE_PRESET,
|
|
116
119
|
text: "If the account exists, a reset email is on its way. Open the link in that email to choose a new password.",
|
|
117
120
|
tone: "secondary",
|
|
118
121
|
visibleWhen: withoutToken,
|