@cntrl-site/sdk-nextjs 1.9.138 → 1.10.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/.idea/codeStyles/codeStyleConfig.xml +5 -0
- package/.idea/modules.xml +8 -0
- package/.idea/sdk-nextjs.iml +12 -0
- package/.idea/vcs.xml +6 -0
- package/lib/components/items/RichTextItem/RichTextItem.js +20 -2
- package/lib/components/items/useRichTextItemValues.js +4 -1
- package/lib/utils/detectLang.js +75 -0
- package/package.json +3 -2
- package/src/components/items/RichTextItem/RichTextItem.tsx +26 -2
- package/src/components/items/useRichTextItemValues.ts +4 -1
- package/src/types/components-utils.d.ts +5 -0
- package/src/utils/detectLang.ts +67 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ProjectModuleManager">
|
|
4
|
+
<modules>
|
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/sdk-nextjs.iml" filepath="$PROJECT_DIR$/.idea/sdk-nextjs.iml" />
|
|
6
|
+
</modules>
|
|
7
|
+
</component>
|
|
8
|
+
</project>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module type="WEB_MODULE" version="4">
|
|
3
|
+
<component name="NewModuleRootManager">
|
|
4
|
+
<content url="file://$MODULE_DIR$">
|
|
5
|
+
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
|
6
|
+
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
|
7
|
+
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
|
8
|
+
</content>
|
|
9
|
+
<orderEntry type="inheritedJdk" />
|
|
10
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
|
11
|
+
</component>
|
|
12
|
+
</module>
|
package/.idea/vcs.xml
ADDED
|
@@ -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)();
|
|
@@ -29,7 +30,8 @@ const RichTextItem = ({ item, sectionId, onResize, interactionCtrl, onVisibility
|
|
|
29
30
|
const [ref, setRef] = (0, react_1.useState)(null);
|
|
30
31
|
const { layouts } = (0, useCntrlContext_1.useCntrlContext)();
|
|
31
32
|
const itemAngle = (0, useItemAngle_1.useItemAngle)(item, sectionId);
|
|
32
|
-
const { blur: itemBlur, wordSpacing: itemWordSpacing, letterSpacing: itemLetterSpacing, color: itemColor, fontSize, lineHeight, xSizing } = (0, useRichTextItemValues_1.useRichTextItemValues)(item, sectionId);
|
|
33
|
+
const { blur: itemBlur, wordSpacing: itemWordSpacing, letterSpacing: itemLetterSpacing, color: itemColor, fontSize, lineHeight, xSizing, columns, columnGutter, hyphens } = (0, useRichTextItemValues_1.useRichTextItemValues)(item, sectionId);
|
|
34
|
+
const columnsEnabled = (columns !== null && columns !== void 0 ? columns : 1) > 1;
|
|
33
35
|
const layoutValues = [item.area, item.layoutParams];
|
|
34
36
|
const exemplary = (0, useExemplary_1.useExemplary)();
|
|
35
37
|
const { layoutId } = (0, useCurrentLayout_1.useCurrentLayout)();
|
|
@@ -58,11 +60,16 @@ const RichTextItem = ({ item, sectionId, onResize, interactionCtrl, onVisibility
|
|
|
58
60
|
const isInteractive = colorAlpha !== 0 || hasVisibleRangeColors;
|
|
59
61
|
const [content, styles] = (0, useRichTextItem_1.useRichTextItem)(item);
|
|
60
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]);
|
|
61
64
|
(0, react_1.useEffect)(() => {
|
|
62
65
|
onVisibilityChange === null || onVisibilityChange === void 0 ? void 0 : onVisibilityChange(isInteractive);
|
|
63
66
|
}, [isInteractive, onVisibilityChange]);
|
|
64
|
-
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({}, (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` } : {})), { 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]) => {
|
|
68
|
+
var _a, _b, _c;
|
|
65
69
|
const color = color_1.CntrlColor.parse(layoutParams.color);
|
|
70
|
+
const columns = (_a = layoutParams.columns) !== null && _a !== void 0 ? _a : 1;
|
|
71
|
+
const columnsEnabled = columns > 1;
|
|
72
|
+
const hyphens = (_b = layoutParams.hyphens) !== null && _b !== void 0 ? _b : sdk_1.Hyphens.None;
|
|
66
73
|
return (`
|
|
67
74
|
.rich-text-wrapper-${item.id} {
|
|
68
75
|
margin: 0;
|
|
@@ -79,6 +86,17 @@ const RichTextItem = ({ item, sectionId, onResize, interactionCtrl, onVisibility
|
|
|
79
86
|
transform: rotate(${area.angle}deg);
|
|
80
87
|
filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
|
|
81
88
|
text-transform: ${layoutParams.textTransform};
|
|
89
|
+
column-count: ${columnsEnabled ? columns : 'unset'};
|
|
90
|
+
column-gap: ${columnsEnabled ? `${((_c = layoutParams.columnGutter) !== null && _c !== void 0 ? _c : 0) * 100}vw` : 'unset'};
|
|
91
|
+
column-fill: ${columnsEnabled ? 'balance' : 'unset'};
|
|
92
|
+
hyphens: ${hyphens};
|
|
93
|
+
-webkit-hyphens: ${hyphens};
|
|
94
|
+
hyphenate-limit-chars: 6 3 2;
|
|
95
|
+
hyphenate-limit-lines: 2;
|
|
96
|
+
hyphenate-limit-last: always;
|
|
97
|
+
hyphenate-limit-zone: 8%;
|
|
98
|
+
-webkit-hyphenate-limit-before: 3;
|
|
99
|
+
-webkit-hyphenate-limit-after: 2;
|
|
82
100
|
${layoutParams.blur !== 0 ? 'will-change: transform;' : ''}
|
|
83
101
|
}
|
|
84
102
|
@supports not (color: oklch(42% 0.3 90 / 1)) {
|
|
@@ -34,6 +34,9 @@ const useRichTextItemValues = (item, sectionId) => {
|
|
|
34
34
|
const fontSize = layoutId ? item.layoutParams[layoutId].fontSize : undefined;
|
|
35
35
|
const lineHeight = layoutId ? item.layoutParams[layoutId].lineHeight : undefined;
|
|
36
36
|
const xSizing = layoutId ? item.layoutParams[layoutId].sizing : undefined;
|
|
37
|
-
|
|
37
|
+
const columns = layoutId ? item.layoutParams[layoutId].columns : undefined;
|
|
38
|
+
const columnGutter = layoutId ? item.layoutParams[layoutId].columnGutter : undefined;
|
|
39
|
+
const hyphens = layoutId ? item.layoutParams[layoutId].hyphens : undefined;
|
|
40
|
+
return { blur, letterSpacing, wordSpacing, color, fontSize, lineHeight, xSizing, columns, columnGutter, hyphens };
|
|
38
41
|
};
|
|
39
42
|
exports.useRichTextItemValues = useRichTextItemValues;
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
const franc_min_1 = __importDefault(require("franc-min"));
|
|
8
|
+
// Maps the ISO 639-3 codes franc-min can detect to their ISO 639-1
|
|
9
|
+
// equivalent, since `lang` in the browser is conventionally two letters.
|
|
10
|
+
// Codes with no ISO 639-1 equivalent are passed through as-is — a bare
|
|
11
|
+
// ISO 639-3 code is still a valid BCP 47 language tag.
|
|
12
|
+
const ISO_639_3_TO_1 = {
|
|
13
|
+
arb: 'ar',
|
|
14
|
+
azj: 'az',
|
|
15
|
+
bel: 'be',
|
|
16
|
+
bos: 'bs',
|
|
17
|
+
bul: 'bg',
|
|
18
|
+
ces: 'cs',
|
|
19
|
+
deu: 'de',
|
|
20
|
+
eng: 'en',
|
|
21
|
+
fas: 'fa',
|
|
22
|
+
fra: 'fr',
|
|
23
|
+
hau: 'ha',
|
|
24
|
+
hin: 'hi',
|
|
25
|
+
hrv: 'hr',
|
|
26
|
+
hun: 'hu',
|
|
27
|
+
ibo: 'ig',
|
|
28
|
+
ind: 'id',
|
|
29
|
+
ita: 'it',
|
|
30
|
+
jav: 'jv',
|
|
31
|
+
kaz: 'kk',
|
|
32
|
+
kin: 'rw',
|
|
33
|
+
lin: 'ln',
|
|
34
|
+
mar: 'mr',
|
|
35
|
+
nep: 'ne',
|
|
36
|
+
nld: 'nl',
|
|
37
|
+
nya: 'ny',
|
|
38
|
+
pol: 'pl',
|
|
39
|
+
por: 'pt',
|
|
40
|
+
ron: 'ro',
|
|
41
|
+
run: 'rn',
|
|
42
|
+
rus: 'ru',
|
|
43
|
+
som: 'so',
|
|
44
|
+
spa: 'es',
|
|
45
|
+
srp: 'sr',
|
|
46
|
+
sun: 'su',
|
|
47
|
+
swe: 'sv',
|
|
48
|
+
swh: 'sw',
|
|
49
|
+
tgl: 'tl',
|
|
50
|
+
tur: 'tr',
|
|
51
|
+
ukr: 'uk',
|
|
52
|
+
urd: 'ur',
|
|
53
|
+
uzn: 'uz',
|
|
54
|
+
vie: 'vi',
|
|
55
|
+
yor: 'yo',
|
|
56
|
+
zlm: 'ms',
|
|
57
|
+
zul: 'zu'
|
|
58
|
+
};
|
|
59
|
+
// Below this many characters franc-min's own trigram model is unreliable
|
|
60
|
+
// (headings, labels, short captions) — leave `lang` unset so it inherits
|
|
61
|
+
// from the document instead of guessing wrong.
|
|
62
|
+
const MIN_DETECTABLE_LENGTH = 20;
|
|
63
|
+
function detectLang(text) {
|
|
64
|
+
var _a;
|
|
65
|
+
if (!text)
|
|
66
|
+
return undefined;
|
|
67
|
+
const trimmed = text.trim();
|
|
68
|
+
if (trimmed.length < MIN_DETECTABLE_LENGTH)
|
|
69
|
+
return undefined;
|
|
70
|
+
const code3 = (0, franc_min_1.default)(trimmed, { minLength: MIN_DETECTABLE_LENGTH });
|
|
71
|
+
if (code3 === 'und')
|
|
72
|
+
return undefined;
|
|
73
|
+
return (_a = ISO_639_3_TO_1[code3]) !== null && _a !== void 0 ? _a : code3;
|
|
74
|
+
}
|
|
75
|
+
exports.detectLang = detectLang;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cntrl-site/sdk-nextjs",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "SDK for Next.js",
|
|
5
5
|
"author": "arsen@momdesign.nyc",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,9 +33,10 @@
|
|
|
33
33
|
"@cntrl-site/color": "^1.0.0",
|
|
34
34
|
"@cntrl-site/components": "^1.0.85",
|
|
35
35
|
"@cntrl-site/effects": "^1.4.2",
|
|
36
|
-
"@cntrl-site/sdk": "^1.28.
|
|
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"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FC, useEffect, useId, useMemo, useState } from 'react';
|
|
2
2
|
import { CntrlColor } from '@cntrl-site/color';
|
|
3
|
-
import { getLayoutStyles, RichTextItem as TRichTextItem } from '@cntrl-site/sdk';
|
|
3
|
+
import { getLayoutStyles, Hyphens, RichTextItem as TRichTextItem } from '@cntrl-site/sdk';
|
|
4
4
|
import JSXStyle from 'styled-jsx/style';
|
|
5
5
|
import { ItemProps } from '../Item';
|
|
6
6
|
import { useRichTextItem } from './useRichTextItem';
|
|
@@ -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();
|
|
@@ -30,8 +31,12 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
|
|
|
30
31
|
color: itemColor,
|
|
31
32
|
fontSize,
|
|
32
33
|
lineHeight,
|
|
33
|
-
xSizing
|
|
34
|
+
xSizing,
|
|
35
|
+
columns,
|
|
36
|
+
columnGutter,
|
|
37
|
+
hyphens
|
|
34
38
|
} = useRichTextItemValues(item, sectionId);
|
|
39
|
+
const columnsEnabled = (columns ?? 1) > 1;
|
|
35
40
|
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
|
|
36
41
|
const exemplary = useExemplary();
|
|
37
42
|
const { layoutId } = useCurrentLayout();
|
|
@@ -59,6 +64,7 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
|
|
|
59
64
|
const isInteractive = colorAlpha !== 0 || hasVisibleRangeColors;
|
|
60
65
|
const [content, styles] = useRichTextItem(item);
|
|
61
66
|
const Wrapper = (getHeadingTag(item) ?? 'div') as 'div';
|
|
67
|
+
const lang = useMemo(() => detectLang(item.commonParams.text), [item.commonParams.text]);
|
|
62
68
|
useEffect(() => {
|
|
63
69
|
onVisibilityChange?.(isInteractive);
|
|
64
70
|
}, [isInteractive, onVisibilityChange]);
|
|
@@ -66,6 +72,7 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
|
|
|
66
72
|
return (
|
|
67
73
|
<>
|
|
68
74
|
<Wrapper
|
|
75
|
+
lang={lang}
|
|
69
76
|
ref={setRef}
|
|
70
77
|
className={`rich-text-wrapper-${item.id}${layoutId ? '' : ` ${RICH_TEXT_LAYOUT_PENDING_CLASS}`}`}
|
|
71
78
|
style={{
|
|
@@ -76,6 +83,9 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
|
|
|
76
83
|
...(wordSpacing !== undefined ? { wordSpacing: `${wordSpacing as number * exemplary}px` } : {}),
|
|
77
84
|
...(fontSize !== undefined ? { fontSize: `${Math.round(fontSize * exemplary)}px` } : {}),
|
|
78
85
|
...(lineHeight !== undefined ? { lineHeight: `${lineHeight * exemplary}px` } : {}),
|
|
86
|
+
...(columnsEnabled ? { columnCount: columns, columnFill: 'balance' as const } : {}),
|
|
87
|
+
...(columnsEnabled && columnGutter !== undefined ? { columnGap: `${Math.round(columnGutter * exemplary)}px` } : {}),
|
|
88
|
+
...(hyphens !== undefined ? { hyphens, WebkitHyphens: hyphens } : {}),
|
|
79
89
|
willChange: blur !== 0 && blur !== undefined ? 'transform' : 'unset',
|
|
80
90
|
transition
|
|
81
91
|
}}
|
|
@@ -86,6 +96,9 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
|
|
|
86
96
|
{styles}
|
|
87
97
|
{`${getLayoutStyles(layouts, layoutValues, ([area, layoutParams]) => {
|
|
88
98
|
const color = CntrlColor.parse(layoutParams.color);
|
|
99
|
+
const columns = layoutParams.columns ?? 1;
|
|
100
|
+
const columnsEnabled = columns > 1;
|
|
101
|
+
const hyphens = layoutParams.hyphens ?? Hyphens.None;
|
|
89
102
|
return (`
|
|
90
103
|
.rich-text-wrapper-${item.id} {
|
|
91
104
|
margin: 0;
|
|
@@ -102,6 +115,17 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
|
|
|
102
115
|
transform: rotate(${area.angle}deg);
|
|
103
116
|
filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
|
|
104
117
|
text-transform: ${layoutParams.textTransform};
|
|
118
|
+
column-count: ${columnsEnabled ? columns : 'unset'};
|
|
119
|
+
column-gap: ${columnsEnabled ? `${(layoutParams.columnGutter ?? 0) * 100}vw` : 'unset'};
|
|
120
|
+
column-fill: ${columnsEnabled ? 'balance' : 'unset'};
|
|
121
|
+
hyphens: ${hyphens};
|
|
122
|
+
-webkit-hyphens: ${hyphens};
|
|
123
|
+
hyphenate-limit-chars: 6 3 2;
|
|
124
|
+
hyphenate-limit-lines: 2;
|
|
125
|
+
hyphenate-limit-last: always;
|
|
126
|
+
hyphenate-limit-zone: 8%;
|
|
127
|
+
-webkit-hyphenate-limit-before: 3;
|
|
128
|
+
-webkit-hyphenate-limit-after: 2;
|
|
105
129
|
${layoutParams.blur !== 0 ? 'will-change: transform;' : ''}
|
|
106
130
|
}
|
|
107
131
|
@supports not (color: oklch(42% 0.3 90 / 1)) {
|
|
@@ -61,6 +61,9 @@ export const useRichTextItemValues = (item: RichTextItem, sectionId: string) =>
|
|
|
61
61
|
const fontSize = layoutId ? item.layoutParams[layoutId].fontSize : undefined;
|
|
62
62
|
const lineHeight = layoutId ? item.layoutParams[layoutId].lineHeight : undefined;
|
|
63
63
|
const xSizing = layoutId ? item.layoutParams[layoutId].sizing : undefined;
|
|
64
|
+
const columns = layoutId ? item.layoutParams[layoutId].columns : undefined;
|
|
65
|
+
const columnGutter = layoutId ? item.layoutParams[layoutId].columnGutter : undefined;
|
|
66
|
+
const hyphens = layoutId ? item.layoutParams[layoutId].hyphens : undefined;
|
|
64
67
|
|
|
65
|
-
return { blur, letterSpacing, wordSpacing, color, fontSize, lineHeight, xSizing };
|
|
68
|
+
return { blur, letterSpacing, wordSpacing, color, fontSize, lineHeight, xSizing, columns, columnGutter, hyphens };
|
|
66
69
|
};
|
|
@@ -5,3 +5,8 @@ declare module '@cntrl-site/components/utils' {
|
|
|
5
5
|
export function getPositionStyles(position: any): Record<string, any>;
|
|
6
6
|
export function getImageRect(rect: any): Record<string, any>;
|
|
7
7
|
}
|
|
8
|
+
|
|
9
|
+
declare module 'franc-min' {
|
|
10
|
+
function franc(text: string, options?: { minLength?: number; only?: string[]; blacklist?: string[] }): string;
|
|
11
|
+
export = franc;
|
|
12
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import franc from 'franc-min';
|
|
2
|
+
|
|
3
|
+
// Maps the ISO 639-3 codes franc-min can detect to their ISO 639-1
|
|
4
|
+
// equivalent, since `lang` in the browser is conventionally two letters.
|
|
5
|
+
// Codes with no ISO 639-1 equivalent are passed through as-is — a bare
|
|
6
|
+
// ISO 639-3 code is still a valid BCP 47 language tag.
|
|
7
|
+
const ISO_639_3_TO_1: Record<string, string> = {
|
|
8
|
+
arb: 'ar',
|
|
9
|
+
azj: 'az',
|
|
10
|
+
bel: 'be',
|
|
11
|
+
bos: 'bs',
|
|
12
|
+
bul: 'bg',
|
|
13
|
+
ces: 'cs',
|
|
14
|
+
deu: 'de',
|
|
15
|
+
eng: 'en',
|
|
16
|
+
fas: 'fa',
|
|
17
|
+
fra: 'fr',
|
|
18
|
+
hau: 'ha',
|
|
19
|
+
hin: 'hi',
|
|
20
|
+
hrv: 'hr',
|
|
21
|
+
hun: 'hu',
|
|
22
|
+
ibo: 'ig',
|
|
23
|
+
ind: 'id',
|
|
24
|
+
ita: 'it',
|
|
25
|
+
jav: 'jv',
|
|
26
|
+
kaz: 'kk',
|
|
27
|
+
kin: 'rw',
|
|
28
|
+
lin: 'ln',
|
|
29
|
+
mar: 'mr',
|
|
30
|
+
nep: 'ne',
|
|
31
|
+
nld: 'nl',
|
|
32
|
+
nya: 'ny',
|
|
33
|
+
pol: 'pl',
|
|
34
|
+
por: 'pt',
|
|
35
|
+
ron: 'ro',
|
|
36
|
+
run: 'rn',
|
|
37
|
+
rus: 'ru',
|
|
38
|
+
som: 'so',
|
|
39
|
+
spa: 'es',
|
|
40
|
+
srp: 'sr',
|
|
41
|
+
sun: 'su',
|
|
42
|
+
swe: 'sv',
|
|
43
|
+
swh: 'sw',
|
|
44
|
+
tgl: 'tl',
|
|
45
|
+
tur: 'tr',
|
|
46
|
+
ukr: 'uk',
|
|
47
|
+
urd: 'ur',
|
|
48
|
+
uzn: 'uz',
|
|
49
|
+
vie: 'vi',
|
|
50
|
+
yor: 'yo',
|
|
51
|
+
zlm: 'ms',
|
|
52
|
+
zul: 'zu'
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Below this many characters franc-min's own trigram model is unreliable
|
|
56
|
+
// (headings, labels, short captions) — leave `lang` unset so it inherits
|
|
57
|
+
// from the document instead of guessing wrong.
|
|
58
|
+
const MIN_DETECTABLE_LENGTH = 20;
|
|
59
|
+
|
|
60
|
+
export function detectLang(text: string | undefined): string | undefined {
|
|
61
|
+
if (!text) return undefined;
|
|
62
|
+
const trimmed = text.trim();
|
|
63
|
+
if (trimmed.length < MIN_DETECTABLE_LENGTH) return undefined;
|
|
64
|
+
const code3 = franc(trimmed, { minLength: MIN_DETECTABLE_LENGTH });
|
|
65
|
+
if (code3 === 'und') return undefined;
|
|
66
|
+
return ISO_639_3_TO_1[code3] ?? code3;
|
|
67
|
+
}
|