@particle-academy/fancy-slides 0.1.5 → 0.1.6

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.
@@ -1,13 +1,15 @@
1
+ import { useIsDarkSlide } from './chunk-WIUXPQAK.js';
1
2
  import { registerAll, registerBuiltinThemes, EChart } from '@particle-academy/fancy-echarts';
2
3
  import { jsx } from 'react/jsx-runtime';
3
4
 
4
- // src/registry/element-renderers/chart-host.tsx
5
5
  registerAll();
6
6
  registerBuiltinThemes();
7
7
  function ChartHost({ element }) {
8
- return /* @__PURE__ */ jsx("div", { style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(EChart, { option: element.option, theme: element.chartTheme }) });
8
+ const isDarkSlide = useIsDarkSlide();
9
+ const theme = element.chartTheme ?? (isDarkSlide ? "dark" : void 0);
10
+ return /* @__PURE__ */ jsx("div", { style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(EChart, { option: element.option, theme }) });
9
11
  }
10
12
 
11
13
  export { ChartHost as default };
12
- //# sourceMappingURL=chart-host-BHP6GUWO.js.map
13
- //# sourceMappingURL=chart-host-BHP6GUWO.js.map
14
+ //# sourceMappingURL=chart-host-X55F6S44.js.map
15
+ //# sourceMappingURL=chart-host-X55F6S44.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/registry/element-renderers/chart-host.tsx"],"names":[],"mappings":";;;;AAaA,WAAA,EAAY;AACZ,qBAAA,EAAsB;AAEP,SAAR,SAAA,CAA2B,EAAE,OAAA,EAAQ,EAAoD;AAI5F,EAAA,MAAM,cAAc,cAAA,EAAe;AACnC,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,UAAA,KAAe,WAAA,GAAc,MAAA,GAAS,MAAA,CAAA;AAC5D,EAAA,uBACI,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAO,MAAA,EAAQ,MAAA,EAAQ,MAAA,EAAO,EACxC,8BAAC,MAAA,EAAA,EAAO,MAAA,EAAQ,OAAA,CAAQ,MAAA,EAAkD,OAAc,CAAA,EAC5F,CAAA;AAER","file":"chart-host-X55F6S44.js","sourcesContent":["import { EChart, registerAll, registerBuiltinThemes } from \"@particle-academy/fancy-echarts\";\nimport type { ChartElement } from \"../../types\";\nimport { useIsDarkSlide } from \"../../components/Slide/slide-context\";\n\n// fancy-echarts ships its chart types as opt-in tree-shake-friendly modules,\n// so the consumer normally calls `registerAll()` somewhere global. The\n// registry subpath here is the natural spot to wire it: this module only\n// loads when a deck actually contains a chart element (defaultElementRegistry\n// is React.lazy), so the registration cost is paid by chart-using hosts\n// only — non-chart consumers never even import this file.\n//\n// `registerAll()` is idempotent on echarts' side, so re-imports across\n// chunks are safe.\nregisterAll();\nregisterBuiltinThemes();\n\nexport default function ChartHost({ element }: { element: ChartElement; slideWidthPx: number }) {\n // If the slide background reads as dark and the chart doesn't already\n // pin a theme explicitly, fall back to echarts' built-in \"dark\" theme\n // so the chart legend/axis/tooltip text stay legible.\n const isDarkSlide = useIsDarkSlide();\n const theme = element.chartTheme ?? (isDarkSlide ? \"dark\" : undefined);\n return (\n <div style={{ width: \"100%\", height: \"100%\" }}>\n <EChart option={element.option as Parameters<typeof EChart>[0][\"option\"]} theme={theme} />\n </div>\n );\n}\n"]}
@@ -0,0 +1,47 @@
1
+ import { createContext, useContext } from 'react';
2
+
3
+ // src/components/Slide/slide-context.ts
4
+ var SlideContext = createContext(null);
5
+ function useSlideContext() {
6
+ return useContext(SlideContext);
7
+ }
8
+ function useSlideTheme() {
9
+ return useContext(SlideContext)?.theme;
10
+ }
11
+ function useIsDarkSlide() {
12
+ return useContext(SlideContext)?.isDark ?? false;
13
+ }
14
+ function isDarkColor(color) {
15
+ const m = color.match(/^#([0-9a-f]{3,8})$/i);
16
+ if (m) {
17
+ let hex = m[1];
18
+ if (hex.length === 3) {
19
+ hex = hex.split("").map((c) => c + c).join("");
20
+ }
21
+ if (hex.length >= 6) {
22
+ const r = parseInt(hex.slice(0, 2), 16);
23
+ const g = parseInt(hex.slice(2, 4), 16);
24
+ const b = parseInt(hex.slice(4, 6), 16);
25
+ return relativeLuminance(r, g, b) < 0.5;
26
+ }
27
+ }
28
+ const rgb = color.match(/rgba?\(([^)]+)\)/i);
29
+ if (rgb) {
30
+ const [r, g, b] = rgb[1].split(",").map((s) => parseInt(s.trim(), 10));
31
+ if (!Number.isNaN(r) && !Number.isNaN(g) && !Number.isNaN(b)) {
32
+ return relativeLuminance(r, g, b) < 0.5;
33
+ }
34
+ }
35
+ return false;
36
+ }
37
+ function relativeLuminance(r, g, b) {
38
+ const toLinear = (c) => {
39
+ const v = c / 255;
40
+ return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
41
+ };
42
+ return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
43
+ }
44
+
45
+ export { SlideContext, isDarkColor, useIsDarkSlide, useSlideContext, useSlideTheme };
46
+ //# sourceMappingURL=chunk-WIUXPQAK.js.map
47
+ //# sourceMappingURL=chunk-WIUXPQAK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Slide/slide-context.ts"],"names":[],"mappings":";;;AAsBO,IAAM,YAAA,GAAe,cAAwC,IAAI;AAOjE,SAAS,eAAA,GAA4C;AACxD,EAAA,OAAO,WAAW,YAAY,CAAA;AAClC;AAMO,SAAS,aAAA,GAAmC;AAC/C,EAAA,OAAO,UAAA,CAAW,YAAY,CAAA,EAAG,KAAA;AACrC;AAMO,SAAS,cAAA,GAA0B;AACtC,EAAA,OAAO,UAAA,CAAW,YAAY,CAAA,EAAG,MAAA,IAAU,KAAA;AAC/C;AAGO,SAAS,YAAY,KAAA,EAAwB;AAEhD,EAAA,MAAM,CAAA,GAAI,KAAA,CAAM,KAAA,CAAM,qBAAqB,CAAA;AAC3C,EAAA,IAAI,CAAA,EAAG;AACH,IAAA,IAAI,GAAA,GAAM,EAAE,CAAC,CAAA;AACb,IAAA,IAAI,GAAA,CAAI,WAAW,CAAA,EAAG;AAClB,MAAA,GAAA,GAAM,GAAA,CAAI,KAAA,CAAM,EAAE,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,GAAI,CAAC,CAAA,CAAE,IAAA,CAAK,EAAE,CAAA;AAAA,IACjD;AACA,IAAA,IAAI,GAAA,CAAI,UAAU,CAAA,EAAG;AACjB,MAAA,MAAM,IAAI,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA,EAAG,CAAC,GAAG,EAAE,CAAA;AACtC,MAAA,MAAM,IAAI,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA,EAAG,CAAC,GAAG,EAAE,CAAA;AACtC,MAAA,MAAM,IAAI,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA,EAAG,CAAC,GAAG,EAAE,CAAA;AACtC,MAAA,OAAO,iBAAA,CAAkB,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA,GAAI,GAAA;AAAA,IACxC;AAAA,EACJ;AACA,EAAA,MAAM,GAAA,GAAM,KAAA,CAAM,KAAA,CAAM,mBAAmB,CAAA;AAC3C,EAAA,IAAI,GAAA,EAAK;AACL,IAAA,MAAM,CAAC,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA,GAAI,GAAA,CAAI,CAAC,CAAA,CAAE,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,QAAA,CAAS,EAAE,IAAA,EAAK,EAAG,EAAE,CAAC,CAAA;AACrE,IAAA,IAAI,CAAC,MAAA,CAAO,KAAA,CAAM,CAAC,KAAK,CAAC,MAAA,CAAO,KAAA,CAAM,CAAC,CAAA,IAAK,CAAC,MAAA,CAAO,KAAA,CAAM,CAAC,CAAA,EAAG;AAC1D,MAAA,OAAO,iBAAA,CAAkB,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA,GAAI,GAAA;AAAA,IACxC;AAAA,EACJ;AACA,EAAA,OAAO,KAAA;AACX;AAEA,SAAS,iBAAA,CAAkB,CAAA,EAAW,CAAA,EAAW,CAAA,EAAmB;AAEhE,EAAA,MAAM,QAAA,GAAW,CAAC,CAAA,KAAc;AAC5B,IAAA,MAAM,IAAI,CAAA,GAAI,GAAA;AACd,IAAA,OAAO,CAAA,IAAK,UAAU,CAAA,GAAI,KAAA,GAAQ,KAAK,GAAA,CAAA,CAAK,CAAA,GAAI,KAAA,IAAS,KAAA,EAAO,GAAG,CAAA;AAAA,EACvE,CAAA;AACA,EAAA,OAAO,MAAA,GAAS,QAAA,CAAS,CAAC,CAAA,GAAI,MAAA,GAAS,SAAS,CAAC,CAAA,GAAI,MAAA,GAAS,QAAA,CAAS,CAAC,CAAA;AAC5E","file":"chunk-WIUXPQAK.js","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { Theme } from \"../../types\";\n\n/**\n * What the surrounding <Slide> knows about itself. Exposed to children\n * (including `renderElement` callbacks and registry hosts like chart-host)\n * via React context so they can react to the deck's theme without the\n * renderElement signature having to carry it around.\n */\nexport interface SlideContextValue {\n /** Fully-resolved theme (merged with defaults). */\n theme: Theme;\n /**\n * Heuristic: true when the resolved background is dark enough that\n * native-rendered widgets (charts, code blocks) should use a dark variant.\n * Computed once from `theme.colors.background` luminance.\n */\n isDark: boolean;\n /** Slide width in CSS pixels at the current render size. */\n slideWidthPx: number;\n}\n\nexport const SlideContext = createContext<SlideContextValue | null>(null);\n\n/**\n * Read the surrounding <Slide>'s context. Returns null when called outside\n * a Slide — chart-host / code-host can use that signal to fall back to\n * a sensible default (light theme, no resize anchor).\n */\nexport function useSlideContext(): SlideContextValue | null {\n return useContext(SlideContext);\n}\n\n/**\n * Convenience: returns the resolved theme, or undefined when the caller\n * isn't inside a Slide.\n */\nexport function useSlideTheme(): Theme | undefined {\n return useContext(SlideContext)?.theme;\n}\n\n/**\n * Convenience: returns true when the surrounding slide's background reads\n * as \"dark enough\" — useful for charts/code blocks that ship a dark variant.\n */\nexport function useIsDarkSlide(): boolean {\n return useContext(SlideContext)?.isDark ?? false;\n}\n\n/** Compute `isDark` from a hex/rgb colour. Used by Slide when building the context. */\nexport function isDarkColor(color: string): boolean {\n // Normalise #rgb / #rrggbb / rgb()/rgba() to a triple.\n const m = color.match(/^#([0-9a-f]{3,8})$/i);\n if (m) {\n let hex = m[1];\n if (hex.length === 3) {\n hex = hex.split(\"\").map((c) => c + c).join(\"\");\n }\n if (hex.length >= 6) {\n const r = parseInt(hex.slice(0, 2), 16);\n const g = parseInt(hex.slice(2, 4), 16);\n const b = parseInt(hex.slice(4, 6), 16);\n return relativeLuminance(r, g, b) < 0.5;\n }\n }\n const rgb = color.match(/rgba?\\(([^)]+)\\)/i);\n if (rgb) {\n const [r, g, b] = rgb[1].split(\",\").map((s) => parseInt(s.trim(), 10));\n if (!Number.isNaN(r) && !Number.isNaN(g) && !Number.isNaN(b)) {\n return relativeLuminance(r, g, b) < 0.5;\n }\n }\n return false;\n}\n\nfunction relativeLuminance(r: number, g: number, b: number): number {\n // Standard sRGB → relative luminance.\n const toLinear = (c: number) => {\n const v = c / 255;\n return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);\n };\n return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);\n}\n"]}
package/dist/index.cjs CHANGED
@@ -224,6 +224,46 @@ function renderShape(el, s) {
224
224
  return null;
225
225
  }
226
226
  }
227
+ var SlideContext = react.createContext(null);
228
+ function useSlideContext() {
229
+ return react.useContext(SlideContext);
230
+ }
231
+ function useSlideTheme() {
232
+ return react.useContext(SlideContext)?.theme;
233
+ }
234
+ function useIsDarkSlide() {
235
+ return react.useContext(SlideContext)?.isDark ?? false;
236
+ }
237
+ function isDarkColor(color) {
238
+ const m = color.match(/^#([0-9a-f]{3,8})$/i);
239
+ if (m) {
240
+ let hex = m[1];
241
+ if (hex.length === 3) {
242
+ hex = hex.split("").map((c) => c + c).join("");
243
+ }
244
+ if (hex.length >= 6) {
245
+ const r = parseInt(hex.slice(0, 2), 16);
246
+ const g = parseInt(hex.slice(2, 4), 16);
247
+ const b = parseInt(hex.slice(4, 6), 16);
248
+ return relativeLuminance(r, g, b) < 0.5;
249
+ }
250
+ }
251
+ const rgb = color.match(/rgba?\(([^)]+)\)/i);
252
+ if (rgb) {
253
+ const [r, g, b] = rgb[1].split(",").map((s) => parseInt(s.trim(), 10));
254
+ if (!Number.isNaN(r) && !Number.isNaN(g) && !Number.isNaN(b)) {
255
+ return relativeLuminance(r, g, b) < 0.5;
256
+ }
257
+ }
258
+ return false;
259
+ }
260
+ function relativeLuminance(r, g, b) {
261
+ const toLinear = (c) => {
262
+ const v = c / 255;
263
+ return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
264
+ };
265
+ return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
266
+ }
227
267
  function Slide({
228
268
  slide,
229
269
  theme,
@@ -261,10 +301,19 @@ function Slide({
261
301
  const slideWidthPx = measured || 1;
262
302
  const slideHeightPx = slideWidthPx / ratio;
263
303
  const bg = slide.background;
304
+ const effectiveBg = bg?.color ?? t.colors?.background ?? "#ffffff";
264
305
  const backgroundStyle = {
265
- background: bg?.gradient ? bg.gradient : bg?.image ? `${bg.color ?? "transparent"} url(${bg.image}) center/${bg.imageFit ?? "cover"} no-repeat` : bg?.color ?? t.colors?.background ?? "#ffffff"
306
+ background: bg?.gradient ? bg.gradient : bg?.image ? `${bg.color ?? "transparent"} url(${bg.image}) center/${bg.imageFit ?? "cover"} no-repeat` : effectiveBg
266
307
  };
267
- return /* @__PURE__ */ jsxRuntime.jsx(
308
+ const slideContext = react.useMemo(
309
+ () => ({
310
+ theme: t,
311
+ isDark: isDarkColor(effectiveBg),
312
+ slideWidthPx
313
+ }),
314
+ [t, effectiveBg, slideWidthPx]
315
+ );
316
+ return /* @__PURE__ */ jsxRuntime.jsx(SlideContext.Provider, { value: slideContext, children: /* @__PURE__ */ jsxRuntime.jsx(
268
317
  "div",
269
318
  {
270
319
  ref,
@@ -300,7 +349,7 @@ function Slide({
300
349
  element.id
301
350
  ))
302
351
  }
303
- );
352
+ ) });
304
353
  }
305
354
  var MIN_DIM = 0.02;
306
355
  var CLICK_DRAG_THRESHOLD_PX = 3;
@@ -1871,7 +1920,10 @@ exports.reduceDeck = reduce;
1871
1920
  exports.resolveTheme = resolveTheme;
1872
1921
  exports.slideId = slideId;
1873
1922
  exports.useDeckState = useDeckState;
1923
+ exports.useIsDarkSlide = useIsDarkSlide;
1924
+ exports.useSlideContext = useSlideContext;
1874
1925
  exports.useSlideKeyboard = useSlideKeyboard;
1926
+ exports.useSlideTheme = useSlideTheme;
1875
1927
  exports.vividTheme = vividTheme;
1876
1928
  //# sourceMappingURL=index.cjs.map
1877
1929
  //# sourceMappingURL=index.cjs.map