@blockslides/extension-slide 0.3.0 → 0.3.2
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 +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +9 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/slide.ts +21 -3
package/dist/index.d.mts
CHANGED
|
@@ -24,6 +24,13 @@ interface SlideOptions {
|
|
|
24
24
|
* @default '16x9'
|
|
25
25
|
*/
|
|
26
26
|
defaultSize: "16x9" | "4x3" | "a4-portrait" | "a4-landscape" | "letter-portrait" | "letter-landscape" | "linkedin-banner";
|
|
27
|
+
/**
|
|
28
|
+
* Scale factor for fixed-size slides.
|
|
29
|
+
* Only applies when renderMode is 'fixed'.
|
|
30
|
+
* Helpful for shrinking/expanding fixed previews without changing size presets.
|
|
31
|
+
* @default 1
|
|
32
|
+
*/
|
|
33
|
+
scale: number;
|
|
27
34
|
/**
|
|
28
35
|
* Inject @media print/@page CSS for paper sizes
|
|
29
36
|
* @default true
|
package/dist/index.d.ts
CHANGED
|
@@ -24,6 +24,13 @@ interface SlideOptions {
|
|
|
24
24
|
* @default '16x9'
|
|
25
25
|
*/
|
|
26
26
|
defaultSize: "16x9" | "4x3" | "a4-portrait" | "a4-landscape" | "letter-portrait" | "letter-landscape" | "linkedin-banner";
|
|
27
|
+
/**
|
|
28
|
+
* Scale factor for fixed-size slides.
|
|
29
|
+
* Only applies when renderMode is 'fixed'.
|
|
30
|
+
* Helpful for shrinking/expanding fixed previews without changing size presets.
|
|
31
|
+
* @default 1
|
|
32
|
+
*/
|
|
33
|
+
scale: number;
|
|
27
34
|
/**
|
|
28
35
|
* Inject @media print/@page CSS for paper sizes
|
|
29
36
|
* @default true
|
package/dist/index.js
CHANGED
|
@@ -62,8 +62,8 @@ var slideStyles = `
|
|
|
62
62
|
opacity: var(--slide-bg-overlay-opacity, 0.35);
|
|
63
63
|
}
|
|
64
64
|
`;
|
|
65
|
-
var
|
|
66
|
-
.slide { --slide-scale:
|
|
65
|
+
var getFixedSizeStyles = (scale) => `
|
|
66
|
+
.slide { --slide-scale: ${scale}; }
|
|
67
67
|
.slide[data-size="16x9"] { width: calc(1920px * var(--slide-scale)); height: calc(1080px * var(--slide-scale)); }
|
|
68
68
|
.slide[data-size="4x3"] { width: calc(1600px * var(--slide-scale)); height: calc(1200px * var(--slide-scale)); }
|
|
69
69
|
.slide[data-size="a4-portrait"] { width: calc(210mm * var(--slide-scale)); height: calc(297mm * var(--slide-scale)); }
|
|
@@ -146,6 +146,7 @@ var Slide = import_core.Node.create({
|
|
|
146
146
|
injectCSS: true,
|
|
147
147
|
renderMode: "fixed",
|
|
148
148
|
defaultSize: "16x9",
|
|
149
|
+
scale: 1,
|
|
149
150
|
injectPrintCSS: true,
|
|
150
151
|
injectNonce: void 0,
|
|
151
152
|
hoverOutline: false,
|
|
@@ -251,9 +252,14 @@ var Slide = import_core.Node.create({
|
|
|
251
252
|
key: SlidePluginKey,
|
|
252
253
|
state: {
|
|
253
254
|
init: () => {
|
|
255
|
+
if (this.options.scale !== 1 && this.options.renderMode !== "fixed") {
|
|
256
|
+
console.warn(
|
|
257
|
+
`[Slide] The 'scale' option only applies when renderMode is 'fixed'. Current renderMode is '${this.options.renderMode}', so the scale value of ${this.options.scale} will be ignored.`
|
|
258
|
+
);
|
|
259
|
+
}
|
|
254
260
|
if (this.options.injectCSS && typeof document !== "undefined") {
|
|
255
261
|
(0, import_core.createStyleTag)(slideStyles, this.options.injectNonce, "slide");
|
|
256
|
-
const sizingCss = this.options.renderMode === "dynamic" ? dynamicSizeStyles :
|
|
262
|
+
const sizingCss = this.options.renderMode === "dynamic" ? dynamicSizeStyles : getFixedSizeStyles(this.options.scale);
|
|
257
263
|
if (sizingCss) {
|
|
258
264
|
(0, import_core.createStyleTag)(sizingCss, this.options.injectNonce, "slide-sizes");
|
|
259
265
|
}
|
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 { --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":[]}
|
|
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 getFixedSizeStyles = (scale: number) => `\n.slide { --slide-scale: ${scale}; }\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 * Scale factor for fixed-size slides.\n * Only applies when renderMode is 'fixed'.\n * Helpful for shrinking/expanding fixed previews without changing size presets.\n * @default 1\n */\n scale: number;\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 scale: 1,\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 // Warn if scale is set but renderMode is not 'fixed'\n if (this.options.scale !== 1 && this.options.renderMode !== \"fixed\") {\n console.warn(\n `[Slide] The 'scale' option only applies when renderMode is 'fixed'. ` +\n `Current renderMode is '${this.options.renderMode}', so the scale value of ${this.options.scale} will be ignored.`\n );\n }\n\n if (this.options.injectCSS && typeof document !== \"undefined\") {\n createStyleTag(slideStyles, this.options.injectNonce, \"slide\");\n const sizingCss =\n this.options.renderMode === \"dynamic\"\n ? dynamicSizeStyles\n : getFixedSizeStyles(this.options.scale);\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
|
@@ -36,8 +36,8 @@ var slideStyles = `
|
|
|
36
36
|
opacity: var(--slide-bg-overlay-opacity, 0.35);
|
|
37
37
|
}
|
|
38
38
|
`;
|
|
39
|
-
var
|
|
40
|
-
.slide { --slide-scale:
|
|
39
|
+
var getFixedSizeStyles = (scale) => `
|
|
40
|
+
.slide { --slide-scale: ${scale}; }
|
|
41
41
|
.slide[data-size="16x9"] { width: calc(1920px * var(--slide-scale)); height: calc(1080px * var(--slide-scale)); }
|
|
42
42
|
.slide[data-size="4x3"] { width: calc(1600px * var(--slide-scale)); height: calc(1200px * var(--slide-scale)); }
|
|
43
43
|
.slide[data-size="a4-portrait"] { width: calc(210mm * var(--slide-scale)); height: calc(297mm * var(--slide-scale)); }
|
|
@@ -120,6 +120,7 @@ var Slide = Node.create({
|
|
|
120
120
|
injectCSS: true,
|
|
121
121
|
renderMode: "fixed",
|
|
122
122
|
defaultSize: "16x9",
|
|
123
|
+
scale: 1,
|
|
123
124
|
injectPrintCSS: true,
|
|
124
125
|
injectNonce: void 0,
|
|
125
126
|
hoverOutline: false,
|
|
@@ -225,9 +226,14 @@ var Slide = Node.create({
|
|
|
225
226
|
key: SlidePluginKey,
|
|
226
227
|
state: {
|
|
227
228
|
init: () => {
|
|
229
|
+
if (this.options.scale !== 1 && this.options.renderMode !== "fixed") {
|
|
230
|
+
console.warn(
|
|
231
|
+
`[Slide] The 'scale' option only applies when renderMode is 'fixed'. Current renderMode is '${this.options.renderMode}', so the scale value of ${this.options.scale} will be ignored.`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
228
234
|
if (this.options.injectCSS && typeof document !== "undefined") {
|
|
229
235
|
createStyleTag(slideStyles, this.options.injectNonce, "slide");
|
|
230
|
-
const sizingCss = this.options.renderMode === "dynamic" ? dynamicSizeStyles :
|
|
236
|
+
const sizingCss = this.options.renderMode === "dynamic" ? dynamicSizeStyles : getFixedSizeStyles(this.options.scale);
|
|
231
237
|
if (sizingCss) {
|
|
232
238
|
createStyleTag(sizingCss, this.options.injectNonce, "slide-sizes");
|
|
233
239
|
}
|
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 { --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":";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;AAAA,EAStB,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;AAEP,IAAM,oBAAoB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAEX,IAAM,qBAAqB;AAAA;AAAA,WAEhB,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAQjB,iBAAiB;AAAA,WACjB,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA,EAK1B,KAAK;AAwDP,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,MACb,cAAc;AAAA,MACd,qBAAqB;AAAA,IACvB;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,UAAM,eAAe,KAAK,QAAQ,iBAAiB;AACnD,UAAM,cACJ,KAAK,QAAQ,iBAAiB,OAAO,CAAC,IAAK,KAAK,QAAQ,gBAAgB,CAAC;AAC3E,UAAM,eAAe,CAAC,CAAC,KAAK,QAAQ;AAEpC,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,QAAI,cAAc;AAChB,YAAM;AAAA,QACJ,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,MACX,IAAI;AACJ,iBAAW,KAAK,gCAAgC,KAAK,EAAE;AACvD,iBAAW,KAAK,gCAAgC,KAAK,EAAE;AACvD,iBAAW,KAAK,iCAAiC,MAAM,EAAE;AAAA,IAC3D;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,sBAAsB,eAAe,OAAO;AAAA,QAC5C,8BAA8B,eAAe,OAAO;AAAA,QACpD,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;AACA,kBAAI,KAAK,QAAQ,iBAAiB,OAAO;AACvC;AAAA,kBACE;AAAA,kBACA,KAAK,QAAQ;AAAA,kBACb;AAAA,gBACF;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 getFixedSizeStyles = (scale: number) => `\n.slide { --slide-scale: ${scale}; }\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 * Scale factor for fixed-size slides.\n * Only applies when renderMode is 'fixed'.\n * Helpful for shrinking/expanding fixed previews without changing size presets.\n * @default 1\n */\n scale: number;\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 scale: 1,\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 // Warn if scale is set but renderMode is not 'fixed'\n if (this.options.scale !== 1 && this.options.renderMode !== \"fixed\") {\n console.warn(\n `[Slide] The 'scale' option only applies when renderMode is 'fixed'. ` +\n `Current renderMode is '${this.options.renderMode}', so the scale value of ${this.options.scale} will be ignored.`\n );\n }\n\n if (this.options.injectCSS && typeof document !== \"undefined\") {\n createStyleTag(slideStyles, this.options.injectNonce, \"slide\");\n const sizingCss =\n this.options.renderMode === \"dynamic\"\n ? dynamicSizeStyles\n : getFixedSizeStyles(this.options.scale);\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
package/src/slide.ts
CHANGED
|
@@ -37,8 +37,8 @@ const slideStyles = `
|
|
|
37
37
|
}
|
|
38
38
|
`;
|
|
39
39
|
|
|
40
|
-
const
|
|
41
|
-
.slide { --slide-scale:
|
|
40
|
+
const getFixedSizeStyles = (scale: number) => `
|
|
41
|
+
.slide { --slide-scale: ${scale}; }
|
|
42
42
|
.slide[data-size="16x9"] { width: calc(1920px * var(--slide-scale)); height: calc(1080px * var(--slide-scale)); }
|
|
43
43
|
.slide[data-size="4x3"] { width: calc(1600px * var(--slide-scale)); height: calc(1200px * var(--slide-scale)); }
|
|
44
44
|
.slide[data-size="a4-portrait"] { width: calc(210mm * var(--slide-scale)); height: calc(297mm * var(--slide-scale)); }
|
|
@@ -137,6 +137,13 @@ export interface SlideOptions {
|
|
|
137
137
|
* @default '16x9'
|
|
138
138
|
*/
|
|
139
139
|
defaultSize: "16x9" | "4x3" | "a4-portrait" | "a4-landscape" | "letter-portrait" | "letter-landscape" | "linkedin-banner";
|
|
140
|
+
/**
|
|
141
|
+
* Scale factor for fixed-size slides.
|
|
142
|
+
* Only applies when renderMode is 'fixed'.
|
|
143
|
+
* Helpful for shrinking/expanding fixed previews without changing size presets.
|
|
144
|
+
* @default 1
|
|
145
|
+
*/
|
|
146
|
+
scale: number;
|
|
140
147
|
/**
|
|
141
148
|
* Inject @media print/@page CSS for paper sizes
|
|
142
149
|
* @default true
|
|
@@ -184,6 +191,7 @@ export const Slide = Node.create<SlideOptions>({
|
|
|
184
191
|
injectCSS: true,
|
|
185
192
|
renderMode: "fixed",
|
|
186
193
|
defaultSize: "16x9",
|
|
194
|
+
scale: 1,
|
|
187
195
|
injectPrintCSS: true,
|
|
188
196
|
injectNonce: undefined,
|
|
189
197
|
hoverOutline: false,
|
|
@@ -305,10 +313,20 @@ export const Slide = Node.create<SlideOptions>({
|
|
|
305
313
|
key: SlidePluginKey,
|
|
306
314
|
state: {
|
|
307
315
|
init: () => {
|
|
316
|
+
// Warn if scale is set but renderMode is not 'fixed'
|
|
317
|
+
if (this.options.scale !== 1 && this.options.renderMode !== "fixed") {
|
|
318
|
+
console.warn(
|
|
319
|
+
`[Slide] The 'scale' option only applies when renderMode is 'fixed'. ` +
|
|
320
|
+
`Current renderMode is '${this.options.renderMode}', so the scale value of ${this.options.scale} will be ignored.`
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
308
324
|
if (this.options.injectCSS && typeof document !== "undefined") {
|
|
309
325
|
createStyleTag(slideStyles, this.options.injectNonce, "slide");
|
|
310
326
|
const sizingCss =
|
|
311
|
-
this.options.renderMode === "dynamic"
|
|
327
|
+
this.options.renderMode === "dynamic"
|
|
328
|
+
? dynamicSizeStyles
|
|
329
|
+
: getFixedSizeStyles(this.options.scale);
|
|
312
330
|
if (sizingCss) {
|
|
313
331
|
createStyleTag(sizingCss, this.options.injectNonce, "slide-sizes");
|
|
314
332
|
}
|