@blockslides/extension-slide 0.2.1 → 0.3.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/dist/index.d.mts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +67 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +67 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/slide.ts +90 -7
package/dist/index.d.mts
CHANGED
|
@@ -33,6 +33,22 @@ interface SlideOptions {
|
|
|
33
33
|
* Content Security Policy nonce
|
|
34
34
|
*/
|
|
35
35
|
injectNonce?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Hover outline highlight for nodes within a slide.
|
|
38
|
+
* - false: disabled (default)
|
|
39
|
+
* - true: enabled with defaults
|
|
40
|
+
* - object: enabled with overrides
|
|
41
|
+
*/
|
|
42
|
+
hoverOutline: false | true | {
|
|
43
|
+
color?: string;
|
|
44
|
+
width?: string;
|
|
45
|
+
offset?: string;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* When enabled, hovering a container outlines its descendant blocks too.
|
|
49
|
+
* @default false
|
|
50
|
+
*/
|
|
51
|
+
hoverOutlineCascade?: boolean;
|
|
36
52
|
}
|
|
37
53
|
declare const Slide: Node<SlideOptions, any>;
|
|
38
54
|
|
package/dist/index.d.ts
CHANGED
|
@@ -33,6 +33,22 @@ interface SlideOptions {
|
|
|
33
33
|
* Content Security Policy nonce
|
|
34
34
|
*/
|
|
35
35
|
injectNonce?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Hover outline highlight for nodes within a slide.
|
|
38
|
+
* - false: disabled (default)
|
|
39
|
+
* - true: enabled with defaults
|
|
40
|
+
* - object: enabled with overrides
|
|
41
|
+
*/
|
|
42
|
+
hoverOutline: false | true | {
|
|
43
|
+
color?: string;
|
|
44
|
+
width?: string;
|
|
45
|
+
offset?: string;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* When enabled, hovering a container outlines its descendant blocks too.
|
|
49
|
+
* @default false
|
|
50
|
+
*/
|
|
51
|
+
hoverOutlineCascade?: boolean;
|
|
36
52
|
}
|
|
37
53
|
declare const Slide: Node<SlideOptions, any>;
|
|
38
54
|
|
package/dist/index.js
CHANGED
|
@@ -63,13 +63,14 @@ var slideStyles = `
|
|
|
63
63
|
}
|
|
64
64
|
`;
|
|
65
65
|
var fixedSizeStyles = `
|
|
66
|
-
.slide
|
|
67
|
-
.slide[data-size="
|
|
68
|
-
.slide[data-size="
|
|
69
|
-
.slide[data-size="a4-
|
|
70
|
-
.slide[data-size="
|
|
71
|
-
.slide[data-size="letter-
|
|
72
|
-
.slide[data-size="
|
|
66
|
+
.slide { --slide-scale: 1; }
|
|
67
|
+
.slide[data-size="16x9"] { width: calc(1920px * var(--slide-scale)); height: calc(1080px * var(--slide-scale)); }
|
|
68
|
+
.slide[data-size="4x3"] { width: calc(1600px * var(--slide-scale)); height: calc(1200px * var(--slide-scale)); }
|
|
69
|
+
.slide[data-size="a4-portrait"] { width: calc(210mm * var(--slide-scale)); height: calc(297mm * var(--slide-scale)); }
|
|
70
|
+
.slide[data-size="a4-landscape"] { width: calc(297mm * var(--slide-scale)); height: calc(210mm * var(--slide-scale)); }
|
|
71
|
+
.slide[data-size="letter-portrait"] { width: calc(8.5in * var(--slide-scale)); height: calc(11in * var(--slide-scale)); }
|
|
72
|
+
.slide[data-size="letter-landscape"] { width: calc(11in * var(--slide-scale)); height: calc(8.5in * var(--slide-scale)); }
|
|
73
|
+
.slide[data-size="linkedin-banner"] { width: calc(1584px * var(--slide-scale)); height: calc(396px * var(--slide-scale)); }
|
|
73
74
|
`.trim();
|
|
74
75
|
var dynamicSizeStyles = `
|
|
75
76
|
.slide[data-size="16x9"] { width: 100%; height: auto; aspect-ratio: 16 / 9; }
|
|
@@ -98,6 +99,40 @@ var printSizeStyles = `
|
|
|
98
99
|
@page { size: Letter landscape; margin: 0; }
|
|
99
100
|
}
|
|
100
101
|
`.trim();
|
|
102
|
+
var hoverableSelector = [
|
|
103
|
+
'[data-node-type]:not([data-node-type="slide"])',
|
|
104
|
+
"p",
|
|
105
|
+
"h1",
|
|
106
|
+
"h2",
|
|
107
|
+
"h3",
|
|
108
|
+
"h4",
|
|
109
|
+
"h5",
|
|
110
|
+
"h6",
|
|
111
|
+
"blockquote",
|
|
112
|
+
"ul",
|
|
113
|
+
"ol",
|
|
114
|
+
"li",
|
|
115
|
+
"pre",
|
|
116
|
+
"figure",
|
|
117
|
+
"table"
|
|
118
|
+
].join(", ");
|
|
119
|
+
var hoverOutlineStyles = `
|
|
120
|
+
.slide[data-hover-outline="on"]
|
|
121
|
+
:where(${hoverableSelector}):hover:not(:has(:hover)) {
|
|
122
|
+
outline: var(--slide-hover-outline-width, 1.5px) solid var(--slide-hover-outline-color, rgba(59, 130, 246, 0.65));
|
|
123
|
+
outline-offset: var(--slide-hover-outline-offset, 4px);
|
|
124
|
+
transition: outline-color 120ms ease, outline-width 120ms ease;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* Cascade: when hovering a container, outline its descendant blocks too */
|
|
128
|
+
.slide[data-hover-outline="on"][data-hover-outline-cascade="on"]
|
|
129
|
+
:where(${hoverableSelector}):hover
|
|
130
|
+
:where(${hoverableSelector}) {
|
|
131
|
+
outline: var(--slide-hover-outline-width, 1.5px) solid var(--slide-hover-outline-color, rgba(59, 130, 246, 0.65));
|
|
132
|
+
outline-offset: var(--slide-hover-outline-offset, 4px);
|
|
133
|
+
transition: outline-color 120ms ease, outline-width 120ms ease;
|
|
134
|
+
}
|
|
135
|
+
`.trim();
|
|
101
136
|
var SlidePluginKey = new import_state.PluginKey("slide");
|
|
102
137
|
var Slide = import_core.Node.create({
|
|
103
138
|
name: "slide",
|
|
@@ -112,7 +147,9 @@ var Slide = import_core.Node.create({
|
|
|
112
147
|
renderMode: "fixed",
|
|
113
148
|
defaultSize: "16x9",
|
|
114
149
|
injectPrintCSS: true,
|
|
115
|
-
injectNonce: void 0
|
|
150
|
+
injectNonce: void 0,
|
|
151
|
+
hoverOutline: false,
|
|
152
|
+
hoverOutlineCascade: false
|
|
116
153
|
};
|
|
117
154
|
},
|
|
118
155
|
addAttributes() {
|
|
@@ -164,6 +201,9 @@ var Slide = import_core.Node.create({
|
|
|
164
201
|
...rest
|
|
165
202
|
} = merged;
|
|
166
203
|
const styleParts = [];
|
|
204
|
+
const hoverEnabled = this.options.hoverOutline !== false;
|
|
205
|
+
const hoverConfig = this.options.hoverOutline === true ? {} : this.options.hoverOutline || {};
|
|
206
|
+
const hoverCascade = !!this.options.hoverOutlineCascade;
|
|
167
207
|
if (backgroundColor) {
|
|
168
208
|
styleParts.push(`--slide-bg-color: ${backgroundColor}`);
|
|
169
209
|
}
|
|
@@ -177,6 +217,16 @@ var Slide = import_core.Node.create({
|
|
|
177
217
|
if (backgroundOverlayOpacity != null) {
|
|
178
218
|
styleParts.push(`--slide-bg-overlay-opacity: ${backgroundOverlayOpacity}`);
|
|
179
219
|
}
|
|
220
|
+
if (hoverEnabled) {
|
|
221
|
+
const {
|
|
222
|
+
color = "rgba(59, 130, 246, 0.65)",
|
|
223
|
+
width = "1.5px",
|
|
224
|
+
offset = "4px"
|
|
225
|
+
} = hoverConfig;
|
|
226
|
+
styleParts.push(`--slide-hover-outline-color: ${color}`);
|
|
227
|
+
styleParts.push(`--slide-hover-outline-width: ${width}`);
|
|
228
|
+
styleParts.push(`--slide-hover-outline-offset: ${offset}`);
|
|
229
|
+
}
|
|
180
230
|
const style = [rest.style, styleParts.join("; ")].filter(Boolean).join("; ");
|
|
181
231
|
const className = [rest.class, rest.className, "slide"].filter(Boolean).join(" ");
|
|
182
232
|
delete rest.className;
|
|
@@ -188,6 +238,8 @@ var Slide = import_core.Node.create({
|
|
|
188
238
|
class: className || "slide",
|
|
189
239
|
"data-node-type": "slide",
|
|
190
240
|
"data-bg-mode": backgroundMode || "none",
|
|
241
|
+
"data-hover-outline": hoverEnabled ? "on" : void 0,
|
|
242
|
+
"data-hover-outline-cascade": hoverCascade ? "on" : void 0,
|
|
191
243
|
style: style || void 0
|
|
192
244
|
},
|
|
193
245
|
0
|
|
@@ -211,6 +263,13 @@ var Slide = import_core.Node.create({
|
|
|
211
263
|
(0, import_core.createStyleTag)(printCss, this.options.injectNonce, "slide-print");
|
|
212
264
|
}
|
|
213
265
|
}
|
|
266
|
+
if (this.options.hoverOutline !== false) {
|
|
267
|
+
(0, import_core.createStyleTag)(
|
|
268
|
+
hoverOutlineStyles,
|
|
269
|
+
this.options.injectNonce,
|
|
270
|
+
"slide-hover-outline"
|
|
271
|
+
);
|
|
272
|
+
}
|
|
214
273
|
}
|
|
215
274
|
return {};
|
|
216
275
|
},
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/slide.ts"],"sourcesContent":["export { Slide } from \"./slide\";\nexport type { SlideOptions } from \"./slide\";\n","import { Node, mergeAttributes, createStyleTag } from \"@blockslides/core\";\nimport { Plugin, PluginKey } from \"@blockslides/pm/state\";\n\nconst slideStyles = `\n.slide {\n position: relative;\n height: var(--slide-height, 100%);\n min-height: var(--slide-min-height, 250px);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n background-color: var(--slide-bg);\n border-radius: var(--slide-border-radius);\n box-shadow: var(--slide-shadow);\n margin-bottom: var(--slide-margin-bottom);\n}\n\n/* Background helpers (driven by data attributes + inline CSS vars) */\n.slide[data-bg-mode=\"color\"] {\n background-color: var(--slide-bg-color, var(--slide-bg));\n}\n\n.slide[data-bg-mode=\"image\"],\n.slide[data-bg-mode=\"imageOverlay\"] {\n background-size: cover;\n background-repeat: no-repeat;\n background-position: center;\n}\n\n.slide[data-bg-mode=\"imageOverlay\"]::before {\n content: \"\";\n position: absolute;\n inset: 0;\n pointer-events: none;\n background-color: var(--slide-bg-overlay-color, #000);\n opacity: var(--slide-bg-overlay-opacity, 0.35);\n}\n`;\n\nconst fixedSizeStyles = `\n.slide[data-size=\"16x9\"] { width: 1920px; height: 1080px; }\n.slide[data-size=\"4x3\"] { width: 1600px; height: 1200px; }\n.slide[data-size=\"a4-portrait\"] { width: 210mm; height: 297mm; }\n.slide[data-size=\"a4-landscape\"] { width: 297mm; height: 210mm; }\n.slide[data-size=\"letter-portrait\"] { width: 8.5in; height: 11in; }\n.slide[data-size=\"letter-landscape\"] { width: 11in; height: 8.5in; }\n.slide[data-size=\"linkedin-banner\"] { width: 1584px; height: 396px; }\n`.trim();\n\nconst dynamicSizeStyles = `\n.slide[data-size=\"16x9\"] { width: 100%; height: auto; aspect-ratio: 16 / 9; }\n.slide[data-size=\"4x3\"] { width: 100%; height: auto; aspect-ratio: 4 / 3; }\n.slide[data-size=\"a4-portrait\"] { width: 100%; height: auto; aspect-ratio: 210 / 297; }\n.slide[data-size=\"a4-landscape\"] { width: 100%; height: auto; aspect-ratio: 297 / 210; }\n.slide[data-size=\"letter-portrait\"] { width: 100%; height: auto; aspect-ratio: 8.5 / 11; }\n.slide[data-size=\"letter-landscape\"] { width: 100%; height: auto; aspect-ratio: 11 / 8.5; }\n.slide[data-size=\"linkedin-banner\"] { width: 100%; height: auto; aspect-ratio: 1584 / 396; }\n`.trim();\n\nconst printSizeStyles = `\n@media print {\n .slide[data-size=\"a4-portrait\"] { width: 210mm; height: 297mm; }\n @page { size: A4 portrait; margin: 0; }\n}\n@media print {\n .slide[data-size=\"a4-landscape\"] { width: 297mm; height: 210mm; }\n @page { size: A4 landscape; margin: 0; }\n}\n@media print {\n .slide[data-size=\"letter-portrait\"] { width: 8.5in; height: 11in; }\n @page { size: Letter portrait; margin: 0; }\n}\n@media print {\n .slide[data-size=\"letter-landscape\"] { width: 11in; height: 8.5in; }\n @page { size: Letter landscape; margin: 0; }\n}\n`.trim();\n\nexport interface SlideOptions {\n /**\n * The HTML attributes for a slide node.\n * @default {}\n * @example { class: 'foo' }\n */\n HTMLAttributes: Record<string, any>;\n /**\n * Whether to inject CSS styles\n * @default true\n */\n injectCSS: boolean;\n /**\n * Render mode for sizing\n * - fixed: width/height set from size registry (mm/in/px)\n * - dynamic: width:100% with preserved aspect ratio\n * @default 'fixed'\n */\n renderMode: \"fixed\" | \"dynamic\";\n /**\n * Default size applied when attrs.size is absent\n * @default '16x9'\n */\n defaultSize: \"16x9\" | \"4x3\" | \"a4-portrait\" | \"a4-landscape\" | \"letter-portrait\" | \"letter-landscape\" | \"linkedin-banner\";\n /**\n * Inject @media print/@page CSS for paper sizes\n * @default true\n */\n injectPrintCSS: boolean;\n /**\n * Content Security Policy nonce\n */\n injectNonce?: string;\n}\n\nconst SlidePluginKey = new PluginKey(\"slide\");\n\nexport const Slide = Node.create<SlideOptions>({\n name: \"slide\",\n isolating: true,\n content: \"row+\",\n\n group: \"slide\",\n\n defining: true,\n\n addOptions() {\n return {\n HTMLAttributes: {},\n injectCSS: true,\n renderMode: \"fixed\",\n defaultSize: \"16x9\",\n injectPrintCSS: true,\n injectNonce: undefined,\n };\n },\n\n addAttributes() {\n return {\n size: {\n default: this.options.defaultSize,\n parseHTML: (element) => element.getAttribute(\"data-size\") || this.options.defaultSize,\n renderHTML: (attributes) => {\n if (!attributes.size) {\n return { \"data-size\": this.options.defaultSize };\n }\n return { \"data-size\": attributes.size };\n },\n },\n className: {\n default: \"\",\n },\n id: {\n default: null,\n },\n backgroundMode: {\n default: \"none\",\n },\n backgroundColor: {\n default: null,\n },\n backgroundImage: {\n default: null,\n },\n backgroundOverlayColor: {\n default: null,\n },\n backgroundOverlayOpacity: {\n default: null,\n },\n };\n },\n\n parseHTML() {\n return [{ tag: \"div.slide\" }];\n },\n\n renderHTML({ HTMLAttributes }) {\n const merged = mergeAttributes(this.options.HTMLAttributes, HTMLAttributes);\n const {\n backgroundMode,\n backgroundColor,\n backgroundImage,\n backgroundOverlayColor,\n backgroundOverlayOpacity,\n ...rest\n } = merged;\n\n const styleParts: string[] = [];\n\n if (backgroundColor) {\n styleParts.push(`--slide-bg-color: ${backgroundColor}`);\n }\n\n if (backgroundImage) {\n const escaped = String(backgroundImage).replace(/\"/g, '\\\\\"');\n styleParts.push(`background-image: url(\"${escaped}\")`);\n }\n\n if (backgroundOverlayColor) {\n styleParts.push(`--slide-bg-overlay-color: ${backgroundOverlayColor}`);\n }\n\n if (backgroundOverlayOpacity != null) {\n styleParts.push(`--slide-bg-overlay-opacity: ${backgroundOverlayOpacity}`);\n }\n\n const style = [rest.style, styleParts.join(\"; \")].filter(Boolean).join(\"; \");\n\n const className = [rest.class, rest.className, \"slide\"].filter(Boolean).join(\" \");\n\n delete (rest as any).className;\n delete (rest as any).class;\n\n return [\n \"div\",\n {\n ...rest,\n class: className || \"slide\",\n \"data-node-type\": \"slide\",\n \"data-bg-mode\": backgroundMode || \"none\",\n style: style || undefined,\n },\n 0,\n ];\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: SlidePluginKey,\n state: {\n init: () => {\n if (this.options.injectCSS && typeof document !== \"undefined\") {\n createStyleTag(slideStyles, this.options.injectNonce, \"slide\");\n const sizingCss =\n this.options.renderMode === \"dynamic\" ? dynamicSizeStyles : fixedSizeStyles;\n if (sizingCss) {\n createStyleTag(sizingCss, this.options.injectNonce, \"slide-sizes\");\n }\n if (this.options.injectPrintCSS) {\n const printCss = printSizeStyles;\n if (printCss) {\n createStyleTag(printCss, this.options.injectNonce, \"slide-print\");\n }\n }\n }\n return {};\n },\n apply: (_tr, pluginState: Record<string, never>) => pluginState,\n },\n }),\n ];\n },\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/slide.ts"],"sourcesContent":["export { Slide } from \"./slide\";\nexport type { SlideOptions } from \"./slide\";\n","import { Node, mergeAttributes, createStyleTag } from \"@blockslides/core\";\nimport { Plugin, PluginKey } from \"@blockslides/pm/state\";\n\nconst slideStyles = `\n.slide {\n position: relative;\n height: var(--slide-height, 100%);\n min-height: var(--slide-min-height, 250px);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n background-color: var(--slide-bg);\n border-radius: var(--slide-border-radius);\n box-shadow: var(--slide-shadow);\n margin-bottom: var(--slide-margin-bottom);\n}\n\n/* Background helpers (driven by data attributes + inline CSS vars) */\n.slide[data-bg-mode=\"color\"] {\n background-color: var(--slide-bg-color, var(--slide-bg));\n}\n\n.slide[data-bg-mode=\"image\"],\n.slide[data-bg-mode=\"imageOverlay\"] {\n background-size: cover;\n background-repeat: no-repeat;\n background-position: center;\n}\n\n.slide[data-bg-mode=\"imageOverlay\"]::before {\n content: \"\";\n position: absolute;\n inset: 0;\n pointer-events: none;\n background-color: var(--slide-bg-overlay-color, #000);\n opacity: var(--slide-bg-overlay-opacity, 0.35);\n}\n`;\n\nconst fixedSizeStyles = `\n.slide { --slide-scale: 1; }\n.slide[data-size=\"16x9\"] { width: calc(1920px * var(--slide-scale)); height: calc(1080px * var(--slide-scale)); }\n.slide[data-size=\"4x3\"] { width: calc(1600px * var(--slide-scale)); height: calc(1200px * var(--slide-scale)); }\n.slide[data-size=\"a4-portrait\"] { width: calc(210mm * var(--slide-scale)); height: calc(297mm * var(--slide-scale)); }\n.slide[data-size=\"a4-landscape\"] { width: calc(297mm * var(--slide-scale)); height: calc(210mm * var(--slide-scale)); }\n.slide[data-size=\"letter-portrait\"] { width: calc(8.5in * var(--slide-scale)); height: calc(11in * var(--slide-scale)); }\n.slide[data-size=\"letter-landscape\"] { width: calc(11in * var(--slide-scale)); height: calc(8.5in * var(--slide-scale)); }\n.slide[data-size=\"linkedin-banner\"] { width: calc(1584px * var(--slide-scale)); height: calc(396px * var(--slide-scale)); }\n`.trim();\n\nconst dynamicSizeStyles = `\n.slide[data-size=\"16x9\"] { width: 100%; height: auto; aspect-ratio: 16 / 9; }\n.slide[data-size=\"4x3\"] { width: 100%; height: auto; aspect-ratio: 4 / 3; }\n.slide[data-size=\"a4-portrait\"] { width: 100%; height: auto; aspect-ratio: 210 / 297; }\n.slide[data-size=\"a4-landscape\"] { width: 100%; height: auto; aspect-ratio: 297 / 210; }\n.slide[data-size=\"letter-portrait\"] { width: 100%; height: auto; aspect-ratio: 8.5 / 11; }\n.slide[data-size=\"letter-landscape\"] { width: 100%; height: auto; aspect-ratio: 11 / 8.5; }\n.slide[data-size=\"linkedin-banner\"] { width: 100%; height: auto; aspect-ratio: 1584 / 396; }\n`.trim();\n\nconst printSizeStyles = `\n@media print {\n .slide[data-size=\"a4-portrait\"] { width: 210mm; height: 297mm; }\n @page { size: A4 portrait; margin: 0; }\n}\n@media print {\n .slide[data-size=\"a4-landscape\"] { width: 297mm; height: 210mm; }\n @page { size: A4 landscape; margin: 0; }\n}\n@media print {\n .slide[data-size=\"letter-portrait\"] { width: 8.5in; height: 11in; }\n @page { size: Letter portrait; margin: 0; }\n}\n@media print {\n .slide[data-size=\"letter-landscape\"] { width: 11in; height: 8.5in; }\n @page { size: Letter landscape; margin: 0; }\n}\n`.trim();\n\nconst hoverableSelector = [\n '[data-node-type]:not([data-node-type=\"slide\"])',\n \"p\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"blockquote\",\n \"ul\",\n \"ol\",\n \"li\",\n \"pre\",\n \"figure\",\n \"table\",\n].join(\", \");\n\nconst hoverOutlineStyles = `\n.slide[data-hover-outline=\"on\"]\n :where(${hoverableSelector}):hover:not(:has(:hover)) {\n outline: var(--slide-hover-outline-width, 1.5px) solid var(--slide-hover-outline-color, rgba(59, 130, 246, 0.65));\n outline-offset: var(--slide-hover-outline-offset, 4px);\n transition: outline-color 120ms ease, outline-width 120ms ease;\n}\n\n/* Cascade: when hovering a container, outline its descendant blocks too */\n.slide[data-hover-outline=\"on\"][data-hover-outline-cascade=\"on\"]\n :where(${hoverableSelector}):hover\n :where(${hoverableSelector}) {\n outline: var(--slide-hover-outline-width, 1.5px) solid var(--slide-hover-outline-color, rgba(59, 130, 246, 0.65));\n outline-offset: var(--slide-hover-outline-offset, 4px);\n transition: outline-color 120ms ease, outline-width 120ms ease;\n}\n`.trim();\n\nexport interface SlideOptions {\n /**\n * The HTML attributes for a slide node.\n * @default {}\n * @example { class: 'foo' }\n */\n HTMLAttributes: Record<string, any>;\n /**\n * Whether to inject CSS styles\n * @default true\n */\n injectCSS: boolean;\n /**\n * Render mode for sizing\n * - fixed: width/height set from size registry (mm/in/px)\n * - dynamic: width:100% with preserved aspect ratio\n * @default 'fixed'\n */\n renderMode: \"fixed\" | \"dynamic\";\n /**\n * Default size applied when attrs.size is absent\n * @default '16x9'\n */\n defaultSize: \"16x9\" | \"4x3\" | \"a4-portrait\" | \"a4-landscape\" | \"letter-portrait\" | \"letter-landscape\" | \"linkedin-banner\";\n /**\n * Inject @media print/@page CSS for paper sizes\n * @default true\n */\n injectPrintCSS: boolean;\n /**\n * Content Security Policy nonce\n */\n injectNonce?: string;\n /**\n * Hover outline highlight for nodes within a slide.\n * - false: disabled (default)\n * - true: enabled with defaults\n * - object: enabled with overrides\n */\n hoverOutline:\n | false\n | true\n | {\n color?: string;\n width?: string;\n offset?: string;\n };\n /**\n * When enabled, hovering a container outlines its descendant blocks too.\n * @default false\n */\n hoverOutlineCascade?: boolean;\n}\n\nconst SlidePluginKey = new PluginKey(\"slide\");\n\nexport const Slide = Node.create<SlideOptions>({\n name: \"slide\",\n isolating: true,\n content: \"row+\",\n\n group: \"slide\",\n\n defining: true,\n\n addOptions() {\n return {\n HTMLAttributes: {},\n injectCSS: true,\n renderMode: \"fixed\",\n defaultSize: \"16x9\",\n injectPrintCSS: true,\n injectNonce: undefined,\n hoverOutline: false,\n hoverOutlineCascade: false,\n };\n },\n\n addAttributes() {\n return {\n size: {\n default: this.options.defaultSize,\n parseHTML: (element) => element.getAttribute(\"data-size\") || this.options.defaultSize,\n renderHTML: (attributes) => {\n if (!attributes.size) {\n return { \"data-size\": this.options.defaultSize };\n }\n return { \"data-size\": attributes.size };\n },\n },\n className: {\n default: \"\",\n },\n id: {\n default: null,\n },\n backgroundMode: {\n default: \"none\",\n },\n backgroundColor: {\n default: null,\n },\n backgroundImage: {\n default: null,\n },\n backgroundOverlayColor: {\n default: null,\n },\n backgroundOverlayOpacity: {\n default: null,\n },\n };\n },\n\n parseHTML() {\n return [{ tag: \"div.slide\" }];\n },\n\n renderHTML({ HTMLAttributes }) {\n const merged = mergeAttributes(this.options.HTMLAttributes, HTMLAttributes);\n const {\n backgroundMode,\n backgroundColor,\n backgroundImage,\n backgroundOverlayColor,\n backgroundOverlayOpacity,\n ...rest\n } = merged;\n\n const styleParts: string[] = [];\n\n const hoverEnabled = this.options.hoverOutline !== false;\n const hoverConfig =\n this.options.hoverOutline === true ? {} : (this.options.hoverOutline || {});\n const hoverCascade = !!this.options.hoverOutlineCascade;\n\n if (backgroundColor) {\n styleParts.push(`--slide-bg-color: ${backgroundColor}`);\n }\n\n if (backgroundImage) {\n const escaped = String(backgroundImage).replace(/\"/g, '\\\\\"');\n styleParts.push(`background-image: url(\"${escaped}\")`);\n }\n\n if (backgroundOverlayColor) {\n styleParts.push(`--slide-bg-overlay-color: ${backgroundOverlayColor}`);\n }\n\n if (backgroundOverlayOpacity != null) {\n styleParts.push(`--slide-bg-overlay-opacity: ${backgroundOverlayOpacity}`);\n }\n\n if (hoverEnabled) {\n const {\n color = \"rgba(59, 130, 246, 0.65)\",\n width = \"1.5px\",\n offset = \"4px\",\n } = hoverConfig;\n styleParts.push(`--slide-hover-outline-color: ${color}`);\n styleParts.push(`--slide-hover-outline-width: ${width}`);\n styleParts.push(`--slide-hover-outline-offset: ${offset}`);\n }\n\n const style = [rest.style, styleParts.join(\"; \")].filter(Boolean).join(\"; \");\n\n const className = [rest.class, rest.className, \"slide\"].filter(Boolean).join(\" \");\n\n delete (rest as any).className;\n delete (rest as any).class;\n\n return [\n \"div\",\n {\n ...rest,\n class: className || \"slide\",\n \"data-node-type\": \"slide\",\n \"data-bg-mode\": backgroundMode || \"none\",\n \"data-hover-outline\": hoverEnabled ? \"on\" : undefined,\n \"data-hover-outline-cascade\": hoverCascade ? \"on\" : undefined,\n style: style || undefined,\n },\n 0,\n ];\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: SlidePluginKey,\n state: {\n init: () => {\n if (this.options.injectCSS && typeof document !== \"undefined\") {\n createStyleTag(slideStyles, this.options.injectNonce, \"slide\");\n const sizingCss =\n this.options.renderMode === \"dynamic\" ? dynamicSizeStyles : fixedSizeStyles;\n if (sizingCss) {\n createStyleTag(sizingCss, this.options.injectNonce, \"slide-sizes\");\n }\n if (this.options.injectPrintCSS) {\n const printCss = printSizeStyles;\n if (printCss) {\n createStyleTag(printCss, this.options.injectNonce, \"slide-print\");\n }\n }\n if (this.options.hoverOutline !== false) {\n createStyleTag(\n hoverOutlineStyles,\n this.options.injectNonce,\n \"slide-hover-outline\"\n );\n }\n }\n return {};\n },\n apply: (_tr, pluginState: Record<string, never>) => pluginState,\n },\n }),\n ];\n },\n});\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -37,13 +37,14 @@ var slideStyles = `
|
|
|
37
37
|
}
|
|
38
38
|
`;
|
|
39
39
|
var fixedSizeStyles = `
|
|
40
|
-
.slide
|
|
41
|
-
.slide[data-size="
|
|
42
|
-
.slide[data-size="
|
|
43
|
-
.slide[data-size="a4-
|
|
44
|
-
.slide[data-size="
|
|
45
|
-
.slide[data-size="letter-
|
|
46
|
-
.slide[data-size="
|
|
40
|
+
.slide { --slide-scale: 1; }
|
|
41
|
+
.slide[data-size="16x9"] { width: calc(1920px * var(--slide-scale)); height: calc(1080px * var(--slide-scale)); }
|
|
42
|
+
.slide[data-size="4x3"] { width: calc(1600px * var(--slide-scale)); height: calc(1200px * var(--slide-scale)); }
|
|
43
|
+
.slide[data-size="a4-portrait"] { width: calc(210mm * var(--slide-scale)); height: calc(297mm * var(--slide-scale)); }
|
|
44
|
+
.slide[data-size="a4-landscape"] { width: calc(297mm * var(--slide-scale)); height: calc(210mm * var(--slide-scale)); }
|
|
45
|
+
.slide[data-size="letter-portrait"] { width: calc(8.5in * var(--slide-scale)); height: calc(11in * var(--slide-scale)); }
|
|
46
|
+
.slide[data-size="letter-landscape"] { width: calc(11in * var(--slide-scale)); height: calc(8.5in * var(--slide-scale)); }
|
|
47
|
+
.slide[data-size="linkedin-banner"] { width: calc(1584px * var(--slide-scale)); height: calc(396px * var(--slide-scale)); }
|
|
47
48
|
`.trim();
|
|
48
49
|
var dynamicSizeStyles = `
|
|
49
50
|
.slide[data-size="16x9"] { width: 100%; height: auto; aspect-ratio: 16 / 9; }
|
|
@@ -72,6 +73,40 @@ var printSizeStyles = `
|
|
|
72
73
|
@page { size: Letter landscape; margin: 0; }
|
|
73
74
|
}
|
|
74
75
|
`.trim();
|
|
76
|
+
var hoverableSelector = [
|
|
77
|
+
'[data-node-type]:not([data-node-type="slide"])',
|
|
78
|
+
"p",
|
|
79
|
+
"h1",
|
|
80
|
+
"h2",
|
|
81
|
+
"h3",
|
|
82
|
+
"h4",
|
|
83
|
+
"h5",
|
|
84
|
+
"h6",
|
|
85
|
+
"blockquote",
|
|
86
|
+
"ul",
|
|
87
|
+
"ol",
|
|
88
|
+
"li",
|
|
89
|
+
"pre",
|
|
90
|
+
"figure",
|
|
91
|
+
"table"
|
|
92
|
+
].join(", ");
|
|
93
|
+
var hoverOutlineStyles = `
|
|
94
|
+
.slide[data-hover-outline="on"]
|
|
95
|
+
:where(${hoverableSelector}):hover:not(:has(:hover)) {
|
|
96
|
+
outline: var(--slide-hover-outline-width, 1.5px) solid var(--slide-hover-outline-color, rgba(59, 130, 246, 0.65));
|
|
97
|
+
outline-offset: var(--slide-hover-outline-offset, 4px);
|
|
98
|
+
transition: outline-color 120ms ease, outline-width 120ms ease;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* Cascade: when hovering a container, outline its descendant blocks too */
|
|
102
|
+
.slide[data-hover-outline="on"][data-hover-outline-cascade="on"]
|
|
103
|
+
:where(${hoverableSelector}):hover
|
|
104
|
+
:where(${hoverableSelector}) {
|
|
105
|
+
outline: var(--slide-hover-outline-width, 1.5px) solid var(--slide-hover-outline-color, rgba(59, 130, 246, 0.65));
|
|
106
|
+
outline-offset: var(--slide-hover-outline-offset, 4px);
|
|
107
|
+
transition: outline-color 120ms ease, outline-width 120ms ease;
|
|
108
|
+
}
|
|
109
|
+
`.trim();
|
|
75
110
|
var SlidePluginKey = new PluginKey("slide");
|
|
76
111
|
var Slide = Node.create({
|
|
77
112
|
name: "slide",
|
|
@@ -86,7 +121,9 @@ var Slide = Node.create({
|
|
|
86
121
|
renderMode: "fixed",
|
|
87
122
|
defaultSize: "16x9",
|
|
88
123
|
injectPrintCSS: true,
|
|
89
|
-
injectNonce: void 0
|
|
124
|
+
injectNonce: void 0,
|
|
125
|
+
hoverOutline: false,
|
|
126
|
+
hoverOutlineCascade: false
|
|
90
127
|
};
|
|
91
128
|
},
|
|
92
129
|
addAttributes() {
|
|
@@ -138,6 +175,9 @@ var Slide = Node.create({
|
|
|
138
175
|
...rest
|
|
139
176
|
} = merged;
|
|
140
177
|
const styleParts = [];
|
|
178
|
+
const hoverEnabled = this.options.hoverOutline !== false;
|
|
179
|
+
const hoverConfig = this.options.hoverOutline === true ? {} : this.options.hoverOutline || {};
|
|
180
|
+
const hoverCascade = !!this.options.hoverOutlineCascade;
|
|
141
181
|
if (backgroundColor) {
|
|
142
182
|
styleParts.push(`--slide-bg-color: ${backgroundColor}`);
|
|
143
183
|
}
|
|
@@ -151,6 +191,16 @@ var Slide = Node.create({
|
|
|
151
191
|
if (backgroundOverlayOpacity != null) {
|
|
152
192
|
styleParts.push(`--slide-bg-overlay-opacity: ${backgroundOverlayOpacity}`);
|
|
153
193
|
}
|
|
194
|
+
if (hoverEnabled) {
|
|
195
|
+
const {
|
|
196
|
+
color = "rgba(59, 130, 246, 0.65)",
|
|
197
|
+
width = "1.5px",
|
|
198
|
+
offset = "4px"
|
|
199
|
+
} = hoverConfig;
|
|
200
|
+
styleParts.push(`--slide-hover-outline-color: ${color}`);
|
|
201
|
+
styleParts.push(`--slide-hover-outline-width: ${width}`);
|
|
202
|
+
styleParts.push(`--slide-hover-outline-offset: ${offset}`);
|
|
203
|
+
}
|
|
154
204
|
const style = [rest.style, styleParts.join("; ")].filter(Boolean).join("; ");
|
|
155
205
|
const className = [rest.class, rest.className, "slide"].filter(Boolean).join(" ");
|
|
156
206
|
delete rest.className;
|
|
@@ -162,6 +212,8 @@ var Slide = Node.create({
|
|
|
162
212
|
class: className || "slide",
|
|
163
213
|
"data-node-type": "slide",
|
|
164
214
|
"data-bg-mode": backgroundMode || "none",
|
|
215
|
+
"data-hover-outline": hoverEnabled ? "on" : void 0,
|
|
216
|
+
"data-hover-outline-cascade": hoverCascade ? "on" : void 0,
|
|
165
217
|
style: style || void 0
|
|
166
218
|
},
|
|
167
219
|
0
|
|
@@ -185,6 +237,13 @@ var Slide = Node.create({
|
|
|
185
237
|
createStyleTag(printCss, this.options.injectNonce, "slide-print");
|
|
186
238
|
}
|
|
187
239
|
}
|
|
240
|
+
if (this.options.hoverOutline !== false) {
|
|
241
|
+
createStyleTag(
|
|
242
|
+
hoverOutlineStyles,
|
|
243
|
+
this.options.injectNonce,
|
|
244
|
+
"slide-hover-outline"
|
|
245
|
+
);
|
|
246
|
+
}
|
|
188
247
|
}
|
|
189
248
|
return {};
|
|
190
249
|
},
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/slide.ts"],"sourcesContent":["import { Node, mergeAttributes, createStyleTag } from \"@blockslides/core\";\nimport { Plugin, PluginKey } from \"@blockslides/pm/state\";\n\nconst slideStyles = `\n.slide {\n position: relative;\n height: var(--slide-height, 100%);\n min-height: var(--slide-min-height, 250px);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n background-color: var(--slide-bg);\n border-radius: var(--slide-border-radius);\n box-shadow: var(--slide-shadow);\n margin-bottom: var(--slide-margin-bottom);\n}\n\n/* Background helpers (driven by data attributes + inline CSS vars) */\n.slide[data-bg-mode=\"color\"] {\n background-color: var(--slide-bg-color, var(--slide-bg));\n}\n\n.slide[data-bg-mode=\"image\"],\n.slide[data-bg-mode=\"imageOverlay\"] {\n background-size: cover;\n background-repeat: no-repeat;\n background-position: center;\n}\n\n.slide[data-bg-mode=\"imageOverlay\"]::before {\n content: \"\";\n position: absolute;\n inset: 0;\n pointer-events: none;\n background-color: var(--slide-bg-overlay-color, #000);\n opacity: var(--slide-bg-overlay-opacity, 0.35);\n}\n`;\n\nconst fixedSizeStyles = `\n.slide[data-size=\"16x9\"] { width: 1920px; height: 1080px; }\n.slide[data-size=\"4x3\"] { width: 1600px; height: 1200px; }\n.slide[data-size=\"a4-portrait\"] { width: 210mm; height: 297mm; }\n.slide[data-size=\"a4-landscape\"] { width: 297mm; height: 210mm; }\n.slide[data-size=\"letter-portrait\"] { width: 8.5in; height: 11in; }\n.slide[data-size=\"letter-landscape\"] { width: 11in; height: 8.5in; }\n.slide[data-size=\"linkedin-banner\"] { width: 1584px; height: 396px; }\n`.trim();\n\nconst dynamicSizeStyles = `\n.slide[data-size=\"16x9\"] { width: 100%; height: auto; aspect-ratio: 16 / 9; }\n.slide[data-size=\"4x3\"] { width: 100%; height: auto; aspect-ratio: 4 / 3; }\n.slide[data-size=\"a4-portrait\"] { width: 100%; height: auto; aspect-ratio: 210 / 297; }\n.slide[data-size=\"a4-landscape\"] { width: 100%; height: auto; aspect-ratio: 297 / 210; }\n.slide[data-size=\"letter-portrait\"] { width: 100%; height: auto; aspect-ratio: 8.5 / 11; }\n.slide[data-size=\"letter-landscape\"] { width: 100%; height: auto; aspect-ratio: 11 / 8.5; }\n.slide[data-size=\"linkedin-banner\"] { width: 100%; height: auto; aspect-ratio: 1584 / 396; }\n`.trim();\n\nconst printSizeStyles = `\n@media print {\n .slide[data-size=\"a4-portrait\"] { width: 210mm; height: 297mm; }\n @page { size: A4 portrait; margin: 0; }\n}\n@media print {\n .slide[data-size=\"a4-landscape\"] { width: 297mm; height: 210mm; }\n @page { size: A4 landscape; margin: 0; }\n}\n@media print {\n .slide[data-size=\"letter-portrait\"] { width: 8.5in; height: 11in; }\n @page { size: Letter portrait; margin: 0; }\n}\n@media print {\n .slide[data-size=\"letter-landscape\"] { width: 11in; height: 8.5in; }\n @page { size: Letter landscape; margin: 0; }\n}\n`.trim();\n\nexport interface SlideOptions {\n /**\n * The HTML attributes for a slide node.\n * @default {}\n * @example { class: 'foo' }\n */\n HTMLAttributes: Record<string, any>;\n /**\n * Whether to inject CSS styles\n * @default true\n */\n injectCSS: boolean;\n /**\n * Render mode for sizing\n * - fixed: width/height set from size registry (mm/in/px)\n * - dynamic: width:100% with preserved aspect ratio\n * @default 'fixed'\n */\n renderMode: \"fixed\" | \"dynamic\";\n /**\n * Default size applied when attrs.size is absent\n * @default '16x9'\n */\n defaultSize: \"16x9\" | \"4x3\" | \"a4-portrait\" | \"a4-landscape\" | \"letter-portrait\" | \"letter-landscape\" | \"linkedin-banner\";\n /**\n * Inject @media print/@page CSS for paper sizes\n * @default true\n */\n injectPrintCSS: boolean;\n /**\n * Content Security Policy nonce\n */\n injectNonce?: string;\n}\n\nconst SlidePluginKey = new PluginKey(\"slide\");\n\nexport const Slide = Node.create<SlideOptions>({\n name: \"slide\",\n isolating: true,\n content: \"row+\",\n\n group: \"slide\",\n\n defining: true,\n\n addOptions() {\n return {\n HTMLAttributes: {},\n injectCSS: true,\n renderMode: \"fixed\",\n defaultSize: \"16x9\",\n injectPrintCSS: true,\n injectNonce: undefined,\n };\n },\n\n addAttributes() {\n return {\n size: {\n default: this.options.defaultSize,\n parseHTML: (element) => element.getAttribute(\"data-size\") || this.options.defaultSize,\n renderHTML: (attributes) => {\n if (!attributes.size) {\n return { \"data-size\": this.options.defaultSize };\n }\n return { \"data-size\": attributes.size };\n },\n },\n className: {\n default: \"\",\n },\n id: {\n default: null,\n },\n backgroundMode: {\n default: \"none\",\n },\n backgroundColor: {\n default: null,\n },\n backgroundImage: {\n default: null,\n },\n backgroundOverlayColor: {\n default: null,\n },\n backgroundOverlayOpacity: {\n default: null,\n },\n };\n },\n\n parseHTML() {\n return [{ tag: \"div.slide\" }];\n },\n\n renderHTML({ HTMLAttributes }) {\n const merged = mergeAttributes(this.options.HTMLAttributes, HTMLAttributes);\n const {\n backgroundMode,\n backgroundColor,\n backgroundImage,\n backgroundOverlayColor,\n backgroundOverlayOpacity,\n ...rest\n } = merged;\n\n const styleParts: string[] = [];\n\n if (backgroundColor) {\n styleParts.push(`--slide-bg-color: ${backgroundColor}`);\n }\n\n if (backgroundImage) {\n const escaped = String(backgroundImage).replace(/\"/g, '\\\\\"');\n styleParts.push(`background-image: url(\"${escaped}\")`);\n }\n\n if (backgroundOverlayColor) {\n styleParts.push(`--slide-bg-overlay-color: ${backgroundOverlayColor}`);\n }\n\n if (backgroundOverlayOpacity != null) {\n styleParts.push(`--slide-bg-overlay-opacity: ${backgroundOverlayOpacity}`);\n }\n\n const style = [rest.style, styleParts.join(\"; \")].filter(Boolean).join(\"; \");\n\n const className = [rest.class, rest.className, \"slide\"].filter(Boolean).join(\" \");\n\n delete (rest as any).className;\n delete (rest as any).class;\n\n return [\n \"div\",\n {\n ...rest,\n class: className || \"slide\",\n \"data-node-type\": \"slide\",\n \"data-bg-mode\": backgroundMode || \"none\",\n style: style || undefined,\n },\n 0,\n ];\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: SlidePluginKey,\n state: {\n init: () => {\n if (this.options.injectCSS && typeof document !== \"undefined\") {\n createStyleTag(slideStyles, this.options.injectNonce, \"slide\");\n const sizingCss =\n this.options.renderMode === \"dynamic\" ? dynamicSizeStyles : fixedSizeStyles;\n if (sizingCss) {\n createStyleTag(sizingCss, this.options.injectNonce, \"slide-sizes\");\n }\n if (this.options.injectPrintCSS) {\n const printCss = printSizeStyles;\n if (printCss) {\n createStyleTag(printCss, this.options.injectNonce, \"slide-print\");\n }\n }\n }\n return {};\n },\n apply: (_tr, pluginState: Record<string, never>) => pluginState,\n },\n }),\n ];\n },\n});\n"],"mappings":";AAAA,SAAS,MAAM,iBAAiB,sBAAsB;AACtD,SAAS,QAAQ,iBAAiB;AAElC,IAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoCpB,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQtB,KAAK;AAEP,IAAM,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQxB,KAAK;AAEP,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBtB,KAAK;AAqCP,IAAM,iBAAiB,IAAI,UAAU,OAAO;AAErC,IAAM,QAAQ,KAAK,OAAqB;AAAA,EAC7C,MAAM;AAAA,EACN,WAAW;AAAA,EACX,SAAS;AAAA,EAET,OAAO;AAAA,EAEP,UAAU;AAAA,EAEV,aAAa;AACX,WAAO;AAAA,MACL,gBAAgB,CAAC;AAAA,MACjB,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EAEA,gBAAgB;AACd,WAAO;AAAA,MACL,MAAM;AAAA,QACJ,SAAS,KAAK,QAAQ;AAAA,QACtB,WAAW,CAAC,YAAY,QAAQ,aAAa,WAAW,KAAK,KAAK,QAAQ;AAAA,QAC1E,YAAY,CAAC,eAAe;AAC1B,cAAI,CAAC,WAAW,MAAM;AACpB,mBAAO,EAAE,aAAa,KAAK,QAAQ,YAAY;AAAA,UACjD;AACA,iBAAO,EAAE,aAAa,WAAW,KAAK;AAAA,QACxC;AAAA,MACF;AAAA,MACA,WAAW;AAAA,QACT,SAAS;AAAA,MACX;AAAA,MACA,IAAI;AAAA,QACF,SAAS;AAAA,MACX;AAAA,MACA,gBAAgB;AAAA,QACd,SAAS;AAAA,MACX;AAAA,MACA,iBAAiB;AAAA,QACf,SAAS;AAAA,MACX;AAAA,MACA,iBAAiB;AAAA,QACf,SAAS;AAAA,MACX;AAAA,MACA,wBAAwB;AAAA,QACtB,SAAS;AAAA,MACX;AAAA,MACA,0BAA0B;AAAA,QACxB,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EAEA,YAAY;AACV,WAAO,CAAC,EAAE,KAAK,YAAY,CAAC;AAAA,EAC9B;AAAA,EAEA,WAAW,EAAE,eAAe,GAAG;AAC7B,UAAM,SAAS,gBAAgB,KAAK,QAAQ,gBAAgB,cAAc;AAC1E,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG;AAAA,IACL,IAAI;AAEJ,UAAM,aAAuB,CAAC;AAE9B,QAAI,iBAAiB;AACnB,iBAAW,KAAK,qBAAqB,eAAe,EAAE;AAAA,IACxD;AAEA,QAAI,iBAAiB;AACnB,YAAM,UAAU,OAAO,eAAe,EAAE,QAAQ,MAAM,KAAK;AAC3D,iBAAW,KAAK,0BAA0B,OAAO,IAAI;AAAA,IACvD;AAEA,QAAI,wBAAwB;AAC1B,iBAAW,KAAK,6BAA6B,sBAAsB,EAAE;AAAA,IACvE;AAEA,QAAI,4BAA4B,MAAM;AACpC,iBAAW,KAAK,+BAA+B,wBAAwB,EAAE;AAAA,IAC3E;AAEA,UAAM,QAAQ,CAAC,KAAK,OAAO,WAAW,KAAK,IAAI,CAAC,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AAE3E,UAAM,YAAY,CAAC,KAAK,OAAO,KAAK,WAAW,OAAO,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAEhF,WAAQ,KAAa;AACrB,WAAQ,KAAa;AAErB,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,GAAG;AAAA,QACH,OAAO,aAAa;AAAA,QACpB,kBAAkB;AAAA,QAClB,gBAAgB,kBAAkB;AAAA,QAClC,OAAO,SAAS;AAAA,MAClB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,WAAO;AAAA,MACL,IAAI,OAAO;AAAA,QACT,KAAK;AAAA,QACL,OAAO;AAAA,UACL,MAAM,MAAM;AACV,gBAAI,KAAK,QAAQ,aAAa,OAAO,aAAa,aAAa;AAC7D,6BAAe,aAAa,KAAK,QAAQ,aAAa,OAAO;AAC7D,oBAAM,YACJ,KAAK,QAAQ,eAAe,YAAY,oBAAoB;AAC9D,kBAAI,WAAW;AACb,+BAAe,WAAW,KAAK,QAAQ,aAAa,aAAa;AAAA,cACnE;AACA,kBAAI,KAAK,QAAQ,gBAAgB;AAC/B,sBAAM,WAAW;AACjB,oBAAI,UAAU;AACZ,iCAAe,UAAU,KAAK,QAAQ,aAAa,aAAa;AAAA,gBAClE;AAAA,cACF;AAAA,YACF;AACA,mBAAO,CAAC;AAAA,UACV;AAAA,UACA,OAAO,CAAC,KAAK,gBAAuC;AAAA,QACtD;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/slide.ts"],"sourcesContent":["import { Node, mergeAttributes, createStyleTag } from \"@blockslides/core\";\nimport { Plugin, PluginKey } from \"@blockslides/pm/state\";\n\nconst slideStyles = `\n.slide {\n position: relative;\n height: var(--slide-height, 100%);\n min-height: var(--slide-min-height, 250px);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n background-color: var(--slide-bg);\n border-radius: var(--slide-border-radius);\n box-shadow: var(--slide-shadow);\n margin-bottom: var(--slide-margin-bottom);\n}\n\n/* Background helpers (driven by data attributes + inline CSS vars) */\n.slide[data-bg-mode=\"color\"] {\n background-color: var(--slide-bg-color, var(--slide-bg));\n}\n\n.slide[data-bg-mode=\"image\"],\n.slide[data-bg-mode=\"imageOverlay\"] {\n background-size: cover;\n background-repeat: no-repeat;\n background-position: center;\n}\n\n.slide[data-bg-mode=\"imageOverlay\"]::before {\n content: \"\";\n position: absolute;\n inset: 0;\n pointer-events: none;\n background-color: var(--slide-bg-overlay-color, #000);\n opacity: var(--slide-bg-overlay-opacity, 0.35);\n}\n`;\n\nconst fixedSizeStyles = `\n.slide { --slide-scale: 1; }\n.slide[data-size=\"16x9\"] { width: calc(1920px * var(--slide-scale)); height: calc(1080px * var(--slide-scale)); }\n.slide[data-size=\"4x3\"] { width: calc(1600px * var(--slide-scale)); height: calc(1200px * var(--slide-scale)); }\n.slide[data-size=\"a4-portrait\"] { width: calc(210mm * var(--slide-scale)); height: calc(297mm * var(--slide-scale)); }\n.slide[data-size=\"a4-landscape\"] { width: calc(297mm * var(--slide-scale)); height: calc(210mm * var(--slide-scale)); }\n.slide[data-size=\"letter-portrait\"] { width: calc(8.5in * var(--slide-scale)); height: calc(11in * var(--slide-scale)); }\n.slide[data-size=\"letter-landscape\"] { width: calc(11in * var(--slide-scale)); height: calc(8.5in * var(--slide-scale)); }\n.slide[data-size=\"linkedin-banner\"] { width: calc(1584px * var(--slide-scale)); height: calc(396px * var(--slide-scale)); }\n`.trim();\n\nconst dynamicSizeStyles = `\n.slide[data-size=\"16x9\"] { width: 100%; height: auto; aspect-ratio: 16 / 9; }\n.slide[data-size=\"4x3\"] { width: 100%; height: auto; aspect-ratio: 4 / 3; }\n.slide[data-size=\"a4-portrait\"] { width: 100%; height: auto; aspect-ratio: 210 / 297; }\n.slide[data-size=\"a4-landscape\"] { width: 100%; height: auto; aspect-ratio: 297 / 210; }\n.slide[data-size=\"letter-portrait\"] { width: 100%; height: auto; aspect-ratio: 8.5 / 11; }\n.slide[data-size=\"letter-landscape\"] { width: 100%; height: auto; aspect-ratio: 11 / 8.5; }\n.slide[data-size=\"linkedin-banner\"] { width: 100%; height: auto; aspect-ratio: 1584 / 396; }\n`.trim();\n\nconst printSizeStyles = `\n@media print {\n .slide[data-size=\"a4-portrait\"] { width: 210mm; height: 297mm; }\n @page { size: A4 portrait; margin: 0; }\n}\n@media print {\n .slide[data-size=\"a4-landscape\"] { width: 297mm; height: 210mm; }\n @page { size: A4 landscape; margin: 0; }\n}\n@media print {\n .slide[data-size=\"letter-portrait\"] { width: 8.5in; height: 11in; }\n @page { size: Letter portrait; margin: 0; }\n}\n@media print {\n .slide[data-size=\"letter-landscape\"] { width: 11in; height: 8.5in; }\n @page { size: Letter landscape; margin: 0; }\n}\n`.trim();\n\nconst hoverableSelector = [\n '[data-node-type]:not([data-node-type=\"slide\"])',\n \"p\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"blockquote\",\n \"ul\",\n \"ol\",\n \"li\",\n \"pre\",\n \"figure\",\n \"table\",\n].join(\", \");\n\nconst hoverOutlineStyles = `\n.slide[data-hover-outline=\"on\"]\n :where(${hoverableSelector}):hover:not(:has(:hover)) {\n outline: var(--slide-hover-outline-width, 1.5px) solid var(--slide-hover-outline-color, rgba(59, 130, 246, 0.65));\n outline-offset: var(--slide-hover-outline-offset, 4px);\n transition: outline-color 120ms ease, outline-width 120ms ease;\n}\n\n/* Cascade: when hovering a container, outline its descendant blocks too */\n.slide[data-hover-outline=\"on\"][data-hover-outline-cascade=\"on\"]\n :where(${hoverableSelector}):hover\n :where(${hoverableSelector}) {\n outline: var(--slide-hover-outline-width, 1.5px) solid var(--slide-hover-outline-color, rgba(59, 130, 246, 0.65));\n outline-offset: var(--slide-hover-outline-offset, 4px);\n transition: outline-color 120ms ease, outline-width 120ms ease;\n}\n`.trim();\n\nexport interface SlideOptions {\n /**\n * The HTML attributes for a slide node.\n * @default {}\n * @example { class: 'foo' }\n */\n HTMLAttributes: Record<string, any>;\n /**\n * Whether to inject CSS styles\n * @default true\n */\n injectCSS: boolean;\n /**\n * Render mode for sizing\n * - fixed: width/height set from size registry (mm/in/px)\n * - dynamic: width:100% with preserved aspect ratio\n * @default 'fixed'\n */\n renderMode: \"fixed\" | \"dynamic\";\n /**\n * Default size applied when attrs.size is absent\n * @default '16x9'\n */\n defaultSize: \"16x9\" | \"4x3\" | \"a4-portrait\" | \"a4-landscape\" | \"letter-portrait\" | \"letter-landscape\" | \"linkedin-banner\";\n /**\n * Inject @media print/@page CSS for paper sizes\n * @default true\n */\n injectPrintCSS: boolean;\n /**\n * Content Security Policy nonce\n */\n injectNonce?: string;\n /**\n * Hover outline highlight for nodes within a slide.\n * - false: disabled (default)\n * - true: enabled with defaults\n * - object: enabled with overrides\n */\n hoverOutline:\n | false\n | true\n | {\n color?: string;\n width?: string;\n offset?: string;\n };\n /**\n * When enabled, hovering a container outlines its descendant blocks too.\n * @default false\n */\n hoverOutlineCascade?: boolean;\n}\n\nconst SlidePluginKey = new PluginKey(\"slide\");\n\nexport const Slide = Node.create<SlideOptions>({\n name: \"slide\",\n isolating: true,\n content: \"row+\",\n\n group: \"slide\",\n\n defining: true,\n\n addOptions() {\n return {\n HTMLAttributes: {},\n injectCSS: true,\n renderMode: \"fixed\",\n defaultSize: \"16x9\",\n injectPrintCSS: true,\n injectNonce: undefined,\n hoverOutline: false,\n hoverOutlineCascade: false,\n };\n },\n\n addAttributes() {\n return {\n size: {\n default: this.options.defaultSize,\n parseHTML: (element) => element.getAttribute(\"data-size\") || this.options.defaultSize,\n renderHTML: (attributes) => {\n if (!attributes.size) {\n return { \"data-size\": this.options.defaultSize };\n }\n return { \"data-size\": attributes.size };\n },\n },\n className: {\n default: \"\",\n },\n id: {\n default: null,\n },\n backgroundMode: {\n default: \"none\",\n },\n backgroundColor: {\n default: null,\n },\n backgroundImage: {\n default: null,\n },\n backgroundOverlayColor: {\n default: null,\n },\n backgroundOverlayOpacity: {\n default: null,\n },\n };\n },\n\n parseHTML() {\n return [{ tag: \"div.slide\" }];\n },\n\n renderHTML({ HTMLAttributes }) {\n const merged = mergeAttributes(this.options.HTMLAttributes, HTMLAttributes);\n const {\n backgroundMode,\n backgroundColor,\n backgroundImage,\n backgroundOverlayColor,\n backgroundOverlayOpacity,\n ...rest\n } = merged;\n\n const styleParts: string[] = [];\n\n const hoverEnabled = this.options.hoverOutline !== false;\n const hoverConfig =\n this.options.hoverOutline === true ? {} : (this.options.hoverOutline || {});\n const hoverCascade = !!this.options.hoverOutlineCascade;\n\n if (backgroundColor) {\n styleParts.push(`--slide-bg-color: ${backgroundColor}`);\n }\n\n if (backgroundImage) {\n const escaped = String(backgroundImage).replace(/\"/g, '\\\\\"');\n styleParts.push(`background-image: url(\"${escaped}\")`);\n }\n\n if (backgroundOverlayColor) {\n styleParts.push(`--slide-bg-overlay-color: ${backgroundOverlayColor}`);\n }\n\n if (backgroundOverlayOpacity != null) {\n styleParts.push(`--slide-bg-overlay-opacity: ${backgroundOverlayOpacity}`);\n }\n\n if (hoverEnabled) {\n const {\n color = \"rgba(59, 130, 246, 0.65)\",\n width = \"1.5px\",\n offset = \"4px\",\n } = hoverConfig;\n styleParts.push(`--slide-hover-outline-color: ${color}`);\n styleParts.push(`--slide-hover-outline-width: ${width}`);\n styleParts.push(`--slide-hover-outline-offset: ${offset}`);\n }\n\n const style = [rest.style, styleParts.join(\"; \")].filter(Boolean).join(\"; \");\n\n const className = [rest.class, rest.className, \"slide\"].filter(Boolean).join(\" \");\n\n delete (rest as any).className;\n delete (rest as any).class;\n\n return [\n \"div\",\n {\n ...rest,\n class: className || \"slide\",\n \"data-node-type\": \"slide\",\n \"data-bg-mode\": backgroundMode || \"none\",\n \"data-hover-outline\": hoverEnabled ? \"on\" : undefined,\n \"data-hover-outline-cascade\": hoverCascade ? \"on\" : undefined,\n style: style || undefined,\n },\n 0,\n ];\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: SlidePluginKey,\n state: {\n init: () => {\n if (this.options.injectCSS && typeof document !== \"undefined\") {\n createStyleTag(slideStyles, this.options.injectNonce, \"slide\");\n const sizingCss =\n this.options.renderMode === \"dynamic\" ? dynamicSizeStyles : fixedSizeStyles;\n if (sizingCss) {\n createStyleTag(sizingCss, this.options.injectNonce, \"slide-sizes\");\n }\n if (this.options.injectPrintCSS) {\n const printCss = printSizeStyles;\n if (printCss) {\n createStyleTag(printCss, this.options.injectNonce, \"slide-print\");\n }\n }\n if (this.options.hoverOutline !== false) {\n createStyleTag(\n hoverOutlineStyles,\n this.options.injectNonce,\n \"slide-hover-outline\"\n );\n }\n }\n return {};\n },\n apply: (_tr, pluginState: Record<string, never>) => pluginState,\n },\n }),\n ];\n },\n});\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blockslides/extension-slide",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Slide node extension for BlockSlides slide editor",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
"slide"
|
|
12
12
|
],
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"@blockslides/core": "^0.
|
|
14
|
+
"@blockslides/core": "^0.2.0",
|
|
15
15
|
"@blockslides/pm": "^0.1.0"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@blockslides/core": "^0.
|
|
18
|
+
"@blockslides/core": "^0.2.0",
|
|
19
19
|
"@blockslides/pm": "^0.1.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
package/src/slide.ts
CHANGED
|
@@ -38,13 +38,14 @@ const slideStyles = `
|
|
|
38
38
|
`;
|
|
39
39
|
|
|
40
40
|
const fixedSizeStyles = `
|
|
41
|
-
.slide
|
|
42
|
-
.slide[data-size="
|
|
43
|
-
.slide[data-size="
|
|
44
|
-
.slide[data-size="a4-
|
|
45
|
-
.slide[data-size="
|
|
46
|
-
.slide[data-size="letter-
|
|
47
|
-
.slide[data-size="
|
|
41
|
+
.slide { --slide-scale: 1; }
|
|
42
|
+
.slide[data-size="16x9"] { width: calc(1920px * var(--slide-scale)); height: calc(1080px * var(--slide-scale)); }
|
|
43
|
+
.slide[data-size="4x3"] { width: calc(1600px * var(--slide-scale)); height: calc(1200px * var(--slide-scale)); }
|
|
44
|
+
.slide[data-size="a4-portrait"] { width: calc(210mm * var(--slide-scale)); height: calc(297mm * var(--slide-scale)); }
|
|
45
|
+
.slide[data-size="a4-landscape"] { width: calc(297mm * var(--slide-scale)); height: calc(210mm * var(--slide-scale)); }
|
|
46
|
+
.slide[data-size="letter-portrait"] { width: calc(8.5in * var(--slide-scale)); height: calc(11in * var(--slide-scale)); }
|
|
47
|
+
.slide[data-size="letter-landscape"] { width: calc(11in * var(--slide-scale)); height: calc(8.5in * var(--slide-scale)); }
|
|
48
|
+
.slide[data-size="linkedin-banner"] { width: calc(1584px * var(--slide-scale)); height: calc(396px * var(--slide-scale)); }
|
|
48
49
|
`.trim();
|
|
49
50
|
|
|
50
51
|
const dynamicSizeStyles = `
|
|
@@ -76,6 +77,42 @@ const printSizeStyles = `
|
|
|
76
77
|
}
|
|
77
78
|
`.trim();
|
|
78
79
|
|
|
80
|
+
const hoverableSelector = [
|
|
81
|
+
'[data-node-type]:not([data-node-type="slide"])',
|
|
82
|
+
"p",
|
|
83
|
+
"h1",
|
|
84
|
+
"h2",
|
|
85
|
+
"h3",
|
|
86
|
+
"h4",
|
|
87
|
+
"h5",
|
|
88
|
+
"h6",
|
|
89
|
+
"blockquote",
|
|
90
|
+
"ul",
|
|
91
|
+
"ol",
|
|
92
|
+
"li",
|
|
93
|
+
"pre",
|
|
94
|
+
"figure",
|
|
95
|
+
"table",
|
|
96
|
+
].join(", ");
|
|
97
|
+
|
|
98
|
+
const hoverOutlineStyles = `
|
|
99
|
+
.slide[data-hover-outline="on"]
|
|
100
|
+
:where(${hoverableSelector}):hover:not(:has(:hover)) {
|
|
101
|
+
outline: var(--slide-hover-outline-width, 1.5px) solid var(--slide-hover-outline-color, rgba(59, 130, 246, 0.65));
|
|
102
|
+
outline-offset: var(--slide-hover-outline-offset, 4px);
|
|
103
|
+
transition: outline-color 120ms ease, outline-width 120ms ease;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* Cascade: when hovering a container, outline its descendant blocks too */
|
|
107
|
+
.slide[data-hover-outline="on"][data-hover-outline-cascade="on"]
|
|
108
|
+
:where(${hoverableSelector}):hover
|
|
109
|
+
:where(${hoverableSelector}) {
|
|
110
|
+
outline: var(--slide-hover-outline-width, 1.5px) solid var(--slide-hover-outline-color, rgba(59, 130, 246, 0.65));
|
|
111
|
+
outline-offset: var(--slide-hover-outline-offset, 4px);
|
|
112
|
+
transition: outline-color 120ms ease, outline-width 120ms ease;
|
|
113
|
+
}
|
|
114
|
+
`.trim();
|
|
115
|
+
|
|
79
116
|
export interface SlideOptions {
|
|
80
117
|
/**
|
|
81
118
|
* The HTML attributes for a slide node.
|
|
@@ -109,6 +146,25 @@ export interface SlideOptions {
|
|
|
109
146
|
* Content Security Policy nonce
|
|
110
147
|
*/
|
|
111
148
|
injectNonce?: string;
|
|
149
|
+
/**
|
|
150
|
+
* Hover outline highlight for nodes within a slide.
|
|
151
|
+
* - false: disabled (default)
|
|
152
|
+
* - true: enabled with defaults
|
|
153
|
+
* - object: enabled with overrides
|
|
154
|
+
*/
|
|
155
|
+
hoverOutline:
|
|
156
|
+
| false
|
|
157
|
+
| true
|
|
158
|
+
| {
|
|
159
|
+
color?: string;
|
|
160
|
+
width?: string;
|
|
161
|
+
offset?: string;
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* When enabled, hovering a container outlines its descendant blocks too.
|
|
165
|
+
* @default false
|
|
166
|
+
*/
|
|
167
|
+
hoverOutlineCascade?: boolean;
|
|
112
168
|
}
|
|
113
169
|
|
|
114
170
|
const SlidePluginKey = new PluginKey("slide");
|
|
@@ -130,6 +186,8 @@ export const Slide = Node.create<SlideOptions>({
|
|
|
130
186
|
defaultSize: "16x9",
|
|
131
187
|
injectPrintCSS: true,
|
|
132
188
|
injectNonce: undefined,
|
|
189
|
+
hoverOutline: false,
|
|
190
|
+
hoverOutlineCascade: false,
|
|
133
191
|
};
|
|
134
192
|
},
|
|
135
193
|
|
|
@@ -186,6 +244,11 @@ export const Slide = Node.create<SlideOptions>({
|
|
|
186
244
|
|
|
187
245
|
const styleParts: string[] = [];
|
|
188
246
|
|
|
247
|
+
const hoverEnabled = this.options.hoverOutline !== false;
|
|
248
|
+
const hoverConfig =
|
|
249
|
+
this.options.hoverOutline === true ? {} : (this.options.hoverOutline || {});
|
|
250
|
+
const hoverCascade = !!this.options.hoverOutlineCascade;
|
|
251
|
+
|
|
189
252
|
if (backgroundColor) {
|
|
190
253
|
styleParts.push(`--slide-bg-color: ${backgroundColor}`);
|
|
191
254
|
}
|
|
@@ -203,6 +266,17 @@ export const Slide = Node.create<SlideOptions>({
|
|
|
203
266
|
styleParts.push(`--slide-bg-overlay-opacity: ${backgroundOverlayOpacity}`);
|
|
204
267
|
}
|
|
205
268
|
|
|
269
|
+
if (hoverEnabled) {
|
|
270
|
+
const {
|
|
271
|
+
color = "rgba(59, 130, 246, 0.65)",
|
|
272
|
+
width = "1.5px",
|
|
273
|
+
offset = "4px",
|
|
274
|
+
} = hoverConfig;
|
|
275
|
+
styleParts.push(`--slide-hover-outline-color: ${color}`);
|
|
276
|
+
styleParts.push(`--slide-hover-outline-width: ${width}`);
|
|
277
|
+
styleParts.push(`--slide-hover-outline-offset: ${offset}`);
|
|
278
|
+
}
|
|
279
|
+
|
|
206
280
|
const style = [rest.style, styleParts.join("; ")].filter(Boolean).join("; ");
|
|
207
281
|
|
|
208
282
|
const className = [rest.class, rest.className, "slide"].filter(Boolean).join(" ");
|
|
@@ -217,6 +291,8 @@ export const Slide = Node.create<SlideOptions>({
|
|
|
217
291
|
class: className || "slide",
|
|
218
292
|
"data-node-type": "slide",
|
|
219
293
|
"data-bg-mode": backgroundMode || "none",
|
|
294
|
+
"data-hover-outline": hoverEnabled ? "on" : undefined,
|
|
295
|
+
"data-hover-outline-cascade": hoverCascade ? "on" : undefined,
|
|
220
296
|
style: style || undefined,
|
|
221
297
|
},
|
|
222
298
|
0,
|
|
@@ -242,6 +318,13 @@ export const Slide = Node.create<SlideOptions>({
|
|
|
242
318
|
createStyleTag(printCss, this.options.injectNonce, "slide-print");
|
|
243
319
|
}
|
|
244
320
|
}
|
|
321
|
+
if (this.options.hoverOutline !== false) {
|
|
322
|
+
createStyleTag(
|
|
323
|
+
hoverOutlineStyles,
|
|
324
|
+
this.options.injectNonce,
|
|
325
|
+
"slide-hover-outline"
|
|
326
|
+
);
|
|
327
|
+
}
|
|
245
328
|
}
|
|
246
329
|
return {};
|
|
247
330
|
},
|