@cntrl-site/sdk-nextjs 1.9.139 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22,6 +22,7 @@ const useItemGeometry_1 = require("../../../ItemGeometry/useItemGeometry");
22
22
  const RichTextGeometryController_1 = require("../../../ItemGeometry/RichTextGeometryController");
23
23
  const RichTextConverter_1 = require("../../../utils/RichTextConverter/RichTextConverter");
24
24
  const getHeadingTag_1 = require("../../../utils/getHeadingTag");
25
+ const detectLang_1 = require("../../../utils/detectLang");
25
26
  const RichTextItem = ({ item, sectionId, onResize, interactionCtrl, onVisibilityChange }) => {
26
27
  var _a, _b, _c, _d, _e;
27
28
  const reactId = (0, react_1.useId)();
@@ -59,10 +60,11 @@ const RichTextItem = ({ item, sectionId, onResize, interactionCtrl, onVisibility
59
60
  const isInteractive = colorAlpha !== 0 || hasVisibleRangeColors;
60
61
  const [content, styles] = (0, useRichTextItem_1.useRichTextItem)(item);
61
62
  const Wrapper = ((_e = (0, getHeadingTag_1.getHeadingTag)(item)) !== null && _e !== void 0 ? _e : 'div');
63
+ const lang = (0, react_1.useMemo)(() => (0, detectLang_1.detectLang)(item.commonParams.text), [item.commonParams.text]);
62
64
  (0, react_1.useEffect)(() => {
63
65
  onVisibilityChange === null || onVisibilityChange === void 0 ? void 0 : onVisibilityChange(isInteractive);
64
66
  }, [isInteractive, onVisibilityChange]);
65
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(Wrapper, { ref: setRef, className: `rich-text-wrapper-${item.id}${layoutId ? '' : ` ${RichTextConverter_1.RICH_TEXT_LAYOUT_PENDING_CLASS}`}`, style: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {})), (textColor ? { color: `${textColor.fmt('rgba')}` } : {})), (angle !== undefined ? { transform: `rotate(${angle}deg)` } : {})), (letterSpacing !== undefined ? { letterSpacing: `${letterSpacing * exemplary}px` } : {})), (wordSpacing !== undefined ? { wordSpacing: `${wordSpacing * exemplary}px` } : {})), (fontSize !== undefined ? { fontSize: `${Math.round(fontSize * exemplary)}px` } : {})), (lineHeight !== undefined ? { lineHeight: `${lineHeight * exemplary}px` } : {})), (columnsEnabled ? { columnCount: columns, columnFill: 'balance' } : {})), (columnsEnabled && columnGutter !== undefined ? { columnGap: `${Math.round(columnGutter * exemplary)}px` } : {})), (hyphens !== undefined ? { hyphens, WebkitHyphens: hyphens } : {})), { willChange: blur !== 0 && blur !== undefined ? 'transform' : 'unset', transition }), children: content }), (0, jsx_runtime_1.jsxs)(style_1.default, { id: id, children: [styles, `${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, layoutParams]) => {
67
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(Wrapper, { lang: lang, ref: setRef, className: `rich-text-wrapper-${item.id}${layoutId ? '' : ` ${RichTextConverter_1.RICH_TEXT_LAYOUT_PENDING_CLASS}`}`, style: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {})), (textColor ? { color: `${textColor.fmt('rgba')}` } : {})), (angle !== undefined ? { transform: `rotate(${angle}deg)` } : {})), (letterSpacing !== undefined ? { letterSpacing: `${letterSpacing * exemplary}px` } : {})), (wordSpacing !== undefined ? { wordSpacing: `${wordSpacing * exemplary}px` } : {})), (fontSize !== undefined ? { fontSize: `${Math.round(fontSize * exemplary)}px` } : {})), (lineHeight !== undefined ? { lineHeight: `${lineHeight * exemplary}px` } : {})), (columnsEnabled ? { columnCount: columns, columnFill: 'balance' } : {})), (columnsEnabled && columnGutter !== undefined ? { columnGap: `${Math.round(columnGutter * exemplary)}px` } : {})), (hyphens !== undefined ? { hyphens, WebkitHyphens: hyphens } : {})), { willChange: blur !== 0 && blur !== undefined ? 'transform' : 'unset', transition }), children: content }), (0, jsx_runtime_1.jsxs)(style_1.default, { id: id, children: [styles, `${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, layoutParams]) => {
66
68
  var _a, _b, _c;
67
69
  const color = color_1.CntrlColor.parse(layoutParams.color);
68
70
  const columns = (_a = layoutParams.columns) !== null && _a !== void 0 ? _a : 1;
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.detectLang = void 0;
7
+ // franc-min ships no types, and this package publishes raw .ts as its
8
+ // "types" entry, so downstream projects type-check this file directly
9
+ // out of node_modules rather than a compiled .d.ts. No local `declare
10
+ // module` here on purpose — that would only cover this package's own
11
+ // build and leave @ts-expect-error unused (and thus itself an error).
12
+ // @ts-expect-error franc-min has no type declarations
13
+ const franc_min_1 = __importDefault(require("franc-min"));
14
+ // Maps the ISO 639-3 codes franc-min can detect to their ISO 639-1
15
+ // equivalent, since `lang` in the browser is conventionally two letters.
16
+ // Codes with no ISO 639-1 equivalent are passed through as-is — a bare
17
+ // ISO 639-3 code is still a valid BCP 47 language tag.
18
+ const ISO_639_3_TO_1 = {
19
+ arb: 'ar',
20
+ azj: 'az',
21
+ bel: 'be',
22
+ bos: 'bs',
23
+ bul: 'bg',
24
+ ces: 'cs',
25
+ deu: 'de',
26
+ eng: 'en',
27
+ fas: 'fa',
28
+ fra: 'fr',
29
+ hau: 'ha',
30
+ hin: 'hi',
31
+ hrv: 'hr',
32
+ hun: 'hu',
33
+ ibo: 'ig',
34
+ ind: 'id',
35
+ ita: 'it',
36
+ jav: 'jv',
37
+ kaz: 'kk',
38
+ kin: 'rw',
39
+ lin: 'ln',
40
+ mar: 'mr',
41
+ nep: 'ne',
42
+ nld: 'nl',
43
+ nya: 'ny',
44
+ pol: 'pl',
45
+ por: 'pt',
46
+ ron: 'ro',
47
+ run: 'rn',
48
+ rus: 'ru',
49
+ som: 'so',
50
+ spa: 'es',
51
+ srp: 'sr',
52
+ sun: 'su',
53
+ swe: 'sv',
54
+ swh: 'sw',
55
+ tgl: 'tl',
56
+ tur: 'tr',
57
+ ukr: 'uk',
58
+ urd: 'ur',
59
+ uzn: 'uz',
60
+ vie: 'vi',
61
+ yor: 'yo',
62
+ zlm: 'ms',
63
+ zul: 'zu'
64
+ };
65
+ // Below this many characters franc-min's own trigram model is unreliable
66
+ // (headings, labels, short captions) — leave `lang` unset so it inherits
67
+ // from the document instead of guessing wrong.
68
+ const MIN_DETECTABLE_LENGTH = 20;
69
+ function detectLang(text) {
70
+ var _a;
71
+ if (!text)
72
+ return undefined;
73
+ const trimmed = text.trim();
74
+ if (trimmed.length < MIN_DETECTABLE_LENGTH)
75
+ return undefined;
76
+ const code3 = (0, franc_min_1.default)(trimmed, { minLength: MIN_DETECTABLE_LENGTH });
77
+ if (code3 === 'und')
78
+ return undefined;
79
+ return (_a = ISO_639_3_TO_1[code3]) !== null && _a !== void 0 ? _a : code3;
80
+ }
81
+ exports.detectLang = detectLang;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cntrl-site/sdk-nextjs",
3
- "version": "1.9.139",
3
+ "version": "1.10.1",
4
4
  "description": "SDK for Next.js",
5
5
  "author": "arsen@momdesign.nyc",
6
6
  "license": "MIT",
@@ -36,6 +36,7 @@
36
36
  "@cntrl-site/sdk": "^1.28.10",
37
37
  "@types/vimeo__player": "^2.18.0",
38
38
  "@vimeo/player": "^2.25.0",
39
+ "franc-min": "^5.0.0",
39
40
  "html-react-parser": "^3.0.1",
40
41
  "resize-observer-polyfill": "^1.5.1",
41
42
  "styled-jsx": "^5.0.2"
@@ -16,6 +16,7 @@ import { useItemGeometry } from '../../../ItemGeometry/useItemGeometry';
16
16
  import { RichTextGeometryController } from '../../../ItemGeometry/RichTextGeometryController';
17
17
  import { RICH_TEXT_LAYOUT_PENDING_CLASS } from '../../../utils/RichTextConverter/RichTextConverter';
18
18
  import { getHeadingTag } from '../../../utils/getHeadingTag';
19
+ import { detectLang } from '../../../utils/detectLang';
19
20
 
20
21
  export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, onResize, interactionCtrl, onVisibilityChange }) => {
21
22
  const reactId = useId();
@@ -63,6 +64,7 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
63
64
  const isInteractive = colorAlpha !== 0 || hasVisibleRangeColors;
64
65
  const [content, styles] = useRichTextItem(item);
65
66
  const Wrapper = (getHeadingTag(item) ?? 'div') as 'div';
67
+ const lang = useMemo(() => detectLang(item.commonParams.text), [item.commonParams.text]);
66
68
  useEffect(() => {
67
69
  onVisibilityChange?.(isInteractive);
68
70
  }, [isInteractive, onVisibilityChange]);
@@ -70,6 +72,7 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
70
72
  return (
71
73
  <>
72
74
  <Wrapper
75
+ lang={lang}
73
76
  ref={setRef}
74
77
  className={`rich-text-wrapper-${item.id}${layoutId ? '' : ` ${RICH_TEXT_LAYOUT_PENDING_CLASS}`}`}
75
78
  style={{
@@ -0,0 +1,73 @@
1
+ // franc-min ships no types, and this package publishes raw .ts as its
2
+ // "types" entry, so downstream projects type-check this file directly
3
+ // out of node_modules rather than a compiled .d.ts. No local `declare
4
+ // module` here on purpose — that would only cover this package's own
5
+ // build and leave @ts-expect-error unused (and thus itself an error).
6
+ // @ts-expect-error franc-min has no type declarations
7
+ import franc from 'franc-min';
8
+
9
+ // Maps the ISO 639-3 codes franc-min can detect to their ISO 639-1
10
+ // equivalent, since `lang` in the browser is conventionally two letters.
11
+ // Codes with no ISO 639-1 equivalent are passed through as-is — a bare
12
+ // ISO 639-3 code is still a valid BCP 47 language tag.
13
+ const ISO_639_3_TO_1: Record<string, string> = {
14
+ arb: 'ar',
15
+ azj: 'az',
16
+ bel: 'be',
17
+ bos: 'bs',
18
+ bul: 'bg',
19
+ ces: 'cs',
20
+ deu: 'de',
21
+ eng: 'en',
22
+ fas: 'fa',
23
+ fra: 'fr',
24
+ hau: 'ha',
25
+ hin: 'hi',
26
+ hrv: 'hr',
27
+ hun: 'hu',
28
+ ibo: 'ig',
29
+ ind: 'id',
30
+ ita: 'it',
31
+ jav: 'jv',
32
+ kaz: 'kk',
33
+ kin: 'rw',
34
+ lin: 'ln',
35
+ mar: 'mr',
36
+ nep: 'ne',
37
+ nld: 'nl',
38
+ nya: 'ny',
39
+ pol: 'pl',
40
+ por: 'pt',
41
+ ron: 'ro',
42
+ run: 'rn',
43
+ rus: 'ru',
44
+ som: 'so',
45
+ spa: 'es',
46
+ srp: 'sr',
47
+ sun: 'su',
48
+ swe: 'sv',
49
+ swh: 'sw',
50
+ tgl: 'tl',
51
+ tur: 'tr',
52
+ ukr: 'uk',
53
+ urd: 'ur',
54
+ uzn: 'uz',
55
+ vie: 'vi',
56
+ yor: 'yo',
57
+ zlm: 'ms',
58
+ zul: 'zu'
59
+ };
60
+
61
+ // Below this many characters franc-min's own trigram model is unreliable
62
+ // (headings, labels, short captions) — leave `lang` unset so it inherits
63
+ // from the document instead of guessing wrong.
64
+ const MIN_DETECTABLE_LENGTH = 20;
65
+
66
+ export function detectLang(text: string | undefined): string | undefined {
67
+ if (!text) return undefined;
68
+ const trimmed = text.trim();
69
+ if (trimmed.length < MIN_DETECTABLE_LENGTH) return undefined;
70
+ const code3 = franc(trimmed, { minLength: MIN_DETECTABLE_LENGTH });
71
+ if (code3 === 'und') return undefined;
72
+ return ISO_639_3_TO_1[code3] ?? code3;
73
+ }