@intlayer/design-system 8.7.7 → 8.7.8
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/esm/components/HeightResizer/index.mjs +25 -0
- package/dist/esm/components/HeightResizer/index.mjs.map +1 -1
- package/dist/esm/components/IDE/Code.mjs +96 -13
- package/dist/esm/components/IDE/Code.mjs.map +1 -1
- package/dist/esm/components/IDE/CodeBlockHighlight.mjs +77 -0
- package/dist/esm/components/IDE/CodeBlockHighlight.mjs.map +1 -0
- package/dist/esm/components/IDE/CodeConditionalRenderer.mjs +15 -4
- package/dist/esm/components/IDE/CodeConditionalRenderer.mjs.map +1 -1
- package/dist/esm/components/IDE/CodeFormatSelector.mjs +5 -4
- package/dist/esm/components/IDE/CodeFormatSelector.mjs.map +1 -1
- package/dist/esm/components/IDE/codeTransformer.mjs +228 -0
- package/dist/esm/components/IDE/codeTransformer.mjs.map +1 -0
- package/dist/esm/components/MarkDownRender/MarkDownIframe.mjs +101 -0
- package/dist/esm/components/MarkDownRender/MarkDownIframe.mjs.map +1 -0
- package/dist/esm/components/MarkDownRender/MarkDownRender.mjs +2 -9
- package/dist/esm/components/MarkDownRender/MarkDownRender.mjs.map +1 -1
- package/dist/esm/components/WithResizer/index.mjs +24 -0
- package/dist/esm/components/WithResizer/index.mjs.map +1 -1
- package/dist/esm/hooks/reactQuery.mjs +4 -2
- package/dist/esm/hooks/reactQuery.mjs.map +1 -1
- package/dist/types/components/Badge/index.d.ts +1 -1
- package/dist/types/components/Button/Button.d.ts +2 -2
- package/dist/types/components/Command/index.d.ts +2 -2
- package/dist/types/components/Container/index.d.ts +2 -2
- package/dist/types/components/HeightResizer/index.d.ts.map +1 -1
- package/dist/types/components/IDE/Code.d.ts +3 -3
- package/dist/types/components/IDE/Code.d.ts.map +1 -1
- package/dist/types/components/IDE/CodeBlockHighlight.d.ts +20 -0
- package/dist/types/components/IDE/CodeBlockHighlight.d.ts.map +1 -0
- package/dist/types/components/IDE/CodeConditionalRenderer.d.ts.map +1 -1
- package/dist/types/components/IDE/CodeFormatSelector.d.ts +5 -1
- package/dist/types/components/IDE/CodeFormatSelector.d.ts.map +1 -1
- package/dist/types/components/IDE/codeTransformer.d.ts +25 -0
- package/dist/types/components/IDE/codeTransformer.d.ts.map +1 -0
- package/dist/types/components/Input/Checkbox.d.ts +1 -1
- package/dist/types/components/Link/Link.d.ts +2 -2
- package/dist/types/components/MarkDownRender/MarkDownIframe.d.ts +7 -0
- package/dist/types/components/MarkDownRender/MarkDownIframe.d.ts.map +1 -0
- package/dist/types/components/MarkDownRender/MarkDownRender.d.ts.map +1 -1
- package/dist/types/components/Pagination/Pagination.d.ts +1 -1
- package/dist/types/components/SwitchSelector/index.d.ts +1 -1
- package/dist/types/components/TabSelector/TabSelector.d.ts +1 -1
- package/dist/types/components/Tag/index.d.ts +1 -1
- package/dist/types/components/WithResizer/index.d.ts.map +1 -1
- package/dist/types/hooks/reactQuery.d.ts.map +1 -1
- package/package.json +19 -19
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
//#region src/components/IDE/codeTransformer.ts
|
|
2
|
+
const trim = (text) => text.trim();
|
|
3
|
+
/**
|
|
4
|
+
* Given a string like `{ A, type B, C as D }`, return the non-type named
|
|
5
|
+
* members preserving aliases: `[{ name:'A', alias:undefined }, { name:'C', alias:'D' }]`.
|
|
6
|
+
*/
|
|
7
|
+
const parseNamedImports = (inner) => inner.split(",").map(trim).filter(Boolean).map((member) => {
|
|
8
|
+
const isType = /^type\s+/.test(member);
|
|
9
|
+
const asParts = member.replace(/^type\s+/, "").trim().split(/\s+as\s+/);
|
|
10
|
+
return {
|
|
11
|
+
name: trim(asParts[0]),
|
|
12
|
+
alias: asParts[1] ? trim(asParts[1]) : void 0,
|
|
13
|
+
isType
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
/** Render a named imports list back to `{ A, B as C }` style. */
|
|
17
|
+
const renderNamedImports = (members) => {
|
|
18
|
+
return `{ ${members.map((member) => member.alias ? `${member.name} as ${member.alias}` : member.name).join(", ")} }`;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Parse all import statements from `code`.
|
|
22
|
+
* Returns them in order together with their start/end char positions.
|
|
23
|
+
*/
|
|
24
|
+
const parseImports = (code) => {
|
|
25
|
+
const result = [];
|
|
26
|
+
const importStatementRegex = /^import\s+(type\s+)?(?:([^\s{,;"'`]+)\s*,?\s*)?(?:\{([^}]*)\}\s*,?\s*)?(?:from\s+["']([^"']+)["'])?(?:\s*["']([^"']+)["'])?;?/gm;
|
|
27
|
+
let importMatch = importStatementRegex.exec(code);
|
|
28
|
+
while (importMatch !== null) {
|
|
29
|
+
const isTypeOnly = !!importMatch[1];
|
|
30
|
+
const defaultPart = importMatch[2] ? trim(importMatch[2]) : void 0;
|
|
31
|
+
const namedPart = importMatch[3] ?? "";
|
|
32
|
+
const fromModule = importMatch[4] ?? importMatch[5] ?? "";
|
|
33
|
+
const isSideEffect = !defaultPart && !namedPart && !isTypeOnly && !!fromModule;
|
|
34
|
+
const named = namedPart ? parseNamedImports(namedPart) : [];
|
|
35
|
+
result.push({
|
|
36
|
+
raw: importMatch[0],
|
|
37
|
+
start: importMatch.index,
|
|
38
|
+
end: importMatch.index + importMatch[0].length,
|
|
39
|
+
source: fromModule,
|
|
40
|
+
defaultImport: defaultPart,
|
|
41
|
+
named,
|
|
42
|
+
isSideEffect,
|
|
43
|
+
isTypeOnly
|
|
44
|
+
});
|
|
45
|
+
importMatch = importStatementRegex.exec(code);
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
/** Build map: typeName → module path, from parsed imports. */
|
|
50
|
+
const buildTypeMap = (imports) => {
|
|
51
|
+
const map = /* @__PURE__ */ new Map();
|
|
52
|
+
for (const parsedImport of imports) if (parsedImport.isTypeOnly) {
|
|
53
|
+
for (const namedMember of parsedImport.named) if (!map.has(namedMember.name)) map.set(namedMember.name, parsedImport.source);
|
|
54
|
+
} else for (const namedMember of parsedImport.named) if (namedMember.isType && !map.has(namedMember.name)) map.set(namedMember.name, parsedImport.source);
|
|
55
|
+
return map;
|
|
56
|
+
};
|
|
57
|
+
const renderImportESM = (parsedImport) => {
|
|
58
|
+
if (parsedImport.isTypeOnly) return null;
|
|
59
|
+
const valueNamed = parsedImport.named.filter((namedMember) => !namedMember.isType);
|
|
60
|
+
if (!valueNamed.length && !parsedImport.defaultImport && !parsedImport.isSideEffect) return null;
|
|
61
|
+
if (parsedImport.isSideEffect) return `import '${parsedImport.source}';`;
|
|
62
|
+
if (parsedImport.defaultImport && valueNamed.length) return `import ${parsedImport.defaultImport}, ${renderNamedImports(valueNamed)} from '${parsedImport.source}';`;
|
|
63
|
+
if (parsedImport.defaultImport) return `import ${parsedImport.defaultImport} from '${parsedImport.source}';`;
|
|
64
|
+
return `import ${renderNamedImports(valueNamed)} from '${parsedImport.source}';`;
|
|
65
|
+
};
|
|
66
|
+
const renderImportCJS = (parsedImport) => {
|
|
67
|
+
if (parsedImport.isTypeOnly) return null;
|
|
68
|
+
const valueNamed = parsedImport.named.filter((namedMember) => !namedMember.isType);
|
|
69
|
+
const hasDefault = !!parsedImport.defaultImport;
|
|
70
|
+
const hasNamed = valueNamed.length > 0;
|
|
71
|
+
if (parsedImport.isSideEffect) return `require('${parsedImport.source}');`;
|
|
72
|
+
if (hasDefault && hasNamed) return `const ${renderNamedImports([{
|
|
73
|
+
name: "default",
|
|
74
|
+
alias: parsedImport.defaultImport
|
|
75
|
+
}, ...valueNamed])} = require('${parsedImport.source}');`;
|
|
76
|
+
if (hasDefault && !hasNamed) return `const ${parsedImport.defaultImport} = require('${parsedImport.source}');`;
|
|
77
|
+
if (!hasDefault && hasNamed) return `const ${renderNamedImports(valueNamed)} = require('${parsedImport.source}');`;
|
|
78
|
+
return null;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Strip TypeScript type annotations from a code string (line by line with
|
|
82
|
+
* some multi-line awareness). Returns the stripped code and a list of JSDoc
|
|
83
|
+
* comments to insert before specific lines (indexed by original line number).
|
|
84
|
+
*/
|
|
85
|
+
const stripTypeAnnotations = (code, typeMap) => {
|
|
86
|
+
const lines = code.split("\n");
|
|
87
|
+
const jsdocInserts = /* @__PURE__ */ new Map();
|
|
88
|
+
const out = [];
|
|
89
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
90
|
+
let line = lines[lineIndex];
|
|
91
|
+
const varTypeMatch = line.match(/^(\s*(?:export\s+)?(?:const|let|var)\s+\w+)\s*:\s*([A-Z]\w*)\s*=/);
|
|
92
|
+
if (varTypeMatch) {
|
|
93
|
+
const typeName = varTypeMatch[2];
|
|
94
|
+
const modulePath = typeMap.get(typeName);
|
|
95
|
+
if (modulePath) jsdocInserts.set(out.length, `/** @type {import('${modulePath}').${typeName}} */`);
|
|
96
|
+
line = line.replace(/^(\s*(?:export\s+)?(?:const|let|var)\s+\w+)\s*:\s*[A-Z][\w<>, ]*?\s*=/, "$1 =");
|
|
97
|
+
}
|
|
98
|
+
line = line.replace(/^(\s*(?:export\s+)?(?:const|let|var)\s+\w+)\s*:\s*\w+<[^>]*>\s*=/, "$1 =");
|
|
99
|
+
line = line.replace(/\}\s*:\s*\w[\w<>, .]*\s*\)/, "})");
|
|
100
|
+
line = line.replace(/\)\s*:\s*\w[\w<>, .]*\s*(=>|\{)/, ") $1");
|
|
101
|
+
line = line.replace(/\s+satisfies\s+\w[\w<>, .]*\s*;/, ";");
|
|
102
|
+
line = line.replace(/\s+as\s+(?!const\b)\w[\w<>, .]*/, "");
|
|
103
|
+
line = line.replace(/(<\w+>)\(/, "(");
|
|
104
|
+
line = line.replace(/(\w+)<[A-Z]\w*(?:,\s*[A-Z]\w*)*>\(/g, "$1(");
|
|
105
|
+
line = line.replace(/(\w)\s*:\s*[A-Z]\w*(\))/g, "$1$2");
|
|
106
|
+
out.push(line);
|
|
107
|
+
}
|
|
108
|
+
const insertPositions = [...jsdocInserts.entries()].sort((first, second) => second[0] - first[0]);
|
|
109
|
+
for (const [pos, jsdoc] of insertPositions) {
|
|
110
|
+
const indent = (out[pos] ?? "").match(/^(\s*)/)?.[1] ?? "";
|
|
111
|
+
out.splice(pos, 0, indent + jsdoc);
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
code: out.join("\n"),
|
|
115
|
+
jsdocInserts
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
const processExportsCJS = (code) => {
|
|
119
|
+
const lines = code.split("\n");
|
|
120
|
+
const collected = [];
|
|
121
|
+
let defaultExport = null;
|
|
122
|
+
const requireLines = [];
|
|
123
|
+
const out = [];
|
|
124
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
125
|
+
const line = lines[lineIndex];
|
|
126
|
+
const defMatch = line.match(/^(\s*)export\s+default\s+(.+?);?\s*$/);
|
|
127
|
+
if (defMatch) {
|
|
128
|
+
defaultExport = trim(defMatch[2]);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const exportConstMatch = line.match(/^(\s*)export\s+(const|let|var)\s+(\w+)/);
|
|
132
|
+
if (exportConstMatch) {
|
|
133
|
+
const name = exportConstMatch[3];
|
|
134
|
+
collected.push({
|
|
135
|
+
localName: name,
|
|
136
|
+
exportedName: name
|
|
137
|
+
});
|
|
138
|
+
out.push(line.replace(/^(\s*)export\s+/, "$1"));
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const exportFnMatch = line.match(/^(\s*)export\s+(?:async\s+)?function\s+(\w+)/);
|
|
142
|
+
if (exportFnMatch) {
|
|
143
|
+
const name = exportFnMatch[2];
|
|
144
|
+
collected.push({
|
|
145
|
+
localName: name,
|
|
146
|
+
exportedName: name
|
|
147
|
+
});
|
|
148
|
+
out.push(line.replace(/^(\s*)export\s+/, "$1"));
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const reexportFromMatch = line.match(/^(\s*)export\s+\{([^}]+)\}\s+from\s+["']([^"']+)["'];?\s*$/);
|
|
152
|
+
if (reexportFromMatch) {
|
|
153
|
+
const members = parseNamedImports(reexportFromMatch[2]);
|
|
154
|
+
const modulePath = reexportFromMatch[3];
|
|
155
|
+
const valueMembers = members.filter((namedMember) => !namedMember.isType);
|
|
156
|
+
if (valueMembers.length) {
|
|
157
|
+
const origNames = valueMembers.map((namedMember) => namedMember.name);
|
|
158
|
+
requireLines.push(`const ${renderNamedImports(origNames.map((name) => ({ name })))} = require('${modulePath}');`);
|
|
159
|
+
for (const namedMember of valueMembers) collected.push({
|
|
160
|
+
localName: namedMember.name,
|
|
161
|
+
exportedName: namedMember.alias ?? namedMember.name
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
const namedExportMatch = line.match(/^(\s*)export\s+\{([^}]+)\};?\s*$/);
|
|
167
|
+
if (namedExportMatch) {
|
|
168
|
+
const members = parseNamedImports(namedExportMatch[2]).filter((namedMember) => !namedMember.isType);
|
|
169
|
+
for (const namedMember of members) collected.push({
|
|
170
|
+
localName: namedMember.name,
|
|
171
|
+
exportedName: namedMember.alias ?? namedMember.name
|
|
172
|
+
});
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
out.push(line);
|
|
176
|
+
}
|
|
177
|
+
const finalLines = requireLines.length ? [
|
|
178
|
+
...requireLines,
|
|
179
|
+
"",
|
|
180
|
+
...out
|
|
181
|
+
] : out;
|
|
182
|
+
if (defaultExport !== null && collected.length === 0) finalLines.push(`module.exports = ${defaultExport};`);
|
|
183
|
+
else if (defaultExport !== null && collected.length > 0) {
|
|
184
|
+
const parts = collected.map((exportEntry) => exportEntry.localName === exportEntry.exportedName ? exportEntry.localName : `${exportEntry.exportedName}: ${exportEntry.localName}`);
|
|
185
|
+
finalLines.push(`module.exports = { default: ${defaultExport}, ${parts.join(", ")} };`);
|
|
186
|
+
} else if (collected.length > 0) {
|
|
187
|
+
const parts = collected.map((exportEntry) => exportEntry.localName === exportEntry.exportedName ? exportEntry.localName : `${exportEntry.exportedName}: ${exportEntry.localName}`);
|
|
188
|
+
finalLines.push(`module.exports = { ${parts.join(", ")} };`);
|
|
189
|
+
}
|
|
190
|
+
return finalLines.join("\n");
|
|
191
|
+
};
|
|
192
|
+
const transformCode = (code, target) => {
|
|
193
|
+
const imports = parseImports(code);
|
|
194
|
+
const typeMap = buildTypeMap(imports);
|
|
195
|
+
let result = code;
|
|
196
|
+
const sorted = [...imports].sort((firstImport, secondImport) => secondImport.start - firstImport.start);
|
|
197
|
+
for (const parsedImport of sorted) {
|
|
198
|
+
const replacement = (target === "esm" ? renderImportESM(parsedImport) : renderImportCJS(parsedImport)) ?? "";
|
|
199
|
+
result = result.slice(0, parsedImport.start) + replacement + result.slice(parsedImport.end);
|
|
200
|
+
}
|
|
201
|
+
result = result.replace(/\n{3,}/g, "\n\n");
|
|
202
|
+
result = stripTypeAnnotations(result, typeMap).code;
|
|
203
|
+
if (target === "commonjs") result = processExportsCJS(result);
|
|
204
|
+
result = result.replace(/\n{3,}/g, "\n\n");
|
|
205
|
+
result = result.replace(/^\n+/, "").replace(/\n+$/, "");
|
|
206
|
+
return result;
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Derive the file name for a transformed format.
|
|
210
|
+
* `next.config.ts` → `next.config.mjs` (ESM) / `next.config.cjs` (CJS)
|
|
211
|
+
* `layout.tsx` → `layout.jsx` (ESM or CJS — JSX always stays .jsx)
|
|
212
|
+
*/
|
|
213
|
+
const deriveFileName = (fileName, target) => {
|
|
214
|
+
if (target === "esm") return fileName.replace(/\.tsx$/, ".jsx").replace(/\.ts$/, ".mjs");
|
|
215
|
+
return fileName.replace(/\.tsx$/, ".jsx").replace(/\.ts$/, ".cjs");
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* Derive the syntax-highlighting language for a transformed format.
|
|
219
|
+
* `typescript` → `javascript`; `tsx` → `jsx`
|
|
220
|
+
*/
|
|
221
|
+
const deriveLanguage = (language, target) => {
|
|
222
|
+
if (target === "typescript") return language;
|
|
223
|
+
return language === "tsx" ? "jsx" : language === "typescript" ? "javascript" : language;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
//#endregion
|
|
227
|
+
export { deriveFileName, deriveLanguage, transformCode };
|
|
228
|
+
//# sourceMappingURL=codeTransformer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codeTransformer.mjs","names":[],"sources":["../../../../src/components/IDE/codeTransformer.ts"],"sourcesContent":["/**\n * Runtime transformer: converts TypeScript source code to ESM or CommonJS format.\n *\n * Design constraints:\n * - Regex/string based (no AST) — runs in the browser\n * - Handles patterns that actually appear in the Intlayer docs\n * - Intentionally limited: exotic TypeScript features may not transform perfectly\n */\n\nexport type TargetCodeFormat = 'esm' | 'commonjs' | 'typescript';\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nconst trim = (text: string) => text.trim();\n\n/**\n * Given a string like `{ A, type B, C as D }`, return the non-type named\n * members preserving aliases: `[{ name:'A', alias:undefined }, { name:'C', alias:'D' }]`.\n */\nconst parseNamedImports = (\n inner: string\n): Array<{ name: string; alias?: string; isType: boolean }> =>\n inner\n .split(',')\n .map(trim)\n .filter(Boolean)\n .map((member) => {\n const isType = /^type\\s+/.test(member);\n const clean = member.replace(/^type\\s+/, '').trim();\n const asParts = clean.split(/\\s+as\\s+/);\n return {\n name: trim(asParts[0]),\n alias: asParts[1] ? trim(asParts[1]) : undefined,\n isType,\n };\n });\n\n/** Render a named imports list back to `{ A, B as C }` style. */\nconst renderNamedImports = (\n members: Array<{ name: string; alias?: string }>\n) => {\n return `{ ${members.map((member) => (member.alias ? `${member.name} as ${member.alias}` : member.name)).join(', ')} }`;\n};\n\n// ── Import parser ────────────────────────────────────────────────────────────\n\ninterface ParsedImport {\n raw: string; // original source lines (may be multi-line)\n source: string;\n defaultImport?: string;\n named: Array<{ name: string; alias?: string; isType: boolean }>;\n isSideEffect: boolean; // `import 'mod'`\n isTypeOnly: boolean; // `import type { ... }`\n}\n\n/**\n * Parse all import statements from `code`.\n * Returns them in order together with their start/end char positions.\n */\nconst parseImports = (\n code: string\n): Array<ParsedImport & { start: number; end: number }> => {\n const result: Array<ParsedImport & { start: number; end: number }> = [];\n // Match the full import statement (possibly multi-line), stopping at the semicolon.\n // Do NOT consume trailing whitespace/newlines — those belong to the surrounding code.\n const importStatementRegex =\n /^import\\s+(type\\s+)?(?:([^\\s{,;\"'`]+)\\s*,?\\s*)?(?:\\{([^}]*)\\}\\s*,?\\s*)?(?:from\\s+[\"']([^\"']+)[\"'])?(?:\\s*[\"']([^\"']+)[\"'])?;?/gm;\n\n let importMatch: RegExpExecArray | null = importStatementRegex.exec(code);\n while (importMatch !== null) {\n const isTypeOnly = !!importMatch[1];\n const defaultPart = importMatch[2] ? trim(importMatch[2]) : undefined;\n const namedPart = importMatch[3] ?? '';\n const fromModule = importMatch[4] ?? importMatch[5] ?? '';\n\n const isSideEffect =\n !defaultPart && !namedPart && !isTypeOnly && !!fromModule;\n\n const named = namedPart ? parseNamedImports(namedPart) : [];\n\n result.push({\n raw: importMatch[0],\n start: importMatch.index,\n end: importMatch.index + importMatch[0].length,\n source: fromModule,\n defaultImport: defaultPart,\n named,\n isSideEffect,\n isTypeOnly,\n });\n importMatch = importStatementRegex.exec(code);\n }\n return result;\n};\n\n// ── Type map ─────────────────────────────────────────────────────────────────\n\n/** Build map: typeName → module path, from parsed imports. */\nconst buildTypeMap = (imports: ParsedImport[]): Map<string, string> => {\n const map = new Map<string, string>();\n for (const parsedImport of imports) {\n if (parsedImport.isTypeOnly) {\n for (const namedMember of parsedImport.named) {\n if (!map.has(namedMember.name))\n map.set(namedMember.name, parsedImport.source);\n }\n } else {\n for (const namedMember of parsedImport.named) {\n if (namedMember.isType && !map.has(namedMember.name))\n map.set(namedMember.name, parsedImport.source);\n }\n }\n }\n return map;\n};\n\n// ── Import renderers ─────────────────────────────────────────────────────────\n\nconst renderImportESM = (parsedImport: ParsedImport): string | null => {\n if (parsedImport.isTypeOnly) return null; // remove entirely\n const valueNamed = parsedImport.named.filter(\n (namedMember) => !namedMember.isType\n );\n if (\n !valueNamed.length &&\n !parsedImport.defaultImport &&\n !parsedImport.isSideEffect\n )\n return null;\n if (parsedImport.isSideEffect) return `import '${parsedImport.source}';`;\n\n if (parsedImport.defaultImport && valueNamed.length) {\n return `import ${parsedImport.defaultImport}, ${renderNamedImports(valueNamed)} from '${parsedImport.source}';`;\n }\n if (parsedImport.defaultImport)\n return `import ${parsedImport.defaultImport} from '${parsedImport.source}';`;\n return `import ${renderNamedImports(valueNamed)} from '${parsedImport.source}';`;\n};\n\nconst renderImportCJS = (parsedImport: ParsedImport): string | null => {\n if (parsedImport.isTypeOnly) return null;\n const valueNamed = parsedImport.named.filter(\n (namedMember) => !namedMember.isType\n );\n const hasDefault = !!parsedImport.defaultImport;\n const hasNamed = valueNamed.length > 0;\n\n if (parsedImport.isSideEffect) return `require('${parsedImport.source}');`;\n\n if (hasDefault && hasNamed) {\n // e.g. import express, { Router } from 'express'\n // → const { default: express, Router } = require('express') OR two lines\n const allMembers = [\n { name: 'default', alias: parsedImport.defaultImport },\n ...valueNamed,\n ];\n return `const ${renderNamedImports(allMembers)} = require('${parsedImport.source}');`;\n }\n if (hasDefault && !hasNamed) {\n return `const ${parsedImport.defaultImport} = require('${parsedImport.source}');`;\n }\n if (!hasDefault && hasNamed) {\n return `const ${renderNamedImports(valueNamed)} = require('${parsedImport.source}');`;\n }\n return null;\n};\n\n// ── Type annotation stripping ────────────────────────────────────────────────\n\n/**\n * Strip TypeScript type annotations from a code string (line by line with\n * some multi-line awareness). Returns the stripped code and a list of JSDoc\n * comments to insert before specific lines (indexed by original line number).\n */\nconst stripTypeAnnotations = (\n code: string,\n typeMap: Map<string, string>\n): { code: string; jsdocInserts: Map<number, string> } => {\n const lines = code.split('\\n');\n const jsdocInserts = new Map<number, string>();\n const out: string[] = [];\n\n for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {\n let line = lines[lineIndex];\n\n // 1. `const/let/var name: SimpleType =` — simple variable type annotation\n // Add JSDoc if the type resolves to a known import module.\n const varTypeMatch = line.match(\n /^(\\s*(?:export\\s+)?(?:const|let|var)\\s+\\w+)\\s*:\\s*([A-Z]\\w*)\\s*=/\n );\n if (varTypeMatch) {\n const typeName = varTypeMatch[2];\n const modulePath = typeMap.get(typeName);\n if (modulePath) {\n jsdocInserts.set(\n out.length,\n `/** @type {import('${modulePath}').${typeName}} */`\n );\n }\n line = line.replace(\n /^(\\s*(?:export\\s+)?(?:const|let|var)\\s+\\w+)\\s*:\\s*[A-Z][\\w<>, ]*?\\s*=/,\n '$1 ='\n );\n }\n\n // 2. `const/let/var name: FC<Props> =` — generic type annotation (no JSDoc)\n line = line.replace(\n /^(\\s*(?:export\\s+)?(?:const|let|var)\\s+\\w+)\\s*:\\s*\\w+<[^>]*>\\s*=/,\n '$1 ='\n );\n\n // 3. Destructured parameter with type: `}: TypeName):` → `}):`\n // Also handles return type: `): ReturnType =>` → `) =>`\n line = line.replace(/\\}\\s*:\\s*\\w[\\w<>, .]*\\s*\\)/, '})');\n\n // 4. Return type after `)`: `): Promise<X> =>` or `): Type {`\n line = line.replace(/\\)\\s*:\\s*\\w[\\w<>, .]*\\s*(=>|\\{)/, ') $1');\n\n // 5. `satisfies TypeName` at end of expression\n line = line.replace(/\\s+satisfies\\s+\\w[\\w<>, .]*\\s*;/, ';');\n\n // 6. `as TypeName` type casts (excluding `as const`)\n line = line.replace(/\\s+as\\s+(?!const\\b)\\w[\\w<>, .]*/, '');\n\n // 7. Generic type parameter in arrow function: `<T>(` at start of arrow fn\n // e.g. `const t = <T>(content: IConfigLocales<T>) =>`\n line = line.replace(/(<\\w+>)\\(/, '(');\n\n // 8. Generic calls: `fn<Type>(` → `fn(`\n // Only strip when angle brackets look like type params, not JSX\n line = line.replace(/(\\w+)<[A-Z]\\w*(?:,\\s*[A-Z]\\w*)*>\\(/g, '$1(');\n\n // 9. Remove typed function parameters: `(param: Type)`, `param: Type,`\n // Simple single-param-with-type: `: TypeName)` → `)`\n // Avoid breaking object spread or return annotation already handled\n line = line.replace(/(\\w)\\s*:\\s*[A-Z]\\w*(\\))/g, '$1$2');\n\n out.push(line);\n }\n\n // Insert JSDoc lines (reverse order so indices stay valid)\n const insertPositions = [...jsdocInserts.entries()].sort(\n (first, second) => second[0] - first[0]\n );\n for (const [pos, jsdoc] of insertPositions) {\n // Preserve indent of the following line\n const indent = (out[pos] ?? '').match(/^(\\s*)/)?.[1] ?? '';\n out.splice(pos, 0, indent + jsdoc);\n }\n\n return { code: out.join('\\n'), jsdocInserts };\n};\n\n// ── Export processing (CJS) ──────────────────────────────────────────────────\n\ninterface CollectedExport {\n localName: string;\n exportedName: string;\n}\n\nconst processExportsCJS = (code: string): string => {\n const lines = code.split('\\n');\n const collected: CollectedExport[] = [];\n let defaultExport: string | null = null;\n const requireLines: string[] = []; // for re-exports\n const out: string[] = [];\n\n for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {\n const line = lines[lineIndex];\n\n // `export default X;` or `export default X`\n const defMatch = line.match(/^(\\s*)export\\s+default\\s+(.+?);?\\s*$/);\n if (defMatch) {\n defaultExport = trim(defMatch[2]);\n continue; // skip line, emit module.exports later\n }\n\n // `export const/let/var name = ...`\n const exportConstMatch = line.match(\n /^(\\s*)export\\s+(const|let|var)\\s+(\\w+)/\n );\n if (exportConstMatch) {\n const name = exportConstMatch[3];\n collected.push({ localName: name, exportedName: name });\n out.push(line.replace(/^(\\s*)export\\s+/, '$1'));\n continue;\n }\n\n // `export function name(...)` or `export async function name(...)`\n const exportFnMatch = line.match(\n /^(\\s*)export\\s+(?:async\\s+)?function\\s+(\\w+)/\n );\n if (exportFnMatch) {\n const name = exportFnMatch[2];\n collected.push({ localName: name, exportedName: name });\n out.push(line.replace(/^(\\s*)export\\s+/, '$1'));\n continue;\n }\n\n // `export { A, B as C } from 'mod'` — re-export\n const reexportFromMatch = line.match(\n /^(\\s*)export\\s+\\{([^}]+)\\}\\s+from\\s+[\"']([^\"']+)[\"'];?\\s*$/\n );\n if (reexportFromMatch) {\n const members = parseNamedImports(reexportFromMatch[2]);\n const modulePath = reexportFromMatch[3];\n // Only value members (no types)\n const valueMembers = members.filter((namedMember) => !namedMember.isType);\n if (valueMembers.length) {\n // require the original names\n const origNames = valueMembers.map((namedMember) => namedMember.name);\n requireLines.push(\n `const ${renderNamedImports(origNames.map((name) => ({ name })))} = require('${modulePath}');`\n );\n for (const namedMember of valueMembers) {\n collected.push({\n localName: namedMember.name,\n exportedName: namedMember.alias ?? namedMember.name,\n });\n }\n }\n continue;\n }\n\n // `export { A, B as C }` — named re-export (same module)\n const namedExportMatch = line.match(/^(\\s*)export\\s+\\{([^}]+)\\};?\\s*$/);\n if (namedExportMatch) {\n const members = parseNamedImports(namedExportMatch[2]).filter(\n (namedMember) => !namedMember.isType\n );\n for (const namedMember of members) {\n collected.push({\n localName: namedMember.name,\n exportedName: namedMember.alias ?? namedMember.name,\n });\n }\n continue;\n }\n\n out.push(line);\n }\n\n // Prepend any require lines for re-exports\n const finalLines = requireLines.length ? [...requireLines, '', ...out] : out;\n\n // Append module.exports\n if (defaultExport !== null && collected.length === 0) {\n finalLines.push(`module.exports = ${defaultExport};`);\n } else if (defaultExport !== null && collected.length > 0) {\n const parts = collected.map((exportEntry) =>\n exportEntry.localName === exportEntry.exportedName\n ? exportEntry.localName\n : `${exportEntry.exportedName}: ${exportEntry.localName}`\n );\n finalLines.push(\n `module.exports = { default: ${defaultExport}, ${parts.join(', ')} };`\n );\n } else if (collected.length > 0) {\n const parts = collected.map((exportEntry) =>\n exportEntry.localName === exportEntry.exportedName\n ? exportEntry.localName\n : `${exportEntry.exportedName}: ${exportEntry.localName}`\n );\n finalLines.push(`module.exports = { ${parts.join(', ')} };`);\n }\n\n return finalLines.join('\\n');\n};\n\n// ── Main transform entry point ────────────────────────────────────────────────\n\nexport const transformCode = (\n code: string,\n target: TargetCodeFormat\n): string => {\n // 1. Parse all imports\n const imports = parseImports(code);\n const typeMap = buildTypeMap(imports);\n\n // 2. Replace import statements in the code\n let result = code;\n // Process in reverse order so string offsets stay valid\n const sorted = [...imports].sort(\n (firstImport, secondImport) => secondImport.start - firstImport.start\n );\n for (const parsedImport of sorted) {\n const rendered =\n target === 'esm'\n ? renderImportESM(parsedImport)\n : renderImportCJS(parsedImport);\n // rendered === null means remove the import\n const replacement = rendered ?? '';\n result =\n result.slice(0, parsedImport.start) +\n replacement +\n result.slice(parsedImport.end);\n }\n\n // 3. Collapse consecutive blank lines that may result from removed imports\n result = result.replace(/\\n{3,}/g, '\\n\\n');\n\n // 4. Strip TypeScript type annotations\n const stripped = stripTypeAnnotations(result, typeMap);\n result = stripped.code;\n\n // 5. Process exports (CJS only)\n if (target === 'commonjs') {\n result = processExportsCJS(result);\n }\n\n // 6. Final cleanup: collapse any triple+ blank lines introduced by export processing,\n // then trim leading/trailing blank lines.\n result = result.replace(/\\n{3,}/g, '\\n\\n');\n result = result.replace(/^\\n+/, '').replace(/\\n+$/, '');\n\n return result;\n};\n\n/**\n * Derive the file name for a transformed format.\n * `next.config.ts` → `next.config.mjs` (ESM) / `next.config.cjs` (CJS)\n * `layout.tsx` → `layout.jsx` (ESM or CJS — JSX always stays .jsx)\n */\nexport const deriveFileName = (\n fileName: string,\n target: TargetCodeFormat\n): string => {\n if (target === 'esm') {\n // TSX → .jsx (standard ESM JSX), TS → .mjs (standard ESM module)\n return fileName.replace(/\\.tsx$/, '.jsx').replace(/\\.ts$/, '.mjs');\n }\n // CJS: TSX → .jsx (JSX stays .jsx), TS → .cjs\n return fileName.replace(/\\.tsx$/, '.jsx').replace(/\\.ts$/, '.cjs');\n};\n\n/**\n * Derive the syntax-highlighting language for a transformed format.\n * `typescript` → `javascript`; `tsx` → `jsx`\n */\nexport const deriveLanguage = (\n language: string,\n target: TargetCodeFormat\n): string => {\n if (target === 'typescript') return language;\n return language === 'tsx'\n ? 'jsx'\n : language === 'typescript'\n ? 'javascript'\n : language;\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { cn } from "../../utils/cn.mjs";
|
|
4
|
+
import { Container } from "../Container/index.mjs";
|
|
5
|
+
import { Button, ButtonSize, ButtonVariant } from "../Button/Button.mjs";
|
|
6
|
+
import { Link } from "../Link/Link.mjs";
|
|
7
|
+
import { Modal, ModalSize } from "../Modal/Modal.mjs";
|
|
8
|
+
import { useState } from "react";
|
|
9
|
+
import { MoveDiagonal } from "lucide-react";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
|
|
12
|
+
//#region src/components/MarkDownRender/MarkDownIframe.tsx
|
|
13
|
+
function embedLinkMeta(src) {
|
|
14
|
+
if (!src) return {
|
|
15
|
+
href: "",
|
|
16
|
+
label: ""
|
|
17
|
+
};
|
|
18
|
+
if (/^https?:\/\//i.test(src)) try {
|
|
19
|
+
const url = new URL(src);
|
|
20
|
+
return {
|
|
21
|
+
href: url.href,
|
|
22
|
+
label: url.host
|
|
23
|
+
};
|
|
24
|
+
} catch {
|
|
25
|
+
return {
|
|
26
|
+
href: src,
|
|
27
|
+
label: src
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
href: src,
|
|
32
|
+
label: src
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const MarkDownIframe = (props) => {
|
|
36
|
+
const { src, className, title, ...rest } = props;
|
|
37
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
38
|
+
const { href, label } = embedLinkMeta(src);
|
|
39
|
+
return /* @__PURE__ */ jsxs(Container, {
|
|
40
|
+
roundedSize: "2xl",
|
|
41
|
+
border: true,
|
|
42
|
+
borderColor: "neutral",
|
|
43
|
+
className: "overflow-hidden p-0",
|
|
44
|
+
gap: "none",
|
|
45
|
+
children: [
|
|
46
|
+
/* @__PURE__ */ jsx("iframe", {
|
|
47
|
+
...rest,
|
|
48
|
+
src,
|
|
49
|
+
title,
|
|
50
|
+
className: cn("block max-h-[80vh] min-h-[12rem] w-full border-0", className)
|
|
51
|
+
}),
|
|
52
|
+
/* @__PURE__ */ jsxs("div", {
|
|
53
|
+
className: "flex items-center justify-between gap-3 border-neutral/30 border-t bg-card/40 px-3 py-1",
|
|
54
|
+
children: [href ? /* @__PURE__ */ jsx(Link, {
|
|
55
|
+
href,
|
|
56
|
+
target: "_blank",
|
|
57
|
+
rel: "noopener noreferrer",
|
|
58
|
+
label: "",
|
|
59
|
+
color: "neutral",
|
|
60
|
+
className: "inline-flex min-w-0 max-w-[calc(100%-3rem)] items-center gap-2 text-neutral text-xs underline-offset-2 hover:text-text hover:underline",
|
|
61
|
+
children: label
|
|
62
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
63
|
+
className: "text-neutral text-sm",
|
|
64
|
+
children: "Embedded frame"
|
|
65
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
66
|
+
variant: "hoverable",
|
|
67
|
+
size: "icon-md",
|
|
68
|
+
onClick: () => setIsModalOpen(true),
|
|
69
|
+
label: "Open embedded page in fullscreen",
|
|
70
|
+
Icon: MoveDiagonal
|
|
71
|
+
})]
|
|
72
|
+
}),
|
|
73
|
+
/* @__PURE__ */ jsx(Modal, {
|
|
74
|
+
isOpen: isModalOpen,
|
|
75
|
+
onClose: () => setIsModalOpen(false),
|
|
76
|
+
size: "unset",
|
|
77
|
+
hasCloseButton: true,
|
|
78
|
+
isScrollable: true,
|
|
79
|
+
padding: "sm",
|
|
80
|
+
children: isModalOpen && src ? /* @__PURE__ */ jsx(Container, {
|
|
81
|
+
roundedSize: "2xl",
|
|
82
|
+
border: true,
|
|
83
|
+
borderColor: "neutral",
|
|
84
|
+
className: "overflow-hidden p-0",
|
|
85
|
+
gap: "none",
|
|
86
|
+
children: /* @__PURE__ */ jsx("iframe", {
|
|
87
|
+
...rest,
|
|
88
|
+
src,
|
|
89
|
+
title: title ?? "Embedded content",
|
|
90
|
+
allowFullScreen: true,
|
|
91
|
+
className: "block min-h-[82vh] w-full border-0"
|
|
92
|
+
})
|
|
93
|
+
}) : null
|
|
94
|
+
})
|
|
95
|
+
]
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
//#endregion
|
|
100
|
+
export { MarkDownIframe };
|
|
101
|
+
//# sourceMappingURL=MarkDownIframe.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MarkDownIframe.mjs","names":[],"sources":["../../../../src/components/MarkDownRender/MarkDownIframe.tsx"],"sourcesContent":["'use client';\n\nimport { Button, ButtonSize, ButtonVariant } from '@components/Button';\nimport { Container } from '@components/Container';\nimport { Link } from '@components/Link';\nimport { Modal, ModalSize } from '@components/Modal';\nimport { cn } from '@utils/cn';\nimport { MoveDiagonal } from 'lucide-react';\nimport { type ComponentProps, type FC, useState } from 'react';\n\nfunction embedLinkMeta(src: string | undefined): {\n href: string;\n label: string;\n} {\n if (!src) return { href: '', label: '' };\n if (/^https?:\\/\\//i.test(src)) {\n try {\n const url = new URL(src);\n return { href: url.href, label: url.host };\n } catch {\n return { href: src, label: src };\n }\n }\n return { href: src, label: src };\n}\n\nexport const MarkDownIframe: FC<ComponentProps<'iframe'>> = (props) => {\n const { src, className, title, ...rest } = props;\n const [isModalOpen, setIsModalOpen] = useState(false);\n const { href, label } = embedLinkMeta(src);\n\n return (\n <Container\n roundedSize=\"2xl\"\n border\n borderColor=\"neutral\"\n className=\"overflow-hidden p-0\"\n gap=\"none\"\n >\n <iframe\n {...rest}\n src={src}\n title={title}\n className={cn(\n 'block max-h-[80vh] min-h-[12rem] w-full border-0',\n className\n )}\n />\n <div className=\"flex items-center justify-between gap-3 border-neutral/30 border-t bg-card/40 px-3 py-1\">\n {href ? (\n <Link\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n label=\"\"\n color=\"neutral\"\n className=\"inline-flex min-w-0 max-w-[calc(100%-3rem)] items-center gap-2 text-neutral text-xs underline-offset-2 hover:text-text hover:underline\"\n >\n {label}\n </Link>\n ) : (\n <span className=\"text-neutral text-sm\">Embedded frame</span>\n )}\n <Button\n variant={ButtonVariant.HOVERABLE}\n size={ButtonSize.ICON_MD}\n onClick={() => setIsModalOpen(true)}\n label=\"Open embedded page in fullscreen\"\n Icon={MoveDiagonal}\n />\n </div>\n\n <Modal\n isOpen={isModalOpen}\n onClose={() => setIsModalOpen(false)}\n size={ModalSize.UNSET}\n hasCloseButton\n isScrollable\n padding=\"sm\"\n >\n {isModalOpen && src ? (\n <Container\n roundedSize=\"2xl\"\n border\n borderColor=\"neutral\"\n className=\"overflow-hidden p-0\"\n gap=\"none\"\n >\n <iframe\n {...rest}\n src={src}\n title={title ?? 'Embedded content'}\n allowFullScreen\n className=\"block min-h-[82vh] w-full border-0\"\n />\n </Container>\n ) : null}\n </Modal>\n </Container>\n );\n};\n"],"mappings":";;;;;;;;;;;;AAUA,SAAS,cAAc,KAGrB;AACA,KAAI,CAAC,IAAK,QAAO;EAAE,MAAM;EAAI,OAAO;EAAI;AACxC,KAAI,gBAAgB,KAAK,IAAI,CAC3B,KAAI;EACF,MAAM,MAAM,IAAI,IAAI,IAAI;AACxB,SAAO;GAAE,MAAM,IAAI;GAAM,OAAO,IAAI;GAAM;SACpC;AACN,SAAO;GAAE,MAAM;GAAK,OAAO;GAAK;;AAGpC,QAAO;EAAE,MAAM;EAAK,OAAO;EAAK;;AAGlC,MAAa,kBAAgD,UAAU;CACrE,MAAM,EAAE,KAAK,WAAW,OAAO,GAAG,SAAS;CAC3C,MAAM,CAAC,aAAa,kBAAkB,SAAS,MAAM;CACrD,MAAM,EAAE,MAAM,UAAU,cAAc,IAAI;AAE1C,QACE,qBAAC,WAAD;EACE,aAAY;EACZ;EACA,aAAY;EACZ,WAAU;EACV,KAAI;YALN;GAOE,oBAAC,UAAD;IACE,GAAI;IACC;IACE;IACP,WAAW,GACT,oDACA,UACD;IACD;GACF,qBAAC,OAAD;IAAK,WAAU;cAAf,CACG,OACC,oBAAC,MAAD;KACQ;KACN,QAAO;KACP,KAAI;KACJ,OAAM;KACN,OAAM;KACN,WAAU;eAET;KACI,IAEP,oBAAC,QAAD;KAAM,WAAU;eAAuB;KAAqB,GAE9D,oBAAC,QAAD;KACE;KACA;KACA,eAAe,eAAe,KAAK;KACnC,OAAM;KACN,MAAM;KACN,EACE;;GAEN,oBAAC,OAAD;IACE,QAAQ;IACR,eAAe,eAAe,MAAM;IACpC;IACA;IACA;IACA,SAAQ;cAEP,eAAe,MACd,oBAAC,WAAD;KACE,aAAY;KACZ;KACA,aAAY;KACZ,WAAU;KACV,KAAI;eAEJ,oBAAC,UAAD;MACE,GAAI;MACC;MACL,OAAO,SAAS;MAChB;MACA,WAAU;MACV;KACQ,IACV;IACE;GACE"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { cn } from "../../utils/cn.mjs";
|
|
2
|
-
import { Container } from "../Container/index.mjs";
|
|
3
2
|
import { Link } from "../Link/Link.mjs";
|
|
4
3
|
import { H1, H2, H3, H4, H5, H6 } from "../Headers/index.mjs";
|
|
5
4
|
import { CodeProvider } from "../IDE/CodeContext.mjs";
|
|
@@ -7,6 +6,7 @@ import { Code } from "../IDE/Code.mjs";
|
|
|
7
6
|
import { TabProvider } from "../Tab/TabContext.mjs";
|
|
8
7
|
import { Tab } from "../Tab/Tab.mjs";
|
|
9
8
|
import { SmartTable } from "../Table/SmartTable.mjs";
|
|
9
|
+
import { MarkDownIframe } from "./MarkDownIframe.mjs";
|
|
10
10
|
import { memo } from "react";
|
|
11
11
|
import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
|
|
12
12
|
import { renderMarkdown } from "react-intlayer";
|
|
@@ -133,14 +133,7 @@ const ColumnRenderer = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
|
133
133
|
className: cn("flex-1", className),
|
|
134
134
|
...props
|
|
135
135
|
});
|
|
136
|
-
const Iframe = (props) => /* @__PURE__ */ jsx(
|
|
137
|
-
roundedSize: "2xl",
|
|
138
|
-
border: true,
|
|
139
|
-
background: "none",
|
|
140
|
-
borderColor: "neutral",
|
|
141
|
-
className: "overflow-hidden",
|
|
142
|
-
children: /* @__PURE__ */ jsx("iframe", { ...props })
|
|
143
|
-
});
|
|
136
|
+
const Iframe = (props) => /* @__PURE__ */ jsx(MarkDownIframe, { ...props });
|
|
144
137
|
const staticMarkdownComponents = {
|
|
145
138
|
h1: H1Renderer,
|
|
146
139
|
h2: H2Renderer,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarkDownRender.mjs","names":[],"sources":["../../../../src/components/MarkDownRender/MarkDownRender.tsx"],"sourcesContent":["import { Container } from '@components/Container';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport { cn } from '@utils/cn';\nimport type { ComponentProps, ComponentPropsWithoutRef, FC } from 'react';\nimport { memo } from 'react';\nimport {\n type MarkdownRenderer as MarkdownRendererIntlayer,\n renderMarkdown,\n} from 'react-intlayer';\nimport type { BundledLanguage } from 'shiki/bundle/web';\nimport { H1, H2, H3, H4, H5, H6 } from '../Headers';\nimport { Code } from '../IDE/Code';\nimport { CodeProvider } from '../IDE/CodeContext';\nimport { Link } from '../Link';\nimport { Tab } from '../Tab';\nimport { TabProvider } from '../Tab/TabContext';\nimport { SmartTable } from '../Table';\n\n// Extracted, stable component renderers\nconst H1Renderer = (props: ComponentProps<'h1'>) => (\n <H1 isClickable className=\"text-text\" {...props} />\n);\nconst H2Renderer = (props: ComponentProps<'h2'>) => (\n <H2 isClickable className=\"mt-16 text-text\" {...props} />\n);\nconst H3Renderer = (props: ComponentProps<'h3'>) => (\n <H3 isClickable className=\"mt-5 text-text\" {...props} />\n);\nconst H4Renderer = (props: ComponentProps<'h4'>) => (\n <H4 isClickable className=\"mt-3 text-text\" {...props} />\n);\nconst H5Renderer = (props: ComponentProps<'h5'>) => (\n <H5 isClickable className=\"mt-3 text-text\" {...props} />\n);\nconst H6Renderer = (props: ComponentProps<'h6'>) => (\n <H6 isClickable className=\"mt-3 text-text\" {...props} />\n);\nconst StrongRenderer = (props: ComponentProps<'strong'>) => (\n <strong className=\"text-text\" {...props} />\n);\n\nconst MemoizedCodeBlock = memo(\n ({\n className,\n children,\n isDarkMode,\n ...rest\n }: ComponentProps<'code'> & { isDarkMode?: boolean }) => {\n const content = String(children ?? '').replace(/\\n$/, '');\n const isBlock = !!className;\n\n if (!isBlock) {\n const decodedContent = content.replace(\n /&(?:amp;)?#(\\d+);/g,\n (_, code: string) => String.fromCharCode(parseInt(code, 10))\n );\n return (\n <code className=\"rounded-md border border-neutral/30 bg-card/60 box-decoration-clone px-1.5 py-0.5 font-mono text-sm\">\n {decodedContent}\n </code>\n );\n }\n\n const language = (className?.replace(/lang(?:uage)?-/, '') ||\n 'plaintext') as BundledLanguage;\n\n return (\n <Code {...rest} language={language} showHeader isDarkMode={isDarkMode}>\n {content}\n </Code>\n );\n },\n (prevProps, nextProps) =>\n prevProps.children === nextProps.children &&\n prevProps.className === nextProps.className &&\n prevProps.isDarkMode === nextProps.isDarkMode\n);\n\nconst createCodeRenderer = (isDarkMode?: boolean) => {\n return function CodeWrapper(props: ComponentProps<'code'>) {\n return <MemoizedCodeBlock {...props} isDarkMode={isDarkMode} />;\n };\n};\n\nconst BlockquoteRenderer = ({\n className,\n ...props\n}: ComponentProps<'blockquote'>) => (\n <blockquote\n className={cn(\n 'mt-5 gap-3 border-card border-l-4 pl-5 text-neutral [&_strong]:text-neutral',\n className\n )}\n {...props}\n />\n);\n\nconst UlRenderer = ({ className, ...props }: ComponentProps<'ul'>) => (\n <ul\n className={cn(\n 'mt-5 flex list-disc flex-col gap-3 pl-5 marker:text-neutral/80',\n className\n )}\n {...props}\n />\n);\n\nconst OlRenderer = ({ className, ...props }: ComponentProps<'ol'>) => (\n <ol\n className={cn(\n 'mt-5 flex list-decimal flex-col gap-3 pl-5 marker:text-neutral/80',\n className\n )}\n {...props}\n />\n);\n\nconst ImgRenderer = ({\n className,\n alt,\n src,\n ...props\n}: ComponentProps<'img'>) => (\n <img\n {...props}\n alt={alt ?? ''}\n loading=\"lazy\"\n className={cn('max-h-[80vh] max-w-full rounded-md', className)}\n src={\n src?.includes('github.com')\n ? src\n ?.replace('github.com', 'raw.githubusercontent.com')\n .replace('/blob/', '/') // GitHub raw URLs do not use /blob/\n : src\n }\n />\n);\n\nconst createLinkRenderer = (locale?: LocalesValues) => {\n return (props: ComponentProps<'a'>) => (\n <Link\n isExternalLink={props.href?.startsWith('http')}\n underlined\n locale={locale}\n label=\"\"\n color=\"text\"\n {...(props as any)}\n />\n );\n};\n\nconst PreRenderer = (props: ComponentProps<'pre'>) => <>{props.children}</>;\nconst TableRenderer = (props: ComponentProps<typeof SmartTable>) => (\n <SmartTable isRollable displayModal {...props} />\n);\nconst ThRenderer = ({ className, ...props }: ComponentProps<'th'>) => (\n <th\n className={cn('border-neutral border-b bg-neutral/10 p-4', className)}\n {...props}\n />\n);\nconst TrRenderer = ({ className, ...props }: ComponentProps<'tr'>) => (\n <tr className={cn('hover:/10 hover:bg-neutral/10', className)} {...props} />\n);\nconst TdRenderer = ({ className, ...props }: ComponentProps<'td'>) => (\n <td\n className={cn('border-neutral-500/50 border-b p-4', className)}\n {...props}\n />\n);\nconst HrRenderer = ({ className, ...props }: ComponentProps<'hr'>) => (\n <hr className={cn('mx-6 mt-16 text-neutral', className)} {...props} />\n);\n\nconst TabsRenderer = (props: ComponentProps<typeof Tab>) => (\n <Tab\n {...props}\n className=\"rounded-xl border border-card\"\n headerClassName=\"sticky rounded-xl top-24 z-5 bg-background/70 backdrop-blur overflow-x-auto\"\n />\n);\nconst ColumnsRenderer = ({\n className,\n ...props\n}: ComponentPropsWithoutRef<'div'>) => (\n <div className={cn('flex gap-4 max-md:flex-col', className)} {...props} />\n);\nconst ColumnRenderer = ({\n className,\n ...props\n}: ComponentPropsWithoutRef<'div'>) => (\n <div className={cn('flex-1', className)} {...props} />\n);\n\nconst Iframe = (props: ComponentProps<'iframe'>) => (\n <Container\n roundedSize=\"2xl\"\n border\n background=\"none\"\n borderColor=\"neutral\"\n className=\"overflow-hidden\"\n >\n <iframe {...props} />\n </Container>\n);\n\n// Static configuration object for static renderers\nconst staticMarkdownComponents = {\n h1: H1Renderer,\n h2: H2Renderer,\n h3: H3Renderer,\n h4: H4Renderer,\n h5: H5Renderer,\n h6: H6Renderer,\n strong: StrongRenderer,\n blockquote: BlockquoteRenderer,\n ul: UlRenderer,\n ol: OlRenderer,\n img: ImgRenderer,\n pre: PreRenderer,\n table: TableRenderer,\n th: ThRenderer,\n tr: TrRenderer,\n td: TdRenderer,\n hr: HrRenderer,\n Tabs: TabsRenderer,\n Tab: Tab.Item,\n Columns: ColumnsRenderer,\n Column: ColumnRenderer,\n iframe: Iframe,\n};\n\n// Factory function to create components with dynamic props\nconst createMarkdownComponents = (\n isDarkMode?: boolean,\n locale?: LocalesValues\n) => ({\n ...staticMarkdownComponents,\n code: createCodeRenderer(isDarkMode),\n a: createLinkRenderer(locale),\n});\n\n// Export static renderers for backward compatibility\nexport const baseMarkdownComponents = staticMarkdownComponents;\n\ntype MarkdownRendererProps = {\n children: string;\n isDarkMode?: boolean;\n locale?: LocalesValues;\n forceBlock?: boolean;\n preserveFrontmatter?: boolean;\n tagfilter?: boolean;\n components?: ComponentProps<typeof MarkdownRendererIntlayer>['components'];\n wrapper?: ComponentProps<typeof MarkdownRendererIntlayer>['wrapper'];\n};\n\nexport const getIntlayerMarkdownOptions = (_isDarkMode?: boolean) => ({\n components: baseMarkdownComponents,\n});\n\nexport const MarkdownRenderer: FC<MarkdownRendererProps> = ({\n children,\n isDarkMode = false,\n locale,\n forceBlock,\n preserveFrontmatter,\n tagfilter,\n components: componentsProp,\n wrapper,\n}) => {\n const markdownComponents = createMarkdownComponents(isDarkMode, locale);\n\n const markdownContent = renderMarkdown(children, {\n components: {\n ...markdownComponents,\n ...componentsProp,\n },\n wrapper,\n forceBlock,\n preserveFrontmatter,\n tagfilter,\n });\n\n return (\n <CodeProvider>\n <TabProvider>{markdownContent}</TabProvider>\n </CodeProvider>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MarkDownRender.mjs","names":[],"sources":["../../../../src/components/MarkDownRender/MarkDownRender.tsx"],"sourcesContent":["import type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport { cn } from '@utils/cn';\nimport type { ComponentProps, ComponentPropsWithoutRef, FC } from 'react';\nimport { memo } from 'react';\nimport {\n type MarkdownRenderer as MarkdownRendererIntlayer,\n renderMarkdown,\n} from 'react-intlayer';\nimport type { BundledLanguage } from 'shiki/bundle/web';\nimport { H1, H2, H3, H4, H5, H6 } from '../Headers';\nimport { Code } from '../IDE/Code';\nimport { CodeProvider } from '../IDE/CodeContext';\nimport { Link } from '../Link';\nimport { Tab } from '../Tab';\nimport { TabProvider } from '../Tab/TabContext';\nimport { SmartTable } from '../Table';\nimport { MarkDownIframe } from './MarkDownIframe';\n\n// Extracted, stable component renderers\nconst H1Renderer = (props: ComponentProps<'h1'>) => (\n <H1 isClickable className=\"text-text\" {...props} />\n);\nconst H2Renderer = (props: ComponentProps<'h2'>) => (\n <H2 isClickable className=\"mt-16 text-text\" {...props} />\n);\nconst H3Renderer = (props: ComponentProps<'h3'>) => (\n <H3 isClickable className=\"mt-5 text-text\" {...props} />\n);\nconst H4Renderer = (props: ComponentProps<'h4'>) => (\n <H4 isClickable className=\"mt-3 text-text\" {...props} />\n);\nconst H5Renderer = (props: ComponentProps<'h5'>) => (\n <H5 isClickable className=\"mt-3 text-text\" {...props} />\n);\nconst H6Renderer = (props: ComponentProps<'h6'>) => (\n <H6 isClickable className=\"mt-3 text-text\" {...props} />\n);\nconst StrongRenderer = (props: ComponentProps<'strong'>) => (\n <strong className=\"text-text\" {...props} />\n);\n\nconst MemoizedCodeBlock = memo(\n ({\n className,\n children,\n isDarkMode,\n ...rest\n }: ComponentProps<'code'> & { isDarkMode?: boolean }) => {\n const content = String(children ?? '').replace(/\\n$/, '');\n const isBlock = !!className;\n\n if (!isBlock) {\n const decodedContent = content.replace(\n /&(?:amp;)?#(\\d+);/g,\n (_, code: string) => String.fromCharCode(parseInt(code, 10))\n );\n return (\n <code className=\"rounded-md border border-neutral/30 bg-card/60 box-decoration-clone px-1.5 py-0.5 font-mono text-sm\">\n {decodedContent}\n </code>\n );\n }\n\n const language = (className?.replace(/lang(?:uage)?-/, '') ||\n 'plaintext') as BundledLanguage;\n\n return (\n <Code {...rest} language={language} showHeader isDarkMode={isDarkMode}>\n {content}\n </Code>\n );\n },\n (prevProps, nextProps) =>\n prevProps.children === nextProps.children &&\n prevProps.className === nextProps.className &&\n prevProps.isDarkMode === nextProps.isDarkMode\n);\n\nconst createCodeRenderer = (isDarkMode?: boolean) => {\n return function CodeWrapper(props: ComponentProps<'code'>) {\n return <MemoizedCodeBlock {...props} isDarkMode={isDarkMode} />;\n };\n};\n\nconst BlockquoteRenderer = ({\n className,\n ...props\n}: ComponentProps<'blockquote'>) => (\n <blockquote\n className={cn(\n 'mt-5 gap-3 border-card border-l-4 pl-5 text-neutral [&_strong]:text-neutral',\n className\n )}\n {...props}\n />\n);\n\nconst UlRenderer = ({ className, ...props }: ComponentProps<'ul'>) => (\n <ul\n className={cn(\n 'mt-5 flex list-disc flex-col gap-3 pl-5 marker:text-neutral/80',\n className\n )}\n {...props}\n />\n);\n\nconst OlRenderer = ({ className, ...props }: ComponentProps<'ol'>) => (\n <ol\n className={cn(\n 'mt-5 flex list-decimal flex-col gap-3 pl-5 marker:text-neutral/80',\n className\n )}\n {...props}\n />\n);\n\nconst ImgRenderer = ({\n className,\n alt,\n src,\n ...props\n}: ComponentProps<'img'>) => (\n <img\n {...props}\n alt={alt ?? ''}\n loading=\"lazy\"\n className={cn('max-h-[80vh] max-w-full rounded-md', className)}\n src={\n src?.includes('github.com')\n ? src\n ?.replace('github.com', 'raw.githubusercontent.com')\n .replace('/blob/', '/') // GitHub raw URLs do not use /blob/\n : src\n }\n />\n);\n\nconst createLinkRenderer = (locale?: LocalesValues) => {\n return (props: ComponentProps<'a'>) => (\n <Link\n isExternalLink={props.href?.startsWith('http')}\n underlined\n locale={locale}\n label=\"\"\n color=\"text\"\n {...(props as any)}\n />\n );\n};\n\nconst PreRenderer = (props: ComponentProps<'pre'>) => <>{props.children}</>;\nconst TableRenderer = (props: ComponentProps<typeof SmartTable>) => (\n <SmartTable isRollable displayModal {...props} />\n);\nconst ThRenderer = ({ className, ...props }: ComponentProps<'th'>) => (\n <th\n className={cn('border-neutral border-b bg-neutral/10 p-4', className)}\n {...props}\n />\n);\nconst TrRenderer = ({ className, ...props }: ComponentProps<'tr'>) => (\n <tr className={cn('hover:/10 hover:bg-neutral/10', className)} {...props} />\n);\nconst TdRenderer = ({ className, ...props }: ComponentProps<'td'>) => (\n <td\n className={cn('border-neutral-500/50 border-b p-4', className)}\n {...props}\n />\n);\nconst HrRenderer = ({ className, ...props }: ComponentProps<'hr'>) => (\n <hr className={cn('mx-6 mt-16 text-neutral', className)} {...props} />\n);\n\nconst TabsRenderer = (props: ComponentProps<typeof Tab>) => (\n <Tab\n {...props}\n className=\"rounded-xl border border-card\"\n headerClassName=\"sticky rounded-xl top-24 z-5 bg-background/70 backdrop-blur overflow-x-auto\"\n />\n);\nconst ColumnsRenderer = ({\n className,\n ...props\n}: ComponentPropsWithoutRef<'div'>) => (\n <div className={cn('flex gap-4 max-md:flex-col', className)} {...props} />\n);\nconst ColumnRenderer = ({\n className,\n ...props\n}: ComponentPropsWithoutRef<'div'>) => (\n <div className={cn('flex-1', className)} {...props} />\n);\n\nconst Iframe = (props: ComponentProps<'iframe'>) => (\n <MarkDownIframe {...props} />\n);\n\n// Static configuration object for static renderers\nconst staticMarkdownComponents = {\n h1: H1Renderer,\n h2: H2Renderer,\n h3: H3Renderer,\n h4: H4Renderer,\n h5: H5Renderer,\n h6: H6Renderer,\n strong: StrongRenderer,\n blockquote: BlockquoteRenderer,\n ul: UlRenderer,\n ol: OlRenderer,\n img: ImgRenderer,\n pre: PreRenderer,\n table: TableRenderer,\n th: ThRenderer,\n tr: TrRenderer,\n td: TdRenderer,\n hr: HrRenderer,\n Tabs: TabsRenderer,\n Tab: Tab.Item,\n Columns: ColumnsRenderer,\n Column: ColumnRenderer,\n iframe: Iframe,\n};\n\n// Factory function to create components with dynamic props\nconst createMarkdownComponents = (\n isDarkMode?: boolean,\n locale?: LocalesValues\n) => ({\n ...staticMarkdownComponents,\n code: createCodeRenderer(isDarkMode),\n a: createLinkRenderer(locale),\n});\n\n// Export static renderers for backward compatibility\nexport const baseMarkdownComponents = staticMarkdownComponents;\n\ntype MarkdownRendererProps = {\n children: string;\n isDarkMode?: boolean;\n locale?: LocalesValues;\n forceBlock?: boolean;\n preserveFrontmatter?: boolean;\n tagfilter?: boolean;\n components?: ComponentProps<typeof MarkdownRendererIntlayer>['components'];\n wrapper?: ComponentProps<typeof MarkdownRendererIntlayer>['wrapper'];\n};\n\nexport const getIntlayerMarkdownOptions = (_isDarkMode?: boolean) => ({\n components: baseMarkdownComponents,\n});\n\nexport const MarkdownRenderer: FC<MarkdownRendererProps> = ({\n children,\n isDarkMode = false,\n locale,\n forceBlock,\n preserveFrontmatter,\n tagfilter,\n components: componentsProp,\n wrapper,\n}) => {\n const markdownComponents = createMarkdownComponents(isDarkMode, locale);\n\n const markdownContent = renderMarkdown(children, {\n components: {\n ...markdownComponents,\n ...componentsProp,\n },\n wrapper,\n forceBlock,\n preserveFrontmatter,\n tagfilter,\n });\n\n return (\n <CodeProvider>\n <TabProvider>{markdownContent}</TabProvider>\n </CodeProvider>\n );\n};\n"],"mappings":"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"}
|
|
@@ -5,6 +5,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
6
|
|
|
7
7
|
//#region src/components/WithResizer/index.tsx
|
|
8
|
+
const HANDLE_DOUBLE_CLICK_ZONE_PX = 16;
|
|
8
9
|
/**
|
|
9
10
|
* WithResizer Component
|
|
10
11
|
*
|
|
@@ -117,6 +118,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
117
118
|
const WithResizer = ({ initialWidth, maxWidth, minWidth = 0, handlePosition = "right", children }) => {
|
|
118
119
|
const containerRef = useRef(null);
|
|
119
120
|
const [width, setWidth] = useState(initialWidth);
|
|
121
|
+
const lastExpandedWidthRef = useRef(initialWidth);
|
|
120
122
|
const resizeState = useRef({
|
|
121
123
|
startX: 0,
|
|
122
124
|
startWidth: 0,
|
|
@@ -174,12 +176,34 @@ const WithResizer = ({ initialWidth, maxWidth, minWidth = 0, handlePosition = "r
|
|
|
174
176
|
window.removeEventListener("touchend", stopResizing);
|
|
175
177
|
};
|
|
176
178
|
}, [resize, stopResizing]);
|
|
179
|
+
useEffect(() => {
|
|
180
|
+
if (width > minWidth) lastExpandedWidthRef.current = width;
|
|
181
|
+
}, [width, minWidth]);
|
|
182
|
+
const handleDoubleClick = useCallback((event) => {
|
|
183
|
+
const el = containerRef.current;
|
|
184
|
+
if (!el) return;
|
|
185
|
+
const { left, right } = el.getBoundingClientRect();
|
|
186
|
+
if (!(handlePosition === "right" ? right - event.clientX <= HANDLE_DOUBLE_CLICK_ZONE_PX : event.clientX - left <= HANDLE_DOUBLE_CLICK_ZONE_PX)) return;
|
|
187
|
+
event.preventDefault();
|
|
188
|
+
event.stopPropagation();
|
|
189
|
+
if (width > minWidth) {
|
|
190
|
+
setWidth(minWidth);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
setWidth(Math.min(Math.max(lastExpandedWidthRef.current, minWidth), maxWidth ?? Infinity));
|
|
194
|
+
}, [
|
|
195
|
+
handlePosition,
|
|
196
|
+
maxWidth,
|
|
197
|
+
minWidth,
|
|
198
|
+
width
|
|
199
|
+
]);
|
|
177
200
|
return /* @__PURE__ */ jsx("div", {
|
|
178
201
|
className: cn("relative h-full w-full max-w-[80%] shrink-0 cursor-ew-resize border-neutral-200 transition dark:border-neutral-950", handlePosition === "right" ? ["border-r-[2px]", "after:absolute after:top-1/2 after:right-0 after:block after:h-10 after:w-2 after:translate-x-1/2 after:-translate-y-1/2 after:transform after:cursor-ew-resize after:rounded-full after:bg-neutral-200 after:transition after:content-[\"\"] dark:after:bg-neutral-950"] : ["border-l-[2px]", "after:absolute after:top-1/2 after:left-0 after:block after:h-10 after:w-2 after:-translate-x-1/2 after:-translate-y-1/2 after:transform after:cursor-ew-resize after:rounded-full after:bg-neutral-200 after:transition after:content-[\"\"] dark:after:bg-neutral-950"], "active:border-neutral-400 active:after:bg-neutral-400 dark:active:border-neutral-600 active:dark:after:bg-neutral-600", minWidth && `min-w-[${minWidth}px]`, maxWidth && `max-w-[${maxWidth}px]`),
|
|
179
202
|
style: { width: `${width}px` },
|
|
180
203
|
ref: containerRef,
|
|
181
204
|
onMouseDown: startResizing,
|
|
182
205
|
onTouchStart: startResizing,
|
|
206
|
+
onDoubleClick: handleDoubleClick,
|
|
183
207
|
"aria-valuemin": minWidth,
|
|
184
208
|
"aria-valuemax": maxWidth,
|
|
185
209
|
"aria-valuenow": width,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/WithResizer/index.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport React, {\n type FC,\n type PropsWithChildren,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\n/**\n * Props for the WithResizer component.\n *\n * Defines the configuration for a resizable container with drag-based width adjustment.\n *\n * @example\n * ```tsx\n * // Basic resizable container\n * <WithResizer initialWidth={300} minWidth={200} maxWidth={600}>\n * <div className=\"p-4\">Resizable content</div>\n * </WithResizer>\n *\n * // Sidebar with resizing\n * <WithResizer\n * initialWidth={250}\n * minWidth={180}\n * maxWidth={400}\n * >\n * <nav className=\"h-full p-4\">\n * <SidebarContent />\n * </nav>\n * </WithResizer>\n *\n * // Panel with unlimited growth\n * <WithResizer initialWidth={400} minWidth={300}>\n * <div className=\"h-full overflow-auto\">\n * <PanelContent />\n * </div>\n * </WithResizer>\n * ```\n */\ntype WithResizerProps = {\n /** Initial width of the resizable container in pixels */\n initialWidth: number;\n /** Maximum allowed width in pixels (optional, no limit if not specified) */\n maxWidth?: number;\n /** Minimum allowed width in pixels */\n minWidth?: number;\n /** Position of the resize handle (default: 'right') */\n handlePosition?: 'left' | 'right';\n};\n\n/**\n * WithResizer Component\n *\n * A flexible container component that allows users to dynamically resize its width\n * through mouse or touch drag interactions. Perfect for creating adjustable panels,\n * sidebars, and split-pane layouts.\n *\n * ## Features\n * - **Mouse & Touch Support**: Works with both mouse drag and touch interactions\n * - **Constraint Enforcement**: Respects minimum and maximum width boundaries\n * - **Visual Feedback**: Clear resize handle with hover and active states\n * - **Smooth Interactions**: Passive event listeners for optimal performance\n * - **Accessibility**: ARIA slider role with proper value announcements\n * - **Responsive Design**: Adapts to different screen sizes and containers\n *\n * ## Technical Implementation\n * - **Event Handling**: Uses `useCallback` for optimal performance\n * - **Boundary Calculation**: Real-time width calculation based on mouse/touch position\n * - **State Management**: Tracks resizing state for visual feedback\n * - **Memory Management**: Proper cleanup of global event listeners\n * - **Touch Events**: Full support for mobile touch interactions\n *\n * ## Visual Design\n * - **Resize Handle**: Rounded handle positioned on the right border\n * - **Border Indicator**: Visual border showing resizable edge\n * - **State Feedback**: Different colors for normal, hover, and active states\n * - **Dark Mode**: Full support with appropriate color scheme\n * - **Smooth Transitions**: CSS transitions for visual polish\n *\n * ## Use Cases\n * - **Application Sidebars**: Collapsible navigation and tool panels\n * - **Content Panels**: Adjustable content areas in complex layouts\n * - **Split Panes**: Dividing screen space between multiple content areas\n * - **Inspector Panels**: Debugging tools and property inspectors\n * - **File Explorers**: Tree views with adjustable column widths\n * - **Dashboard Widgets**: Customizable widget sizes for dashboards\n *\n * ## Accessibility Features\n * - **ARIA Slider**: Proper slider role for screen readers\n * - **Value Announcements**: Current, minimum, and maximum values announced\n * - **Keyboard Focus**: Focusable with tab navigation\n * - **Clear Affordances**: Visual indicators for interactive elements\n *\n * @example\n * ```tsx\n * // Application sidebar with resizing\n * const [sidebarWidth, setSidebarWidth] = useState(250);\n *\n * <div className=\"flex h-screen\">\n * <WithResizer\n * initialWidth={sidebarWidth}\n * minWidth={200}\n * maxWidth={400}\n * >\n * <aside className=\"h-full bg-gray-100 p-4\">\n * <nav>\n * <NavItems />\n * </nav>\n * </aside>\n * </WithResizer>\n *\n * <main className=\"flex-1 p-6\">\n * <MainContent />\n * </main>\n * </div>\n *\n * // Developer tools panel\n * <WithResizer\n * initialWidth={350}\n * minWidth={250}\n * maxWidth={600}\n * >\n * <div className=\"h-full flex flex-col\">\n * <div className=\"flex-1 overflow-auto p-4\">\n * <InspectorContent />\n * </div>\n * <div className=\"border-t p-2\">\n * <Controls />\n * </div>\n * </div>\n * </WithResizer>\n *\n * // Multi-column layout\n * <div className=\"flex h-full\">\n * <WithResizer initialWidth={300} minWidth={200} maxWidth={500}>\n * <FileExplorer />\n * </WithResizer>\n *\n * <WithResizer initialWidth={400} minWidth={300}>\n * <CodeEditor />\n * </WithResizer>\n *\n * <div className=\"flex-1 min-w-0\">\n * <OutputPanel />\n * </div>\n * </div>\n * ```\n *\n * ## Performance Considerations\n * - Uses passive event listeners to prevent scroll blocking\n * - Optimized with `useCallback` to prevent unnecessary re-renders\n * - Efficient boundary calculations using `getBoundingClientRect`\n * - Minimal DOM manipulation for smooth drag interactions\n *\n * ## Browser Support\n * - Modern browsers with support for touch events\n * - Graceful degradation for older browsers\n * - Mobile-first touch interaction handling\n */\nexport const WithResizer: FC<PropsWithChildren<WithResizerProps>> = ({\n initialWidth,\n maxWidth,\n minWidth = 0,\n handlePosition = 'right',\n children,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const [width, setWidth] = useState(initialWidth);\n\n const resizeState = useRef({\n startX: 0,\n startWidth: 0,\n factor: 1,\n });\n\n // Handler to resize the div\n const resize = useCallback(\n (mouseMoveEvent: MouseEvent | TouchEvent) => {\n if (resizeState.current.startWidth === 0) return;\n\n let clientX = 0;\n if (mouseMoveEvent instanceof MouseEvent) {\n clientX = mouseMoveEvent.clientX;\n } else if (\n typeof TouchEvent !== 'undefined' &&\n mouseMoveEvent instanceof TouchEvent\n ) {\n clientX = mouseMoveEvent.touches[0].clientX;\n }\n\n const { startX, startWidth, factor } = resizeState.current;\n const delta = (clientX - startX) / factor;\n // Invert delta for left handle (moving left decreases width, moving right increases width)\n const adjustedDelta = handlePosition === 'left' ? -delta : delta;\n const newWidth = startWidth + adjustedDelta;\n\n const constrainedWidth = Math.max(\n Math.min(newWidth, maxWidth ?? Infinity),\n minWidth\n );\n\n setWidth(constrainedWidth);\n },\n [maxWidth, minWidth, handlePosition]\n );\n\n // Handler to stop resizing\n const stopResizing = useCallback(() => {\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n window.removeEventListener('mousemove', resize);\n window.removeEventListener('mouseup', stopResizing);\n window.removeEventListener('touchmove', resize);\n window.removeEventListener('touchend', stopResizing);\n }, [resize]);\n\n // Handler to start resizing\n const startResizing = useCallback(\n (\n mouseDownEvent:\n | React.MouseEvent<HTMLDivElement>\n | React.TouchEvent<HTMLDivElement>\n ) => {\n mouseDownEvent.preventDefault();\n const container = containerRef.current;\n\n if (!container) return;\n\n const { width: rectWidth } = container.getBoundingClientRect();\n const offsetWidth = container.offsetWidth;\n const factor = offsetWidth > 0 ? rectWidth / offsetWidth : 1;\n\n let clientX = 0;\n if ('touches' in mouseDownEvent) {\n clientX = mouseDownEvent.touches[0].clientX;\n } else {\n clientX = mouseDownEvent.clientX;\n }\n\n resizeState.current = {\n startX: clientX,\n startWidth: offsetWidth,\n factor,\n };\n\n document.body.style.cursor = 'ew-resize';\n document.body.style.userSelect = 'none';\n\n window.addEventListener('mousemove', resize, { passive: true });\n window.addEventListener('mouseup', stopResizing);\n window.addEventListener('touchmove', resize, { passive: true });\n window.addEventListener('touchend', stopResizing);\n },\n [resize, stopResizing]\n );\n\n useEffect(() => {\n return () => {\n window.removeEventListener('mousemove', resize);\n window.removeEventListener('mouseup', stopResizing);\n window.removeEventListener('touchmove', resize);\n window.removeEventListener('touchend', stopResizing);\n };\n }, [resize, stopResizing]);\n\n return (\n <div\n className={cn(\n 'relative h-full w-full max-w-[80%] shrink-0 cursor-ew-resize border-neutral-200 transition dark:border-neutral-950',\n handlePosition === 'right'\n ? [\n 'border-r-[2px]',\n 'after:absolute after:top-1/2 after:right-0 after:block after:h-10 after:w-2 after:translate-x-1/2 after:-translate-y-1/2 after:transform after:cursor-ew-resize after:rounded-full after:bg-neutral-200 after:transition after:content-[\"\"] dark:after:bg-neutral-950',\n ]\n : [\n 'border-l-[2px]',\n 'after:absolute after:top-1/2 after:left-0 after:block after:h-10 after:w-2 after:-translate-x-1/2 after:-translate-y-1/2 after:transform after:cursor-ew-resize after:rounded-full after:bg-neutral-200 after:transition after:content-[\"\"] dark:after:bg-neutral-950',\n ],\n 'active:border-neutral-400 active:after:bg-neutral-400 dark:active:border-neutral-600 active:dark:after:bg-neutral-600',\n minWidth && `min-w-[${minWidth}px]`,\n maxWidth && `max-w-[${maxWidth}px]`\n )}\n style={{\n width: `${width}px`,\n }}\n ref={containerRef}\n onMouseDown={startResizing}\n onTouchStart={startResizing}\n aria-valuemin={minWidth}\n aria-valuemax={maxWidth}\n aria-valuenow={width}\n aria-label=\"Resizable component\"\n role=\"slider\"\n tabIndex={0}\n >\n {/* biome-ignore lint/a11y/noStaticElementInteractions: This div stops event propagation to prevent content clicks from triggering resize */}\n <div\n role=\"presentation\"\n className=\"absolute top-0 left-0 size-full cursor-default overflow-hidden\"\n onMouseDown={(e) => e.stopPropagation()}\n onTouchStart={(e) => e.stopPropagation()}\n >\n {children}\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmKA,MAAa,eAAwD,EACnE,cACA,UACA,WAAW,GACX,iBAAiB,SACjB,eACI;CACJ,MAAM,eAAe,OAAuB,KAAK;CACjD,MAAM,CAAC,OAAO,YAAY,SAAS,aAAa;CAEhD,MAAM,cAAc,OAAO;EACzB,QAAQ;EACR,YAAY;EACZ,QAAQ;EACT,CAAC;CAGF,MAAM,SAAS,aACZ,mBAA4C;AAC3C,MAAI,YAAY,QAAQ,eAAe,EAAG;EAE1C,IAAI,UAAU;AACd,MAAI,0BAA0B,WAC5B,WAAU,eAAe;WAEzB,OAAO,eAAe,eACtB,0BAA0B,WAE1B,WAAU,eAAe,QAAQ,GAAG;EAGtC,MAAM,EAAE,QAAQ,YAAY,WAAW,YAAY;EACnD,MAAM,SAAS,UAAU,UAAU;EAGnC,MAAM,WAAW,cADK,mBAAmB,SAAS,CAAC,QAAQ;AAQ3D,WALyB,KAAK,IAC5B,KAAK,IAAI,UAAU,YAAY,SAAS,EACxC,SAGuB,CAAC;IAE5B;EAAC;EAAU;EAAU;EAAe,CACrC;CAGD,MAAM,eAAe,kBAAkB;AACrC,WAAS,KAAK,MAAM,SAAS;AAC7B,WAAS,KAAK,MAAM,aAAa;AACjC,SAAO,oBAAoB,aAAa,OAAO;AAC/C,SAAO,oBAAoB,WAAW,aAAa;AACnD,SAAO,oBAAoB,aAAa,OAAO;AAC/C,SAAO,oBAAoB,YAAY,aAAa;IACnD,CAAC,OAAO,CAAC;CAGZ,MAAM,gBAAgB,aAElB,mBAGG;AACH,iBAAe,gBAAgB;EAC/B,MAAM,YAAY,aAAa;AAE/B,MAAI,CAAC,UAAW;EAEhB,MAAM,EAAE,OAAO,cAAc,UAAU,uBAAuB;EAC9D,MAAM,cAAc,UAAU;EAC9B,MAAM,SAAS,cAAc,IAAI,YAAY,cAAc;EAE3D,IAAI,UAAU;AACd,MAAI,aAAa,eACf,WAAU,eAAe,QAAQ,GAAG;MAEpC,WAAU,eAAe;AAG3B,cAAY,UAAU;GACpB,QAAQ;GACR,YAAY;GACZ;GACD;AAED,WAAS,KAAK,MAAM,SAAS;AAC7B,WAAS,KAAK,MAAM,aAAa;AAEjC,SAAO,iBAAiB,aAAa,QAAQ,EAAE,SAAS,MAAM,CAAC;AAC/D,SAAO,iBAAiB,WAAW,aAAa;AAChD,SAAO,iBAAiB,aAAa,QAAQ,EAAE,SAAS,MAAM,CAAC;AAC/D,SAAO,iBAAiB,YAAY,aAAa;IAEnD,CAAC,QAAQ,aAAa,CACvB;AAED,iBAAgB;AACd,eAAa;AACX,UAAO,oBAAoB,aAAa,OAAO;AAC/C,UAAO,oBAAoB,WAAW,aAAa;AACnD,UAAO,oBAAoB,aAAa,OAAO;AAC/C,UAAO,oBAAoB,YAAY,aAAa;;IAErD,CAAC,QAAQ,aAAa,CAAC;AAE1B,QACE,oBAAC,OAAD;EACE,WAAW,GACT,sHACA,mBAAmB,UACf,CACE,kBACA,0QACD,GACD,CACE,kBACA,0QACD,EACL,yHACA,YAAY,UAAU,SAAS,MAC/B,YAAY,UAAU,SAAS,KAChC;EACD,OAAO,EACL,OAAO,GAAG,MAAM,KACjB;EACD,KAAK;EACL,aAAa;EACb,cAAc;EACd,iBAAe;EACf,iBAAe;EACf,iBAAe;EACf,cAAW;EACX,MAAK;EACL,UAAU;YAGV,oBAAC,OAAD;GACE,MAAK;GACL,WAAU;GACV,cAAc,MAAM,EAAE,iBAAiB;GACvC,eAAe,MAAM,EAAE,iBAAiB;GAEvC;GACG;EACF"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/WithResizer/index.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport React, {\n type FC,\n type PropsWithChildren,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\nconst HANDLE_DOUBLE_CLICK_ZONE_PX = 16;\n\n/**\n * Props for the WithResizer component.\n *\n * Defines the configuration for a resizable container with drag-based width adjustment.\n *\n * @example\n * ```tsx\n * // Basic resizable container\n * <WithResizer initialWidth={300} minWidth={200} maxWidth={600}>\n * <div className=\"p-4\">Resizable content</div>\n * </WithResizer>\n *\n * // Sidebar with resizing\n * <WithResizer\n * initialWidth={250}\n * minWidth={180}\n * maxWidth={400}\n * >\n * <nav className=\"h-full p-4\">\n * <SidebarContent />\n * </nav>\n * </WithResizer>\n *\n * // Panel with unlimited growth\n * <WithResizer initialWidth={400} minWidth={300}>\n * <div className=\"h-full overflow-auto\">\n * <PanelContent />\n * </div>\n * </WithResizer>\n * ```\n */\ntype WithResizerProps = {\n /** Initial width of the resizable container in pixels */\n initialWidth: number;\n /** Maximum allowed width in pixels (optional, no limit if not specified) */\n maxWidth?: number;\n /** Minimum allowed width in pixels */\n minWidth?: number;\n /** Position of the resize handle (default: 'right') */\n handlePosition?: 'left' | 'right';\n};\n\n/**\n * WithResizer Component\n *\n * A flexible container component that allows users to dynamically resize its width\n * through mouse or touch drag interactions. Perfect for creating adjustable panels,\n * sidebars, and split-pane layouts.\n *\n * ## Features\n * - **Mouse & Touch Support**: Works with both mouse drag and touch interactions\n * - **Constraint Enforcement**: Respects minimum and maximum width boundaries\n * - **Visual Feedback**: Clear resize handle with hover and active states\n * - **Smooth Interactions**: Passive event listeners for optimal performance\n * - **Accessibility**: ARIA slider role with proper value announcements\n * - **Responsive Design**: Adapts to different screen sizes and containers\n *\n * ## Technical Implementation\n * - **Event Handling**: Uses `useCallback` for optimal performance\n * - **Boundary Calculation**: Real-time width calculation based on mouse/touch position\n * - **State Management**: Tracks resizing state for visual feedback\n * - **Memory Management**: Proper cleanup of global event listeners\n * - **Touch Events**: Full support for mobile touch interactions\n *\n * ## Visual Design\n * - **Resize Handle**: Rounded handle positioned on the right border\n * - **Border Indicator**: Visual border showing resizable edge\n * - **State Feedback**: Different colors for normal, hover, and active states\n * - **Dark Mode**: Full support with appropriate color scheme\n * - **Smooth Transitions**: CSS transitions for visual polish\n *\n * ## Use Cases\n * - **Application Sidebars**: Collapsible navigation and tool panels\n * - **Content Panels**: Adjustable content areas in complex layouts\n * - **Split Panes**: Dividing screen space between multiple content areas\n * - **Inspector Panels**: Debugging tools and property inspectors\n * - **File Explorers**: Tree views with adjustable column widths\n * - **Dashboard Widgets**: Customizable widget sizes for dashboards\n *\n * ## Accessibility Features\n * - **ARIA Slider**: Proper slider role for screen readers\n * - **Value Announcements**: Current, minimum, and maximum values announced\n * - **Keyboard Focus**: Focusable with tab navigation\n * - **Clear Affordances**: Visual indicators for interactive elements\n *\n * @example\n * ```tsx\n * // Application sidebar with resizing\n * const [sidebarWidth, setSidebarWidth] = useState(250);\n *\n * <div className=\"flex h-screen\">\n * <WithResizer\n * initialWidth={sidebarWidth}\n * minWidth={200}\n * maxWidth={400}\n * >\n * <aside className=\"h-full bg-gray-100 p-4\">\n * <nav>\n * <NavItems />\n * </nav>\n * </aside>\n * </WithResizer>\n *\n * <main className=\"flex-1 p-6\">\n * <MainContent />\n * </main>\n * </div>\n *\n * // Developer tools panel\n * <WithResizer\n * initialWidth={350}\n * minWidth={250}\n * maxWidth={600}\n * >\n * <div className=\"h-full flex flex-col\">\n * <div className=\"flex-1 overflow-auto p-4\">\n * <InspectorContent />\n * </div>\n * <div className=\"border-t p-2\">\n * <Controls />\n * </div>\n * </div>\n * </WithResizer>\n *\n * // Multi-column layout\n * <div className=\"flex h-full\">\n * <WithResizer initialWidth={300} minWidth={200} maxWidth={500}>\n * <FileExplorer />\n * </WithResizer>\n *\n * <WithResizer initialWidth={400} minWidth={300}>\n * <CodeEditor />\n * </WithResizer>\n *\n * <div className=\"flex-1 min-w-0\">\n * <OutputPanel />\n * </div>\n * </div>\n * ```\n *\n * ## Performance Considerations\n * - Uses passive event listeners to prevent scroll blocking\n * - Optimized with `useCallback` to prevent unnecessary re-renders\n * - Efficient boundary calculations using `getBoundingClientRect`\n * - Minimal DOM manipulation for smooth drag interactions\n *\n * ## Browser Support\n * - Modern browsers with support for touch events\n * - Graceful degradation for older browsers\n * - Mobile-first touch interaction handling\n */\nexport const WithResizer: FC<PropsWithChildren<WithResizerProps>> = ({\n initialWidth,\n maxWidth,\n minWidth = 0,\n handlePosition = 'right',\n children,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const [width, setWidth] = useState(initialWidth);\n const lastExpandedWidthRef = useRef(initialWidth);\n\n const resizeState = useRef({\n startX: 0,\n startWidth: 0,\n factor: 1,\n });\n\n // Handler to resize the div\n const resize = useCallback(\n (mouseMoveEvent: MouseEvent | TouchEvent) => {\n if (resizeState.current.startWidth === 0) return;\n\n let clientX = 0;\n if (mouseMoveEvent instanceof MouseEvent) {\n clientX = mouseMoveEvent.clientX;\n } else if (\n typeof TouchEvent !== 'undefined' &&\n mouseMoveEvent instanceof TouchEvent\n ) {\n clientX = mouseMoveEvent.touches[0].clientX;\n }\n\n const { startX, startWidth, factor } = resizeState.current;\n const delta = (clientX - startX) / factor;\n // Invert delta for left handle (moving left decreases width, moving right increases width)\n const adjustedDelta = handlePosition === 'left' ? -delta : delta;\n const newWidth = startWidth + adjustedDelta;\n\n const constrainedWidth = Math.max(\n Math.min(newWidth, maxWidth ?? Infinity),\n minWidth\n );\n\n setWidth(constrainedWidth);\n },\n [maxWidth, minWidth, handlePosition]\n );\n\n // Handler to stop resizing\n const stopResizing = useCallback(() => {\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n window.removeEventListener('mousemove', resize);\n window.removeEventListener('mouseup', stopResizing);\n window.removeEventListener('touchmove', resize);\n window.removeEventListener('touchend', stopResizing);\n }, [resize]);\n\n // Handler to start resizing\n const startResizing = useCallback(\n (\n mouseDownEvent:\n | React.MouseEvent<HTMLDivElement>\n | React.TouchEvent<HTMLDivElement>\n ) => {\n mouseDownEvent.preventDefault();\n const container = containerRef.current;\n\n if (!container) return;\n\n const { width: rectWidth } = container.getBoundingClientRect();\n const offsetWidth = container.offsetWidth;\n const factor = offsetWidth > 0 ? rectWidth / offsetWidth : 1;\n\n let clientX = 0;\n if ('touches' in mouseDownEvent) {\n clientX = mouseDownEvent.touches[0].clientX;\n } else {\n clientX = mouseDownEvent.clientX;\n }\n\n resizeState.current = {\n startX: clientX,\n startWidth: offsetWidth,\n factor,\n };\n\n document.body.style.cursor = 'ew-resize';\n document.body.style.userSelect = 'none';\n\n window.addEventListener('mousemove', resize, { passive: true });\n window.addEventListener('mouseup', stopResizing);\n window.addEventListener('touchmove', resize, { passive: true });\n window.addEventListener('touchend', stopResizing);\n },\n [resize, stopResizing]\n );\n\n useEffect(() => {\n return () => {\n window.removeEventListener('mousemove', resize);\n window.removeEventListener('mouseup', stopResizing);\n window.removeEventListener('touchmove', resize);\n window.removeEventListener('touchend', stopResizing);\n };\n }, [resize, stopResizing]);\n\n useEffect(() => {\n if (width > minWidth) {\n lastExpandedWidthRef.current = width;\n }\n }, [width, minWidth]);\n\n const handleDoubleClick = useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n const el = containerRef.current;\n if (!el) return;\n\n const { left, right } = el.getBoundingClientRect();\n const inHandleZone =\n handlePosition === 'right'\n ? right - event.clientX <= HANDLE_DOUBLE_CLICK_ZONE_PX\n : event.clientX - left <= HANDLE_DOUBLE_CLICK_ZONE_PX;\n\n if (!inHandleZone) return;\n\n event.preventDefault();\n event.stopPropagation();\n\n if (width > minWidth) {\n setWidth(minWidth);\n return;\n }\n\n const target = Math.min(\n Math.max(lastExpandedWidthRef.current, minWidth),\n maxWidth ?? Infinity\n );\n setWidth(target);\n },\n [handlePosition, maxWidth, minWidth, width]\n );\n\n return (\n <div\n className={cn(\n 'relative h-full w-full max-w-[80%] shrink-0 cursor-ew-resize border-neutral-200 transition dark:border-neutral-950',\n handlePosition === 'right'\n ? [\n 'border-r-[2px]',\n 'after:absolute after:top-1/2 after:right-0 after:block after:h-10 after:w-2 after:translate-x-1/2 after:-translate-y-1/2 after:transform after:cursor-ew-resize after:rounded-full after:bg-neutral-200 after:transition after:content-[\"\"] dark:after:bg-neutral-950',\n ]\n : [\n 'border-l-[2px]',\n 'after:absolute after:top-1/2 after:left-0 after:block after:h-10 after:w-2 after:-translate-x-1/2 after:-translate-y-1/2 after:transform after:cursor-ew-resize after:rounded-full after:bg-neutral-200 after:transition after:content-[\"\"] dark:after:bg-neutral-950',\n ],\n 'active:border-neutral-400 active:after:bg-neutral-400 dark:active:border-neutral-600 active:dark:after:bg-neutral-600',\n minWidth && `min-w-[${minWidth}px]`,\n maxWidth && `max-w-[${maxWidth}px]`\n )}\n style={{\n width: `${width}px`,\n }}\n ref={containerRef}\n onMouseDown={startResizing}\n onTouchStart={startResizing}\n onDoubleClick={handleDoubleClick}\n aria-valuemin={minWidth}\n aria-valuemax={maxWidth}\n aria-valuenow={width}\n aria-label=\"Resizable component\"\n role=\"slider\"\n tabIndex={0}\n >\n {/* biome-ignore lint/a11y/noStaticElementInteractions: This div stops event propagation to prevent content clicks from triggering resize */}\n <div\n role=\"presentation\"\n className=\"absolute top-0 left-0 size-full cursor-default overflow-hidden\"\n onMouseDown={(e) => e.stopPropagation()}\n onTouchStart={(e) => e.stopPropagation()}\n >\n {children}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1038,10 +1038,12 @@ const useFillAllTranslations = () => {
|
|
|
1038
1038
|
return useMutation({
|
|
1039
1039
|
mutationKey: ["fill-all-translations"],
|
|
1040
1040
|
mutationFn: async (args) => {
|
|
1041
|
-
|
|
1041
|
+
let dictionaryIds = args.dictionaryIds;
|
|
1042
|
+
if (!dictionaryIds || dictionaryIds.length === 0) dictionaryIds = ((await intlayerOAuth.dictionary.getDictionaries({ pageSize: 1e3 }))?.data ?? []).map((d) => d.id);
|
|
1042
1043
|
return intlayerOAuth.translate.translateDictionaries({
|
|
1043
1044
|
dictionaryIds,
|
|
1044
|
-
targetLocales: args.targetLocales
|
|
1045
|
+
targetLocales: args.targetLocales,
|
|
1046
|
+
mode: args.mode
|
|
1045
1047
|
});
|
|
1046
1048
|
}
|
|
1047
1049
|
});
|