@phis/ui 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/components/cms/phi-cms-layout-renderer.js +1 -1
  2. package/components/controls/phi-collection-header-control.js +1 -1
  3. package/components/controls/phi-collection-view-control.js +2 -1
  4. package/components/controls/phi-drawer-control-adapter.d.ts +1 -1
  5. package/components/controls/phi-drawer-control-adapter.js +2 -2
  6. package/components/controls/phi-image-control.d.ts +6 -1
  7. package/components/controls/phi-image-control.js +23 -4
  8. package/components/controls/phi-lightbox-image-control-adapter.d.ts +2 -0
  9. package/components/controls/phi-lightbox-image-control-adapter.js +11 -0
  10. package/components/controls/phi-lightbox-image-control.d.ts +3 -2
  11. package/components/controls/phi-lightbox-image-control.js +7 -3
  12. package/components/controls/phi-modal-control-adapter.d.ts +1 -1
  13. package/components/controls/phi-modal-control-adapter.js +5 -2
  14. package/components/controls/phi-overlay-control-contract.d.ts +5 -0
  15. package/components/controls/phi-pagination-control-adapter.d.ts +2 -0
  16. package/components/controls/phi-pagination-control-adapter.js +15 -0
  17. package/components/controls/phi-pagination-control.d.ts +2 -1
  18. package/components/controls/phi-pagination-control.js +11 -7
  19. package/components/controls/phi-table-control.js +1 -1
  20. package/components/controls/phi-timeline-control-adapter.d.ts +2 -0
  21. package/components/controls/phi-timeline-control-adapter.js +28 -0
  22. package/components/controls/phi-timeline-control.d.ts +3 -1
  23. package/components/controls/phi-timeline-control.js +8 -22
  24. package/components/controls/phi-tour-control.js +25 -7
  25. package/components/controls/phi-tour-viewport.d.ts +36 -0
  26. package/components/controls/phi-tour-viewport.js +86 -0
  27. package/components/controls/phi-zoom-image-overlay.d.ts +17 -0
  28. package/components/controls/phi-zoom-image-overlay.js +58 -0
  29. package/components/layouts/layout-definitions.d.ts +0 -18
  30. package/components/layouts/layout-definitions.js +7 -1
  31. package/components/layouts/plugins/carousel-layout-plugin.js +2 -1
  32. package/components/media/aspect-ratios.d.ts +14 -0
  33. package/components/media/aspect-ratios.js +13 -0
  34. package/components/motion/phi-sequence-run.d.ts +27 -0
  35. package/components/motion/phi-sequence-run.js +87 -0
  36. package/components/motion/phi-sequence-viewport.d.ts +16 -0
  37. package/components/motion/phi-sequence-viewport.js +97 -34
  38. package/components/overlays/phi-overlay-container-client.js +5 -2
  39. package/components/runtime/runtime-visual-anchors.d.ts +13 -2
  40. package/components/runtime/runtime-visual-anchors.js +49 -3
  41. package/components/widgets/config/image-variant-options.d.ts +3 -5
  42. package/components/widgets/config/image-variant-options.js +11 -11
  43. package/components/widgets/config/sequence-settings.d.ts +75 -0
  44. package/components/widgets/config/sequence-settings.js +173 -0
  45. package/components/widgets/label-sets/content-entries.d.ts +9 -0
  46. package/components/widgets/label-sets/content-entries.js +17 -0
  47. package/components/widgets/label-types/gallery.d.ts +2 -10
  48. package/components/widgets/label-types/sequence.d.ts +18 -0
  49. package/components/widgets/label-types/sequence.js +1 -0
  50. package/helpers/cms-layout-defaults.js +9 -2
  51. package/helpers/cms-region-keys.d.ts +1 -1
  52. package/helpers/motion.d.ts +7 -0
  53. package/helpers/motion.js +7 -0
  54. package/package.json +1 -1
  55. package/plugins/runtime-modules/asset/data-providers.d.ts +4 -0
  56. package/plugins/runtime-modules/asset/data-providers.js +1 -0
  57. package/plugins/runtime-modules/asset/definition.d.ts +4 -0
  58. package/plugins/runtime-modules/asset/presets.d.ts +53 -1
  59. package/plugins/runtime-modules/asset/presets.js +16 -12
  60. package/plugins/runtime-modules/asset/tour.d.ts +44 -0
  61. package/plugins/runtime-modules/asset/tour.js +53 -0
  62. package/plugins/runtime-modules/builder/clients/inspector-config-field.js +42 -18
  63. package/plugins/runtime-modules/builder/clients/inspector-image-field.js +2 -2
  64. package/plugins/runtime-modules/builder/clients/workspace-scope-boundary.js +10 -0
  65. package/plugins/runtime-modules/builder/controller/workspace-controller.js +18 -3
  66. package/plugins/runtime-modules/builder/plugin-meta-labels.js +8 -0
  67. package/plugins/runtime-modules/builder/presets.js +5 -3
  68. package/plugins/runtime-modules/builder/tour.d.ts +316 -0
  69. package/plugins/runtime-modules/builder/tour.js +337 -0
  70. package/plugins/runtime-modules/builder/trees/phi-builder-inspector-area-overlay-tree.server.js +8 -1
  71. package/plugins/runtime-modules/builder/widgets/structure-region/built-in.js +3 -2
  72. package/plugins/runtime-modules/core/widgets/collection-view/client.js +10 -0
  73. package/plugins/runtime-modules/core/widgets/description/config.d.ts +25 -1
  74. package/plugins/runtime-modules/core/widgets/entry-detail/config.d.ts +2 -0
  75. package/plugins/runtime-modules/core/widgets/entry-detail/config.js +8 -1
  76. package/plugins/runtime-modules/core/widgets/entry-header/config.d.ts +3 -0
  77. package/plugins/runtime-modules/core/widgets/entry-header/config.js +8 -1
  78. package/plugins/runtime-modules/core/widgets/entry-list/client.d.ts +4 -1
  79. package/plugins/runtime-modules/core/widgets/entry-list/client.js +3 -2
  80. package/plugins/runtime-modules/core/widgets/entry-list/config.d.ts +392 -93
  81. package/plugins/runtime-modules/core/widgets/entry-list/config.js +21 -3
  82. package/plugins/runtime-modules/core/widgets/entry-list/server.js +2 -0
  83. package/plugins/runtime-modules/core/widgets/gallery/authoring.js +62 -10
  84. package/plugins/runtime-modules/core/widgets/gallery/client.d.ts +6 -1
  85. package/plugins/runtime-modules/core/widgets/gallery/client.js +7 -79
  86. package/plugins/runtime-modules/core/widgets/gallery/config.d.ts +337 -144
  87. package/plugins/runtime-modules/core/widgets/gallery/config.js +52 -113
  88. package/plugins/runtime-modules/core/widgets/gallery/defaults.d.ts +15 -0
  89. package/plugins/runtime-modules/core/widgets/gallery/defaults.js +20 -0
  90. package/plugins/runtime-modules/core/widgets/image/client.js +1 -1
  91. package/plugins/runtime-modules/core/widgets/image/config.d.ts +29 -4
  92. package/plugins/runtime-modules/core/widgets/image/config.js +19 -2
  93. package/plugins/runtime-modules/core/widgets/image/editor.js +1 -1
  94. package/plugins/runtime-modules/core/widgets/result/config.d.ts +25 -1
  95. package/plugins/runtime-modules/core/widgets/simple-text/config.d.ts +25 -1
  96. package/plugins/runtime-modules/core/widgets/table/config.d.ts +79 -3
  97. package/plugins/runtime-modules/core/widgets/tour-button/client.js +1 -1
  98. package/plugins/runtime-modules/revisions/presets.d.ts +53 -2
  99. package/plugins/runtime-modules/revisions/presets.js +16 -12
  100. package/plugins/runtime-modules/revisions/tour.d.ts +43 -0
  101. package/plugins/runtime-modules/revisions/tour.js +51 -0
  102. package/plugins/runtime-modules/theme/presets.d.ts +36 -4
  103. package/plugins/runtime-modules/theme/tour.d.ts +45 -11
  104. package/plugins/runtime-modules/theme/tour.js +39 -8
  105. package/plugins/runtime-modules/tour/controller/address.js +2 -2
  106. package/plugins/runtime-modules/tour/controller/client.js +43 -2
  107. package/plugins/runtime-modules/tour/controller/definition.js +2 -1
  108. package/server-helpers/cms-request.js +1 -0
  109. package/server-helpers/page-tour.d.ts +10 -3
  110. package/server-helpers/page-tour.js +25 -12
  111. package/types/cms-config.d.ts +3 -1
  112. package/types/cms-config.js +3 -3
  113. package/types/cms-plugins.d.ts +31 -4
  114. package/types/tour-controller.d.ts +10 -0
  115. package/types/tour-controller.js +13 -0
  116. package/types/tours.d.ts +27 -25
  117. package/types/tours.js +9 -23
  118. package/types/visual-anchors.d.ts +15 -0
  119. package/types/visual-anchors.js +12 -0
  120. package/types.d.ts +2 -0
  121. package/types.js +4 -1
@@ -1,9 +1,10 @@
1
1
  import { resolvePhiCmsWidgetPluginKey } from "../../../../../constants/cms-widget-types";
2
2
  import { PHI_MEDIA_LIBRARY_DATA_PROVIDER_KEYS } from "../../../../../constants/media-library-provider-keys";
3
+ import { PHI_MEDIA_ASPECT_RATIOS, PHI_MEDIA_ASPECT_RATIO_OPTIONS, } from "../../../../../components/media/aspect-ratios";
3
4
  import { PHI_IMAGE_VARIANT_OPTIONS } from "../../../../../components/widgets/config/image-variant-options";
5
+ import { buildPhiSequenceSettingsFields, PHI_SEQUENCE_SETTINGS_DEFAULTS, readPhiSequenceSettings, } from "../../../../../components/widgets/config/sequence-settings";
6
+ import { PHI_GALLERY_DEFAULT_ASPECT_RATIO } from "./defaults";
4
7
  import { readPhiRuntimeDataProviderBinding, } from "../../../../../types/runtime-data-provider";
5
- import { PHI_MOTION_EASING_FIELD_OPTIONS, PHI_SEQUENCE_TRANSITION_MAX_MS, PHI_SEQUENCE_TRANSITION_MIN_MS, PHI_SEQUENCE_TRANSITION_STEP_MS, clampPhiSequenceTransitionMs, PHI_SEQUENCE_ANCHORS, PHI_SEQUENCE_TRANSITIONS, readPhiMotionEasing, } from "../../../../../helpers/motion";
6
- import { PHI_CMS_MOUNT_POLICY_FIELD_OPTIONS, readPhiCmsMountPolicy, } from "../../../../../types/cms-mount-policy";
7
8
  import { readRenderableBlockConfig, } from "../../../../../components/widgets/config/parser-primitives";
8
9
  /*
9
10
  * Read strictly, and say so when a document is wrong.
@@ -24,14 +25,6 @@ function readMember(value, members, key) {
24
25
  }
25
26
  return value;
26
27
  }
27
- function readCount(value, key, min) {
28
- if (value === undefined || value === null)
29
- return undefined;
30
- if (typeof value !== "number" || !Number.isInteger(value) || value < min) {
31
- fail(key, value, `a whole number of at least ${min}`);
32
- }
33
- return value;
34
- }
35
28
  function readFlag(value, key) {
36
29
  if (value === undefined || value === null)
37
30
  return undefined;
@@ -39,55 +32,38 @@ function readFlag(value, key) {
39
32
  fail(key, value, "true or false");
40
33
  return value;
41
34
  }
42
- function readText(value, key) {
35
+ /** The Folder the pictures come from; a binding to any other Provider is a mistake in the document. */
36
+ function readFolderSource(value) {
37
+ const binding = readPhiRuntimeDataProviderBinding(value);
38
+ if (binding && binding.providerKey !== PHI_MEDIA_LIBRARY_DATA_PROVIDER_KEYS.folderImages) {
39
+ fail("source", binding.providerKey, PHI_MEDIA_LIBRARY_DATA_PROVIDER_KEYS.folderImages);
40
+ }
41
+ return binding;
42
+ }
43
+ function readVariantKey(value) {
43
44
  if (value === undefined || value === null)
44
45
  return undefined;
45
- if (typeof value !== "string" || value.trim() === "")
46
- fail(key, value, "a non-empty string");
46
+ if (!PHI_IMAGE_VARIANT_OPTIONS.some((option) => option.value === value)) {
47
+ fail("variantKey", value, `one of ${PHI_IMAGE_VARIANT_OPTIONS.map((option) => option.value).join(", ")}`);
48
+ }
47
49
  return value;
48
50
  }
49
51
  export function parsePhiCmsGalleryWidgetConfig(raw) {
50
- const visibleCount = readCount(raw.visibleCount, "visibleCount", 1);
51
- const windowAnchor = readMember(raw.windowAnchor, PHI_SEQUENCE_ANCHORS, "windowAnchor");
52
- const transition = readMember(raw.transition, PHI_SEQUENCE_TRANSITIONS, "transition");
53
- const easing = readPhiMotionEasing(raw.easing, undefined);
54
- const gap = readText(raw.gap, "gap");
55
- const lookahead = readCount(raw.lookahead, "lookahead", 0);
56
- const aspectRatio = readText(raw.aspectRatio, "aspectRatio");
52
+ const aspectRatio = readMember(raw.aspectRatio, PHI_MEDIA_ASPECT_RATIOS, "aspectRatio");
57
53
  const fit = readMember(raw.fit, ["cover", "contain"], "fit");
58
- const controls = readMember(raw.controls, ["none", "arrows", "dots", "both"], "controls");
59
- const loop = readFlag(raw.loop, "loop");
60
- const autoplayMs = readCount(raw.autoplayMs, "autoplayMs", 0);
61
- const variantKey = readMember(raw.variantKey, PHI_IMAGE_VARIANT_OPTIONS.map((option) => option.value), "variantKey");
54
+ const variantKey = readVariantKey(raw.variantKey);
62
55
  const preview = readFlag(raw.preview, "preview");
63
56
  return {
64
57
  ...readRenderableBlockConfig(raw),
65
- source: readPhiRuntimeDataProviderBinding(raw.source),
58
+ ...readPhiSequenceSettings(raw, fail),
59
+ source: readFolderSource(raw.source),
66
60
  ...(variantKey === undefined ? {} : { variantKey }),
67
61
  ...(preview === undefined ? {} : { preview }),
68
- ...(visibleCount === undefined ? {} : { visibleCount }),
69
- ...(windowAnchor === undefined ? {} : { windowAnchor }),
70
- ...(transition === undefined ? {} : { transition }),
71
- // Absent stays absent: the viewport then moves at the pace the theme sets rather than one this
72
- // Widget invented.
73
- ...(raw.durationMs === undefined || raw.durationMs === null
74
- ? {}
75
- : { durationMs: clampPhiSequenceTransitionMs(raw.durationMs) }),
76
- ...(easing === undefined ? {} : { easing }),
77
- ...(gap === undefined ? {} : { gap }),
78
- ...(lookahead === undefined ? {} : { lookahead }),
79
- mountPolicy: readPhiCmsMountPolicy(raw.mountPolicy, "lazy-keep"),
80
62
  ...(aspectRatio === undefined ? {} : { aspectRatio }),
81
63
  ...(fit === undefined ? {} : { fit }),
82
- ...(controls === undefined ? {} : { controls }),
83
- ...(loop === undefined ? {} : { loop }),
84
- // Zero is how "does not move on its own" is written, so it is the one number below the floor
85
- // that means something.
86
- ...(autoplayMs === undefined || autoplayMs === 0
87
- ? {}
88
- : { autoplayMs: clampPhiSequenceTransitionMs(autoplayMs) }),
89
64
  };
90
65
  }
66
+ const PHI_GALLERY_SEQUENCE_FIELDS = buildPhiSequenceSettingsFields({ countLabel: "Visible Pictures" });
91
67
  export const PHI_GALLERY_WIDGET_DEFINITION = {
92
68
  kind: "widget",
93
69
  pluginKey: resolvePhiCmsWidgetPluginKey("gallery"),
@@ -104,63 +80,33 @@ export const PHI_GALLERY_WIDGET_DEFINITION = {
104
80
  key: "source",
105
81
  type: "data-provider",
106
82
  providerKind: "record",
107
- label: "Pictures",
108
- description: "Where the pictures come from. The folder and its order are set inside the binding.",
83
+ // Always a Folder: the Inspector asks for the folder and its order, never for a Provider.
84
+ providerKey: PHI_MEDIA_LIBRARY_DATA_PROVIDER_KEYS.folderImages,
85
+ label: "Source",
109
86
  },
110
87
  {
111
88
  key: "variantKey",
112
89
  type: "choice",
90
+ valueType: "number",
91
+ heading: "Picture",
113
92
  label: "Variant",
114
93
  options: PHI_IMAGE_VARIANT_OPTIONS,
115
94
  emptyOption: { value: "__original__", label: "Original" },
116
95
  emptyValue: null,
117
96
  },
118
- { key: "preview", type: "boolean", label: "Lightbox", description: "Open a picture larger over the page when it is clicked." },
119
- { key: "visibleCount", type: "number", label: "Visible Pictures", min: 1 },
120
- {
121
- key: "windowAnchor",
122
- type: "choice",
123
- label: "Window Anchor",
124
- options: [
125
- { value: "start", label: "Leading edge" },
126
- { value: "center", label: "Centre" },
127
- ],
128
- },
129
- {
130
- key: "transition",
131
- type: "choice",
132
- label: "Transition",
133
- options: [
134
- { value: "none", label: "None" },
135
- { value: "fade", label: "Fade" },
136
- { value: "slide", label: "Slide" },
137
- { value: "diagonal", label: "Diagonal" },
138
- ],
139
- },
140
- {
141
- key: "durationMs",
142
- type: "number",
143
- label: "Duration",
144
- min: PHI_SEQUENCE_TRANSITION_MIN_MS,
145
- max: PHI_SEQUENCE_TRANSITION_MAX_MS,
146
- step: PHI_SEQUENCE_TRANSITION_STEP_MS,
147
- prefix: "ms",
148
- },
149
- {
150
- key: "easing",
151
- type: "choice",
152
- label: "Easing",
153
- options: [...PHI_MOTION_EASING_FIELD_OPTIONS],
154
- },
155
- { key: "gap", type: "string", label: "Gap" },
156
- { key: "lookahead", type: "number", label: "Preload Ahead", min: 0 },
97
+ /*
98
+ * Proportion and fit are questions only the original asks. A variant is cut to its own width and
99
+ * height, so the frame takes that proportion and the picture fills it exactly; a second ratio on
100
+ * top would crop a crop. Originals come in every shape, and a run of them needs one frame to
101
+ * stand in, or the Gallery changes height with every step.
102
+ */
157
103
  {
158
- key: "mountPolicy",
104
+ key: "aspectRatio",
159
105
  type: "choice",
160
- label: "Mounting",
161
- options: [...PHI_CMS_MOUNT_POLICY_FIELD_OPTIONS],
106
+ label: "Aspect Ratio",
107
+ options: PHI_MEDIA_ASPECT_RATIO_OPTIONS,
108
+ visibleWhen: { field: "variantKey", equals: null },
162
109
  },
163
- { key: "aspectRatio", type: "string", label: "Aspect Ratio" },
164
110
  {
165
111
  key: "fit",
166
112
  type: "choice",
@@ -169,31 +115,24 @@ export const PHI_GALLERY_WIDGET_DEFINITION = {
169
115
  { value: "cover", label: "Cover" },
170
116
  { value: "contain", label: "Contain" },
171
117
  ],
118
+ visibleWhen: { field: "variantKey", equals: null },
172
119
  },
173
- {
174
- key: "controls",
175
- type: "choice",
176
- label: "Controls",
177
- options: [
178
- { value: "both", label: "Arrows and dots" },
179
- { value: "arrows", label: "Arrows" },
180
- { value: "dots", label: "Dots" },
181
- { value: "none", label: "None" },
182
- ],
183
- },
184
- { key: "loop", type: "boolean", label: "Wrap Around" },
185
- {
186
- // Zero is the one value below the floor that means something: it does not move on its own.
187
- key: "autoplayMs",
188
- type: "number",
189
- label: "Autoplay",
190
- min: 0,
191
- max: PHI_SEQUENCE_TRANSITION_MAX_MS,
192
- step: PHI_SEQUENCE_TRANSITION_STEP_MS,
193
- prefix: "ms",
194
- },
120
+ { key: "preview", type: "boolean", label: "Lightbox", description: "Open a picture larger over the page when it is clicked." },
121
+ ...PHI_GALLERY_SEQUENCE_FIELDS.window,
122
+ ...PHI_GALLERY_SEQUENCE_FIELDS.movement,
123
+ ...PHI_GALLERY_SEQUENCE_FIELDS.controls,
195
124
  ],
196
- // Bound to the Folder Provider from the start, so the inspector asks for a folder and nothing else.
197
- defaultConfig: { source: { providerKey: PHI_MEDIA_LIBRARY_DATA_PROVIDER_KEYS.folderImages, params: {} } },
125
+ /*
126
+ * Bound to the Folder Provider from the start, so the inspector asks for a folder and nothing else.
127
+ * The rest is what the Gallery does when nobody said otherwise, stated here so the Inspector shows
128
+ * it as the standing answer rather than an empty box over a setting that was never in doubt.
129
+ */
130
+ defaultConfig: {
131
+ source: { providerKey: PHI_MEDIA_LIBRARY_DATA_PROVIDER_KEYS.folderImages, params: {} },
132
+ preview: false,
133
+ aspectRatio: PHI_GALLERY_DEFAULT_ASPECT_RATIO,
134
+ fit: "cover",
135
+ ...PHI_SEQUENCE_SETTINGS_DEFAULTS,
136
+ },
198
137
  parseConfig: parsePhiCmsGalleryWidgetConfig,
199
138
  };
@@ -0,0 +1,15 @@
1
+ import { type PhiMediaAspectRatio } from "../../../../../components/media/aspect-ratios";
2
+ import type { PhiCmsGalleryWidgetConfig } from "./config";
3
+ /** What a Gallery is drawn at where nobody said otherwise, on the page and on the canvas alike. */
4
+ export declare const PHI_GALLERY_DEFAULT_ASPECT_RATIO: PhiMediaAspectRatio;
5
+ /**
6
+ * The frame every picture of the run stands in.
7
+ *
8
+ * A variant is cut to a width and a height of its own, so its proportion is the frame's and the
9
+ * picture fills it exactly -- the author's ratio and fit are not asked for and not read. Only the
10
+ * original, which comes in every shape, takes them.
11
+ */
12
+ export declare function resolvePhiGalleryFrame(config: Pick<PhiCmsGalleryWidgetConfig, "variantKey" | "aspectRatio" | "fit"> | undefined): {
13
+ aspectRatio: string;
14
+ fit: "cover" | "contain";
15
+ };
@@ -0,0 +1,20 @@
1
+ import { resolvePhiImageAssetVariantSpec } from "../../../../../constants/media";
2
+ import { resolvePhiMediaAspectRatioCss } from "../../../../../components/media/aspect-ratios";
3
+ /** What a Gallery is drawn at where nobody said otherwise, on the page and on the canvas alike. */
4
+ export const PHI_GALLERY_DEFAULT_ASPECT_RATIO = "16:9";
5
+ /**
6
+ * The frame every picture of the run stands in.
7
+ *
8
+ * A variant is cut to a width and a height of its own, so its proportion is the frame's and the
9
+ * picture fills it exactly -- the author's ratio and fit are not asked for and not read. Only the
10
+ * original, which comes in every shape, takes them.
11
+ */
12
+ export function resolvePhiGalleryFrame(config) {
13
+ const spec = config?.variantKey == null ? null : resolvePhiImageAssetVariantSpec(config.variantKey);
14
+ return spec
15
+ ? { aspectRatio: `${spec.width} / ${spec.height}`, fit: "cover" }
16
+ : {
17
+ aspectRatio: resolvePhiMediaAspectRatioCss(config?.aspectRatio ?? PHI_GALLERY_DEFAULT_ASPECT_RATIO),
18
+ fit: config?.fit ?? "cover",
19
+ };
20
+ }
@@ -87,7 +87,7 @@ export function PhiImageWidget({ labels, config, resolvedAsset, focalRect, autho
87
87
  textAlign: "center",
88
88
  }, children: [_jsx(PhiEmptyControl, {}), _jsx(PhiTypographyControl, { type: "secondary", strong: false, children: labels.emptyTitle }), _jsx(PhiTypographyControl, { type: "secondary", children: labels.emptyDescription })] }));
89
89
  }
90
- return (_jsx(PhiImageControl, { presentation: presentation, source: sourceKind === "asset" ? "asset" : config?.trusted === true ? "trusted-url" : "url", alt: alt, title: title || undefined, width: width, height: height, blurDataUrl: resolvedAsset?.blurDataUrl, optimizable: optimizable, sizes: config?.sizes, preload: config?.preload === true && isPublicAsset, preview: (config?.previewMode ?? "none") === "lightbox", style: {
90
+ return (_jsx(PhiImageControl, { presentation: presentation, source: sourceKind === "asset" ? "asset" : config?.trusted === true ? "trusted-url" : "url", alt: alt, title: title || undefined, width: width, height: height, blurDataUrl: resolvedAsset?.blurDataUrl, optimizable: optimizable, sizes: config?.sizes, preload: config?.preload === true && isPublicAsset, preview: (config?.previewMode ?? "none") === "lightbox", zoom: config?.previewMode === "native", style: {
91
91
  ...radiusStyle,
92
92
  ...maskStyle,
93
93
  overflow: presentation.simulatedCropStyle || Object.values(radiusStyle).some((value) => value != null)
@@ -42,6 +42,7 @@ export declare const PHI_IMAGE_WIDGET_DEFINITION: {
42
42
  label: string;
43
43
  }[];
44
44
  visibleWhen?: undefined;
45
+ valueType?: undefined;
45
46
  emptyOption?: undefined;
46
47
  emptyValue?: undefined;
47
48
  widthPlaceholder?: undefined;
@@ -59,6 +60,7 @@ export declare const PHI_IMAGE_WIDGET_DEFINITION: {
59
60
  equals: string;
60
61
  };
61
62
  options?: undefined;
63
+ valueType?: undefined;
62
64
  emptyOption?: undefined;
63
65
  emptyValue?: undefined;
64
66
  widthPlaceholder?: undefined;
@@ -70,11 +72,9 @@ export declare const PHI_IMAGE_WIDGET_DEFINITION: {
70
72
  } | {
71
73
  key: string;
72
74
  type: "choice";
75
+ valueType: "number";
73
76
  label: string;
74
- options: {
75
- value: string;
76
- label: string;
77
- }[];
77
+ options: import("@phis/contracts/controls").PhiControlOption<number>[];
78
78
  emptyOption: {
79
79
  value: string;
80
80
  label: string;
@@ -99,6 +99,28 @@ export declare const PHI_IMAGE_WIDGET_DEFINITION: {
99
99
  equals: string;
100
100
  };
101
101
  options?: undefined;
102
+ valueType?: undefined;
103
+ emptyOption?: undefined;
104
+ emptyValue?: undefined;
105
+ widthPlaceholder?: undefined;
106
+ heightPlaceholder?: undefined;
107
+ topLeftKey?: undefined;
108
+ topRightKey?: undefined;
109
+ bottomLeftKey?: undefined;
110
+ bottomRightKey?: undefined;
111
+ } | {
112
+ key: string;
113
+ type: "choice";
114
+ label: string;
115
+ visibleWhen: {
116
+ field: string;
117
+ equals: null;
118
+ };
119
+ options: {
120
+ value: string;
121
+ label: string;
122
+ }[];
123
+ valueType?: undefined;
102
124
  emptyOption?: undefined;
103
125
  emptyValue?: undefined;
104
126
  widthPlaceholder?: undefined;
@@ -113,6 +135,7 @@ export declare const PHI_IMAGE_WIDGET_DEFINITION: {
113
135
  label: string;
114
136
  options?: undefined;
115
137
  visibleWhen?: undefined;
138
+ valueType?: undefined;
116
139
  emptyOption?: undefined;
117
140
  emptyValue?: undefined;
118
141
  widthPlaceholder?: undefined;
@@ -129,6 +152,7 @@ export declare const PHI_IMAGE_WIDGET_DEFINITION: {
129
152
  heightPlaceholder: string;
130
153
  options?: undefined;
131
154
  visibleWhen?: undefined;
155
+ valueType?: undefined;
132
156
  emptyOption?: undefined;
133
157
  emptyValue?: undefined;
134
158
  topLeftKey?: undefined;
@@ -145,6 +169,7 @@ export declare const PHI_IMAGE_WIDGET_DEFINITION: {
145
169
  bottomRightKey: string;
146
170
  options?: undefined;
147
171
  visibleWhen?: undefined;
172
+ valueType?: undefined;
148
173
  emptyOption?: undefined;
149
174
  emptyValue?: undefined;
150
175
  widthPlaceholder?: undefined;
@@ -66,8 +66,12 @@ export function normalizePhiImageWidgetConfig(config) {
66
66
  borderBottomRightRadius: readCssSize(raw.borderBottomRightRadius),
67
67
  mask: normalizePhiMaskConfig(raw.mask),
68
68
  };
69
+ /*
70
+ * An Asset without a word about its variant gets the Card variant; `null` is a word -- the Inspector's
71
+ * Original -- and stays. Read from the raw config, because the source parser answers `null` for both.
72
+ */
69
73
  return source.sourceKind === "asset"
70
- ? { ...normalized, ...source, variantKey: source.variantKey ?? PhiImageAssetVariantKey.Card }
74
+ ? { ...normalized, ...source, variantKey: raw.variantKey === null ? null : source.variantKey ?? PhiImageAssetVariantKey.Card }
71
75
  : { ...normalized, ...source };
72
76
  }
73
77
  function parsePhiCmsImageWidgetConfig(config) {
@@ -108,6 +112,7 @@ export const PHI_IMAGE_WIDGET_DEFINITION = {
108
112
  {
109
113
  key: "variantKey",
110
114
  type: "choice",
115
+ valueType: "number",
111
116
  label: "Variant",
112
117
  options: PHI_IMAGE_VARIANT_OPTIONS,
113
118
  emptyOption: { value: "__original__", label: "Original" },
@@ -116,17 +121,29 @@ export const PHI_IMAGE_WIDGET_DEFINITION = {
116
121
  },
117
122
  { key: "alt", type: "string", label: "Alt Text", visibleWhen: { field: "sourceKind", equals: "url" } },
118
123
  { key: "title", type: "string", label: "Title", visibleWhen: { field: "sourceKind", equals: "url" } },
124
+ /*
125
+ * Fit and position steer only the original picture, an Asset's or a URL's. A variant is cut to its
126
+ * own fit, and its position comes from the Asset's focal rectangle, so with a variant chosen both
127
+ * would be settings that change nothing. A URL clears the variant, so one rule covers both cases.
128
+ */
119
129
  {
120
130
  key: "fit",
121
131
  type: "choice",
122
132
  label: "Fit",
133
+ visibleWhen: { field: "variantKey", equals: null },
123
134
  options: [
124
135
  { value: "cover", label: "Cover" },
125
136
  { value: "contain", label: "Contain" },
126
137
  { value: "fill", label: "Fill" },
127
138
  ],
128
139
  },
129
- { key: "objectPosition", type: "choice", label: "Object Position", options: IMAGE_OBJECT_POSITION_OPTIONS },
140
+ {
141
+ key: "objectPosition",
142
+ type: "choice",
143
+ label: "Object Position",
144
+ options: IMAGE_OBJECT_POSITION_OPTIONS,
145
+ visibleWhen: { field: "variantKey", equals: null },
146
+ },
130
147
  {
131
148
  key: "previewMode",
132
149
  type: "choice",
@@ -76,7 +76,7 @@ export function PhiImageWidgetEditor({ config, onChange }) {
76
76
  }, children: overlaySize })) : null] }));
77
77
  }
78
78
  export function PhiImageWidgetEditorTools({ blockId, config, onChange, }) {
79
- return (_jsxs("span", { style: { display: "inline-flex" }, children: [_jsx(PhiWidgetImageToolButton, { blockId: blockId, onChange: (patch) => onChange?.(patch) }), _jsx(PhiMaskPickerButton, { value: config?.mask, onChange: (mask) => {
79
+ return (_jsxs("span", { style: { display: "inline-flex" }, children: [_jsx(PhiWidgetImageToolButton, { blockId: blockId, onChange: (patch) => onChange?.(patch) }), _jsx(PhiMaskPickerButton, { placement: "topRight", value: config?.mask, onChange: (mask) => {
80
80
  onChange?.({ mask });
81
81
  } })] }));
82
82
  }
@@ -74,6 +74,7 @@ export declare const PHI_RESULT_WIDGET_DEFINITION: {
74
74
  } & {
75
75
  type: "data-provider";
76
76
  providerKind?: import("../../../../../types").PhiRuntimeDataProviderKind;
77
+ providerKey?: import("@phis/contracts/controls").PhiRuntimeDataProviderKey;
77
78
  }) | ({
78
79
  key: string;
79
80
  label: string;
@@ -247,10 +248,33 @@ export declare const PHI_RESULT_WIDGET_DEFINITION: {
247
248
  editorPlacement?: "inspector" | "geometry" | "toolbar";
248
249
  visibleWhen?: import("../../../../../types").PhiCmsConfigFieldVisibilityRule | readonly import("../../../../../types").PhiCmsConfigFieldVisibilityRule[];
249
250
  heading?: string;
251
+ } & {
252
+ type: "choice";
253
+ valueType: "number";
254
+ options: import("@phis/contracts/controls").PhiControlOption<number>[];
255
+ emptyOption?: import("@phis/contracts/controls").PhiControlOption;
256
+ emptyValue?: null | undefined;
257
+ placeholder?: string;
258
+ mode?: never;
259
+ presentation?: never;
260
+ optionsProvider?: never;
261
+ allowCustom?: never;
262
+ createBehavior?: never;
263
+ filter?: never;
264
+ patchOnChange?: never;
265
+ optionPresets?: never;
266
+ }) | ({
267
+ key: string;
268
+ label: string;
269
+ description?: string;
270
+ required?: boolean;
271
+ editorPlacement?: "inspector" | "geometry" | "toolbar";
272
+ visibleWhen?: import("../../../../../types").PhiCmsConfigFieldVisibilityRule | readonly import("../../../../../types").PhiCmsConfigFieldVisibilityRule[];
273
+ heading?: string;
250
274
  } & {
251
275
  type: "choice";
252
276
  mode?: import("../../../../../types").PhiCmsConfigFieldChoiceMode;
253
- valueType?: import("../../../../../types").PhiCmsConfigFieldChoiceValueType;
277
+ valueType?: Exclude<import("../../../../../types").PhiCmsConfigFieldChoiceValueType, "number">;
254
278
  presentation?: import("../../../../../types").PhiCmsConfigFieldChoicePresentation;
255
279
  options?: import("@phis/contracts/controls").PhiControlOption[];
256
280
  optionsProvider?: import("@phis/contracts/controls").PhiControlOptionsProviderConfig;
@@ -79,6 +79,7 @@ export declare const PHI_SIMPLE_TEXT_WIDGET_DEFINITION: {
79
79
  } & {
80
80
  type: "data-provider";
81
81
  providerKind?: import("../../../../../types").PhiRuntimeDataProviderKind;
82
+ providerKey?: import("@phis/contracts/controls").PhiRuntimeDataProviderKey;
82
83
  }) | ({
83
84
  key: string;
84
85
  label: string;
@@ -252,10 +253,33 @@ export declare const PHI_SIMPLE_TEXT_WIDGET_DEFINITION: {
252
253
  editorPlacement?: "inspector" | "geometry" | "toolbar";
253
254
  visibleWhen?: import("../../../../../types").PhiCmsConfigFieldVisibilityRule | readonly import("../../../../../types").PhiCmsConfigFieldVisibilityRule[];
254
255
  heading?: string;
256
+ } & {
257
+ type: "choice";
258
+ valueType: "number";
259
+ options: import("@phis/contracts/controls").PhiControlOption<number>[];
260
+ emptyOption?: import("@phis/contracts/controls").PhiControlOption;
261
+ emptyValue?: null | undefined;
262
+ placeholder?: string;
263
+ mode?: never;
264
+ presentation?: never;
265
+ optionsProvider?: never;
266
+ allowCustom?: never;
267
+ createBehavior?: never;
268
+ filter?: never;
269
+ patchOnChange?: never;
270
+ optionPresets?: never;
271
+ }) | ({
272
+ key: string;
273
+ label: string;
274
+ description?: string;
275
+ required?: boolean;
276
+ editorPlacement?: "inspector" | "geometry" | "toolbar";
277
+ visibleWhen?: import("../../../../../types").PhiCmsConfigFieldVisibilityRule | readonly import("../../../../../types").PhiCmsConfigFieldVisibilityRule[];
278
+ heading?: string;
255
279
  } & {
256
280
  type: "choice";
257
281
  mode?: import("../../../../../types").PhiCmsConfigFieldChoiceMode;
258
- valueType?: import("../../../../../types").PhiCmsConfigFieldChoiceValueType;
282
+ valueType?: Exclude<import("../../../../../types").PhiCmsConfigFieldChoiceValueType, "number">;
259
283
  presentation?: import("../../../../../types").PhiCmsConfigFieldChoicePresentation;
260
284
  options?: import("@phis/contracts/controls").PhiControlOption[];
261
285
  optionsProvider?: import("@phis/contracts/controls").PhiControlOptionsProviderConfig;
@@ -105,6 +105,7 @@ export declare const PHI_TABLE_WIDGET_DEFINITION: {
105
105
  } & {
106
106
  type: "data-provider";
107
107
  providerKind?: import("../../../../../types").PhiRuntimeDataProviderKind;
108
+ providerKey?: import("@phis/contracts/controls").PhiRuntimeDataProviderKey;
108
109
  }) | ({
109
110
  key: string;
110
111
  label: string;
@@ -278,10 +279,33 @@ export declare const PHI_TABLE_WIDGET_DEFINITION: {
278
279
  editorPlacement?: "inspector" | "geometry" | "toolbar";
279
280
  visibleWhen?: import("../../../../../types").PhiCmsConfigFieldVisibilityRule | readonly import("../../../../../types").PhiCmsConfigFieldVisibilityRule[];
280
281
  heading?: string;
282
+ } & {
283
+ type: "choice";
284
+ valueType: "number";
285
+ options: import("@phis/contracts/controls").PhiControlOption<number>[];
286
+ emptyOption?: import("@phis/contracts/controls").PhiControlOption;
287
+ emptyValue?: null | undefined;
288
+ placeholder?: string;
289
+ mode?: never;
290
+ presentation?: never;
291
+ optionsProvider?: never;
292
+ allowCustom?: never;
293
+ createBehavior?: never;
294
+ filter?: never;
295
+ patchOnChange?: never;
296
+ optionPresets?: never;
297
+ }) | ({
298
+ key: string;
299
+ label: string;
300
+ description?: string;
301
+ required?: boolean;
302
+ editorPlacement?: "inspector" | "geometry" | "toolbar";
303
+ visibleWhen?: import("../../../../../types").PhiCmsConfigFieldVisibilityRule | readonly import("../../../../../types").PhiCmsConfigFieldVisibilityRule[];
304
+ heading?: string;
281
305
  } & {
282
306
  type: "choice";
283
307
  mode?: import("../../../../../types").PhiCmsConfigFieldChoiceMode;
284
- valueType?: import("../../../../../types").PhiCmsConfigFieldChoiceValueType;
308
+ valueType?: Exclude<import("../../../../../types").PhiCmsConfigFieldChoiceValueType, "number">;
285
309
  presentation?: import("../../../../../types").PhiCmsConfigFieldChoicePresentation;
286
310
  options?: import("@phis/contracts/controls").PhiControlOption[];
287
311
  optionsProvider?: import("@phis/contracts/controls").PhiControlOptionsProviderConfig;
@@ -411,6 +435,7 @@ export declare const PHI_TABLE_WIDGET_DEFINITION: {
411
435
  heading?: string;
412
436
  type: "data-provider";
413
437
  providerKind?: import("../../../../../types").PhiRuntimeDataProviderKind;
438
+ providerKey?: import("@phis/contracts/controls").PhiRuntimeDataProviderKey;
414
439
  } | {
415
440
  visibleWhen: {
416
441
  field: string;
@@ -602,6 +627,31 @@ export declare const PHI_TABLE_WIDGET_DEFINITION: {
602
627
  prefix?: string;
603
628
  presentation?: "slider";
604
629
  suffix?: string;
630
+ } | {
631
+ visibleWhen: {
632
+ field: string;
633
+ notEquals: string;
634
+ };
635
+ key: string;
636
+ label: string;
637
+ description?: string;
638
+ required?: boolean;
639
+ editorPlacement?: "inspector" | "geometry" | "toolbar";
640
+ heading?: string;
641
+ type: "choice";
642
+ valueType: "number";
643
+ options: import("@phis/contracts/controls").PhiControlOption<number>[];
644
+ emptyOption?: import("@phis/contracts/controls").PhiControlOption;
645
+ emptyValue?: null | undefined;
646
+ placeholder?: string;
647
+ mode?: never;
648
+ presentation?: never;
649
+ optionsProvider?: never;
650
+ allowCustom?: never;
651
+ createBehavior?: never;
652
+ filter?: never;
653
+ patchOnChange?: never;
654
+ optionPresets?: never;
605
655
  } | {
606
656
  visibleWhen: {
607
657
  field: string;
@@ -615,7 +665,7 @@ export declare const PHI_TABLE_WIDGET_DEFINITION: {
615
665
  heading?: string;
616
666
  type: "choice";
617
667
  mode?: import("../../../../../types").PhiCmsConfigFieldChoiceMode;
618
- valueType?: import("../../../../../types").PhiCmsConfigFieldChoiceValueType;
668
+ valueType?: Exclude<import("../../../../../types").PhiCmsConfigFieldChoiceValueType, "number">;
619
669
  presentation?: import("../../../../../types").PhiCmsConfigFieldChoicePresentation;
620
670
  options?: import("@phis/contracts/controls").PhiControlOption[];
621
671
  optionsProvider?: import("@phis/contracts/controls").PhiControlOptionsProviderConfig;
@@ -778,6 +828,7 @@ export declare const PHI_TABLE_WIDGET_DEFINITION: {
778
828
  heading?: string;
779
829
  type: "data-provider";
780
830
  providerKind?: import("../../../../../types").PhiRuntimeDataProviderKind;
831
+ providerKey?: import("@phis/contracts/controls").PhiRuntimeDataProviderKey;
781
832
  } | {
782
833
  visibleWhen: {
783
834
  field: string;
@@ -969,6 +1020,31 @@ export declare const PHI_TABLE_WIDGET_DEFINITION: {
969
1020
  prefix?: string;
970
1021
  presentation?: "slider";
971
1022
  suffix?: string;
1023
+ } | {
1024
+ visibleWhen: {
1025
+ field: string;
1026
+ equals: boolean;
1027
+ };
1028
+ key: string;
1029
+ label: string;
1030
+ description?: string;
1031
+ required?: boolean;
1032
+ editorPlacement?: "inspector" | "geometry" | "toolbar";
1033
+ heading?: string;
1034
+ type: "choice";
1035
+ valueType: "number";
1036
+ options: import("@phis/contracts/controls").PhiControlOption<number>[];
1037
+ emptyOption?: import("@phis/contracts/controls").PhiControlOption;
1038
+ emptyValue?: null | undefined;
1039
+ placeholder?: string;
1040
+ mode?: never;
1041
+ presentation?: never;
1042
+ optionsProvider?: never;
1043
+ allowCustom?: never;
1044
+ createBehavior?: never;
1045
+ filter?: never;
1046
+ patchOnChange?: never;
1047
+ optionPresets?: never;
972
1048
  } | {
973
1049
  visibleWhen: {
974
1050
  field: string;
@@ -982,7 +1058,7 @@ export declare const PHI_TABLE_WIDGET_DEFINITION: {
982
1058
  heading?: string;
983
1059
  type: "choice";
984
1060
  mode?: import("../../../../../types").PhiCmsConfigFieldChoiceMode;
985
- valueType?: import("../../../../../types").PhiCmsConfigFieldChoiceValueType;
1061
+ valueType?: Exclude<import("../../../../../types").PhiCmsConfigFieldChoiceValueType, "number">;
986
1062
  presentation?: import("../../../../../types").PhiCmsConfigFieldChoicePresentation;
987
1063
  options?: import("@phis/contracts/controls").PhiControlOption[];
988
1064
  optionsProvider?: import("@phis/contracts/controls").PhiControlOptionsProviderConfig;
@@ -4,7 +4,7 @@ import { useCallback, useEffect, useMemo, useState } from "react";
4
4
  import { usePhiSignalDispatcher, usePhiSignalListener } from "../../../../../components/runtime/runtime-signal-bus";
5
5
  import { PhiButtonControl } from "../../../../../components/controls/phi-button-control";
6
6
  import { PhiIcon } from "../../../../../components/shell/phi-icon";
7
- import { PHI_TOUR_CONTROLLER_ADDRESS } from "../../../../../types/tours";
7
+ import { PHI_TOUR_CONTROLLER_ADDRESS } from "../../../../../types/tour-controller";
8
8
  function readAvailable(value) {
9
9
  return typeof value === "object" && value !== null && value.available === true;
10
10
  }