@nice2dev/ui-graphics 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +142 -0
- package/dist/cjs/animation/AnimatedPerson.js +3 -1
- package/dist/cjs/animation/Audience.js +3 -1
- package/dist/cjs/animation/rig/RigPlayer.js +1 -1
- package/dist/cjs/animation/rig/presets.js +67 -55
- package/dist/cjs/animation/shapes/headwear.js +4 -1
- package/dist/cjs/core/NiceBrushEngine.js +8 -5
- package/dist/cjs/core/collaboration.js +11 -6
- package/dist/cjs/core/editorAsk.js +48 -0
- package/dist/cjs/core/i18n.js +7 -3
- package/dist/cjs/core/minimap.js +21 -20
- package/dist/cjs/core/plugins.js +1 -1
- package/dist/cjs/core/rtl.js +1 -1
- package/dist/cjs/core/theme.js +1 -1
- package/dist/cjs/font/NiceFontEditor.js +14 -12
- package/dist/cjs/game/GameAsset2dEditor.js +75 -44
- package/dist/cjs/nice2dev-ui-graphics.css +1 -1
- package/dist/cjs/photo/FilterThumb.js +3 -3
- package/dist/cjs/photo/NiceAITools.js +6 -4
- package/dist/cjs/photo/PhotoEditor.js +3 -1
- package/dist/cjs/photo/PhotoEditorCanvas.js +8 -6
- package/dist/cjs/photo/PhotoEditorLeftPanel.js +29 -29
- package/dist/cjs/photo/PhotoEditorToolbar.js +4 -4
- package/dist/cjs/photo/photoFilters.js +1 -0
- package/dist/cjs/pixel/PixelEditor.js +6 -3
- package/dist/cjs/pixel/PixelEditorCanvas.js +10 -6
- package/dist/cjs/pixel/PixelEditorMenuBar.js +95 -30
- package/dist/cjs/pixel/PixelEditorRightPanel.js +32 -24
- package/dist/cjs/pixel/PixelEditorStatusBar.js +12 -4
- package/dist/cjs/pixel/PixelEditorTimeline.js +15 -2
- package/dist/cjs/pixel/PixelEditorToolbar.js +12 -6
- package/dist/cjs/pixel/SpriteGeneratorPanel.js +28 -17
- package/dist/cjs/pixel/animalGeneratorPresets.js +31 -31
- package/dist/cjs/pixel/buildingGeneratorPresets.js +24 -24
- package/dist/cjs/pixel/characterGeneratorPresets.js +25 -25
- package/dist/cjs/pixel/usePixelEditor.js +13 -10
- package/dist/cjs/texture/Nice3DTexturePainter.js +16 -10
- package/dist/cjs/ui/NiceUIDesigner.js +17 -13
- package/dist/cjs/vector/NiceSvgToCode.js +7 -5
- package/dist/cjs/vector/VectorEditor.js +3 -2
- package/dist/cjs/vector/VectorEditorMenuBar.js +7 -1
- package/dist/cjs/vector/VectorEditorRightPanel.js +1 -1
- package/dist/cjs/vector/VectorEditorShapeRenderer.js +3 -1
- package/dist/cjs/vector/useVectorEditor.js +38 -29
- package/dist/cjs/vector/vectorEditorImport.js +1 -0
- package/dist/cjs/vector/vectorIconImport.js +1 -0
- package/dist/esm/animation/AnimatedPerson.js +3 -1
- package/dist/esm/animation/Audience.js +3 -1
- package/dist/esm/animation/rig/RigPlayer.js +1 -1
- package/dist/esm/animation/rig/presets.js +68 -56
- package/dist/esm/animation/shapes/headwear.js +4 -1
- package/dist/esm/animation/spineExport.js +1 -1
- package/dist/esm/core/NiceBrushEngine.js +8 -5
- package/dist/esm/core/collaboration.js +11 -6
- package/dist/esm/core/editorAsk.js +45 -0
- package/dist/esm/core/i18n.js +8 -4
- package/dist/esm/core/minimap.js +21 -20
- package/dist/esm/core/plugins.js +1 -1
- package/dist/esm/core/rtl.js +1 -1
- package/dist/esm/core/theme.js +1 -1
- package/dist/esm/font/NiceFontEditor.js +14 -12
- package/dist/esm/game/GameAsset2dEditor.js +75 -44
- package/dist/esm/nice2dev-ui-graphics.css +1 -1
- package/dist/esm/photo/FilterThumb.js +3 -3
- package/dist/esm/photo/NiceAITools.js +6 -4
- package/dist/esm/photo/PhotoEditor.js +3 -1
- package/dist/esm/photo/PhotoEditorCanvas.js +8 -6
- package/dist/esm/photo/PhotoEditorLeftPanel.js +29 -29
- package/dist/esm/photo/PhotoEditorToolbar.js +4 -4
- package/dist/esm/photo/photoFilters.js +1 -0
- package/dist/esm/pixel/PixelEditor.js +6 -3
- package/dist/esm/pixel/PixelEditorCanvas.js +10 -6
- package/dist/esm/pixel/PixelEditorMenuBar.js +95 -30
- package/dist/esm/pixel/PixelEditorRightPanel.js +32 -24
- package/dist/esm/pixel/PixelEditorStatusBar.js +12 -4
- package/dist/esm/pixel/PixelEditorTimeline.js +15 -2
- package/dist/esm/pixel/PixelEditorToolbar.js +12 -6
- package/dist/esm/pixel/SpriteGeneratorPanel.js +28 -17
- package/dist/esm/pixel/animalGeneratorPresets.js +32 -32
- package/dist/esm/pixel/buildingGeneratorPresets.js +25 -25
- package/dist/esm/pixel/characterGeneratorPresets.js +26 -26
- package/dist/esm/pixel/usePixelEditor.js +13 -10
- package/dist/esm/texture/Nice3DTexturePainter.js +16 -10
- package/dist/esm/ui/NiceUIDesigner.js +17 -13
- package/dist/esm/vector/NiceSvgToCode.js +7 -5
- package/dist/esm/vector/VectorEditor.js +3 -2
- package/dist/esm/vector/VectorEditorMenuBar.js +7 -1
- package/dist/esm/vector/VectorEditorRightPanel.js +1 -1
- package/dist/esm/vector/VectorEditorShapeRenderer.js +3 -1
- package/dist/esm/vector/useVectorEditor.js +38 -29
- package/dist/esm/vector/vectorEditorImport.js +1 -0
- package/dist/esm/vector/vectorIconImport.js +1 -0
- package/dist/types/core/LocalUI.d.ts +1 -2
- package/dist/types/core/editorAsk.d.ts +26 -0
- package/dist/types/core/i18n.d.ts +1 -17
- package/dist/types/pixel/usePixelEditor.d.ts +3 -0
- package/dist/types/vector/useVectorEditor.d.ts +2 -0
- package/package.json +5 -5
|
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
4
4
|
var ui = require('@nice2dev/ui');
|
|
5
5
|
var uiCore = require('@nice2dev/ui-core');
|
|
6
6
|
var React = require('react');
|
|
7
|
+
var i18n = require('../core/i18n.js');
|
|
7
8
|
|
|
8
9
|
/** Built-in guided tour (EN inline + i18n keys → PL and other locales). */
|
|
9
10
|
const UI_DESIGNER_TOUR = [
|
|
@@ -312,6 +313,7 @@ function updateInTree(nodes, id, updates) {
|
|
|
312
313
|
/** Visual drag & drop UI builder that generates React code. */
|
|
313
314
|
const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
|
|
314
315
|
var _a, _b, _c, _d, _e;
|
|
316
|
+
const { t } = i18n.useNiceTranslation();
|
|
315
317
|
const designer = useUIDesigner();
|
|
316
318
|
const [breakpoint, setBreakpoint] = React.useState('desktop');
|
|
317
319
|
const [dragType, setDragType] = React.useState(null);
|
|
@@ -395,7 +397,7 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
|
|
|
395
397
|
color: 'var(--nice-text-secondary, #868e96)',
|
|
396
398
|
fontSize: 11,
|
|
397
399
|
gap: 4,
|
|
398
|
-
}, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "image", size: 14 }), " Image
|
|
400
|
+
}, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "image", size: 14 }), " ", t('uiDesigner.image', 'Image')] })) : node.type === 'List' ? (jsxRuntime.jsx("ul", { style: { margin: 0, ...node.style }, children: ((_d = node.props.items) !== null && _d !== void 0 ? _d : []).map((item, i) => (jsxRuntime.jsx("li", { children: item }, i))) })) : node.type === 'Divider' ? (jsxRuntime.jsx("hr", { style: {
|
|
399
401
|
margin: 0,
|
|
400
402
|
border: 'none',
|
|
401
403
|
borderTop: '1px solid var(--nice-border, #dee2e6)',
|
|
@@ -421,7 +423,9 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
|
|
|
421
423
|
display: 'flex',
|
|
422
424
|
alignItems: 'center',
|
|
423
425
|
gap: 4,
|
|
424
|
-
background: designer.selectedId === node.id
|
|
426
|
+
background: designer.selectedId === node.id
|
|
427
|
+
? 'color-mix(in srgb, var(--nice-color-primary, #339af0) 20%, transparent)'
|
|
428
|
+
: 'transparent',
|
|
425
429
|
borderRadius: 2,
|
|
426
430
|
}, children: [(def === null || def === void 0 ? void 0 : def.icon) && jsxRuntime.jsx(uiCore.NiceIcon, { name: def.icon, size: 12 }), jsxRuntime.jsxs("span", { style: { flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: [node.type, node.props.text ? `: ${String(node.props.text).slice(0, 15)}` : ''] })] }), node.children.map((c) => renderTreeItem(c, depth + 1))] }, node.id));
|
|
427
431
|
};
|
|
@@ -435,7 +439,7 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
|
|
|
435
439
|
height: '100%',
|
|
436
440
|
overflowY: 'auto',
|
|
437
441
|
boxSizing: 'border-box',
|
|
438
|
-
}, children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { style: { fontSize: 12, display: 'block', marginBottom: 4 }, children:
|
|
442
|
+
}, children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { style: { fontSize: 12, display: 'block', marginBottom: 4 }, children: t('uiDesigner.components', 'Components') }), jsxRuntime.jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 2 }, children: COMPONENT_DEFS.map((def) => (jsxRuntime.jsxs("button", { draggable: true, onDragStart: () => setDragType(def.type), onDragEnd: () => setDragType(null), onClick: () => { var _a; return designer.addNode(def.type, (_a = designer.selectedId) !== null && _a !== void 0 ? _a : undefined); }, style: {
|
|
439
443
|
display: 'flex',
|
|
440
444
|
alignItems: 'center',
|
|
441
445
|
gap: 4,
|
|
@@ -447,8 +451,8 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
|
|
|
447
451
|
cursor: 'grab',
|
|
448
452
|
textAlign: 'left',
|
|
449
453
|
fontSize: 11,
|
|
450
|
-
}, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: def.icon, size: 14 }), " ", def.label] }, def.type))) })] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { style: { fontSize: 12, display: 'block', marginBottom: 4 }, children:
|
|
451
|
-
const toolbarContent = (jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 4, alignItems: 'center', fontSize: 11, padding: '4px 8px' }, children: [jsxRuntime.jsx("button", { onClick: () => designer.undo(), disabled: !designer.canUndo, style: { ...btnStyle(false), opacity: designer.canUndo ? 1 : 0.4 }, title:
|
|
454
|
+
}, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: def.icon, size: 14 }), " ", def.label] }, def.type))) })] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { style: { fontSize: 12, display: 'block', marginBottom: 4 }, children: t('uiDesigner.tree', 'Tree') }), jsxRuntime.jsx("div", { style: { maxHeight: 200, overflowY: 'auto' }, children: designer.tree.length === 0 ? (jsxRuntime.jsx("div", { style: { color: 'var(--nice-text-secondary, #5c5f66)', padding: 4 }, children: t('uiDesigner.empty', 'Empty') })) : (designer.tree.map((n) => renderTreeItem(n))) })] })] }));
|
|
455
|
+
const toolbarContent = (jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 4, alignItems: 'center', fontSize: 11, padding: '4px 8px' }, children: [jsxRuntime.jsx("button", { onClick: () => designer.undo(), disabled: !designer.canUndo, style: { ...btnStyle(false), opacity: designer.canUndo ? 1 : 0.4 }, title: t('uiDesigner.undoCtrlZ', 'Undo (Ctrl+Z)'), "aria-label": t('uiDesigner.undo', 'Undo'), children: jsxRuntime.jsx(uiCore.NiceIcon, { name: "reply", size: 14 }) }), jsxRuntime.jsx("button", { onClick: () => designer.redo(), disabled: !designer.canRedo, style: { ...btnStyle(false), opacity: designer.canRedo ? 1 : 0.4 }, title: t('uiDesigner.redoCtrlY', 'Redo (Ctrl+Y)'), "aria-label": t('uiDesigner.redo', 'Redo'), children: jsxRuntime.jsx(uiCore.NiceIcon, { name: "forward", size: 14 }) }), jsxRuntime.jsx("div", { style: { width: 1, height: 16, background: 'var(--nice-border, #373a40)' } }), jsxRuntime.jsxs("button", { onClick: () => setBreakpoint('desktop'), style: btnStyle(breakpoint === 'desktop'), children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "monitor", size: 14 }), " ", t('uiDesigner.desktop', 'Desktop')] }), jsxRuntime.jsxs("button", { onClick: () => setBreakpoint('tablet'), style: btnStyle(breakpoint === 'tablet'), children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "window", size: 14 }), " ", t('uiDesigner.tablet', 'Tablet')] }), jsxRuntime.jsxs("button", { onClick: () => setBreakpoint('mobile'), style: btnStyle(breakpoint === 'mobile'), children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "phone", size: 14 }), " ", t('uiDesigner.mobile', 'Mobile')] }), jsxRuntime.jsx("div", { style: { flex: 1 } }), designer.selectedId && (jsxRuntime.jsxs("button", { onClick: () => designer.removeNode(designer.selectedId), style: { ...btnStyle(false), color: 'var(--nice-danger, #ff6b6b)' }, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "trash", size: 14 }), " ", t('uiDesigner.delete', 'Delete')] })), jsxRuntime.jsxs("button", { onClick: () => setShowCode((v) => !v), style: btnStyle(showCode), children: ['</>', " ", t('uiDesigner.code', 'Code')] }), jsxRuntime.jsx("button", { onClick: () => onExport === null || onExport === void 0 ? void 0 : onExport(designer.exportCode()), style: {
|
|
452
456
|
padding: '4px 8px',
|
|
453
457
|
background: 'var(--nice-primary, #228be6)',
|
|
454
458
|
color: 'var(--nice-text-on-primary, #fff)',
|
|
@@ -456,7 +460,7 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
|
|
|
456
460
|
borderRadius: 3,
|
|
457
461
|
cursor: 'pointer',
|
|
458
462
|
fontSize: 11,
|
|
459
|
-
}, children:
|
|
463
|
+
}, children: t('uiDesigner.export', 'Export') })] }));
|
|
460
464
|
const mainContent = (jsxRuntime.jsx("div", { style: {
|
|
461
465
|
flex: 1,
|
|
462
466
|
display: 'flex',
|
|
@@ -472,7 +476,7 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
|
|
|
472
476
|
margin: 0,
|
|
473
477
|
overflow: 'auto',
|
|
474
478
|
fontSize: 11,
|
|
475
|
-
color: '#a6e3a1',
|
|
479
|
+
color: 'var(--nice-color-success, #a6e3a1)',
|
|
476
480
|
fontFamily: 'monospace',
|
|
477
481
|
}, children: designer.exportCode() })) : (jsxRuntime.jsx("div", { style: {
|
|
478
482
|
flex: 1,
|
|
@@ -489,7 +493,7 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
|
|
|
489
493
|
textAlign: 'center',
|
|
490
494
|
padding: 40,
|
|
491
495
|
color: 'var(--nice-text-secondary, #868e96)',
|
|
492
|
-
}, children:
|
|
496
|
+
}, children: t('uiDesigner.dragOrClickAComponent', 'Drag or click a component to add it') })) : (designer.tree.map((n) => renderNode(n))) })) }));
|
|
493
497
|
const rightPanel = (jsxRuntime.jsxs("div", { style: {
|
|
494
498
|
width: 200,
|
|
495
499
|
fontSize: 11,
|
|
@@ -500,7 +504,7 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
|
|
|
500
504
|
overflowY: 'auto',
|
|
501
505
|
padding: 8,
|
|
502
506
|
boxSizing: 'border-box',
|
|
503
|
-
}, children: [jsxRuntime.jsx("strong", { style: { fontSize: 12 }, children:
|
|
507
|
+
}, children: [jsxRuntime.jsx("strong", { style: { fontSize: 12 }, children: t('uiDesigner.properties', 'Properties') }), selectedNode ? (jsxRuntime.jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [jsxRuntime.jsxs("div", { style: {
|
|
504
508
|
color: 'var(--nice-text-secondary, #868e96)',
|
|
505
509
|
display: 'flex',
|
|
506
510
|
alignItems: 'center',
|
|
@@ -509,13 +513,13 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
|
|
|
509
513
|
var _a;
|
|
510
514
|
const di = (_a = COMPONENT_DEFS.find((d) => d.type === selectedNode.type)) === null || _a === void 0 ? void 0 : _a.icon;
|
|
511
515
|
return di ? jsxRuntime.jsx(uiCore.NiceIcon, { name: di, size: 14 }) : null;
|
|
512
|
-
})(), ' ', selectedNode.type] }), selectedNode.props.text !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children:
|
|
516
|
+
})(), ' ', selectedNode.type] }), selectedNode.props.text !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: t('uiDesigner.text', 'Text') }), jsxRuntime.jsx("input", { value: String(selectedNode.props.text), onChange: (e) => designer.updateNode(selectedNode.id, { props: { text: e.target.value } }), style: fieldStyle })] })), selectedNode.props.placeholder !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: t('uiDesigner.placeholder', 'Placeholder') }), jsxRuntime.jsx("input", { value: String(selectedNode.props.placeholder), onChange: (e) => designer.updateNode(selectedNode.id, { props: { placeholder: e.target.value } }), style: fieldStyle })] })), selectedNode.props.level !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: t('uiDesigner.headingLevel', 'Heading Level') }), jsxRuntime.jsx("select", { value: Number(selectedNode.props.level), onChange: (e) => designer.updateNode(selectedNode.id, {
|
|
513
517
|
props: { level: Number(e.target.value) },
|
|
514
|
-
}), style: fieldStyle, children: [1, 2, 3, 4, 5, 6].map((v) => (jsxRuntime.jsxs("option", { value: v, children: ["H", v] }, v))) })] })), selectedNode.props.variant !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children:
|
|
518
|
+
}), style: fieldStyle, children: [1, 2, 3, 4, 5, 6].map((v) => (jsxRuntime.jsxs("option", { value: v, children: ["H", v] }, v))) })] })), selectedNode.props.variant !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: t('uiDesigner.variant', 'Variant') }), jsxRuntime.jsx("select", { value: String(selectedNode.props.variant), onChange: (e) => designer.updateNode(selectedNode.id, { props: { variant: e.target.value } }), style: fieldStyle, children: ['primary', 'secondary', 'outline', 'ghost'].map((v) => (jsxRuntime.jsx("option", { value: v, children: v }, v))) })] })), selectedNode.props.src !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: t('uiDesigner.imageUrl', 'Image URL') }), jsxRuntime.jsx("input", { value: String(selectedNode.props.src), onChange: (e) => designer.updateNode(selectedNode.id, { props: { src: e.target.value } }), style: fieldStyle })] })), selectedNode.props.alt !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: t('uiDesigner.altText', 'Alt Text') }), jsxRuntime.jsx("input", { value: String(selectedNode.props.alt), onChange: (e) => designer.updateNode(selectedNode.id, { props: { alt: e.target.value } }), style: fieldStyle })] })), jsxRuntime.jsx("hr", { style: {
|
|
515
519
|
border: 'none',
|
|
516
520
|
borderTop: '1px solid var(--nice-border, #373a40)',
|
|
517
521
|
margin: '4px 0',
|
|
518
|
-
} }), jsxRuntime.jsx("strong", { style: { color: 'var(--nice-text-secondary, #868e96)' }, children:
|
|
522
|
+
} }), jsxRuntime.jsx("strong", { style: { color: 'var(--nice-text-secondary, #868e96)' }, children: t('uiDesigner.style', 'Style') }), ['padding', 'margin', 'gap', 'borderRadius'].map((prop) => (jsxRuntime.jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4 }, children: [jsxRuntime.jsx("label", { style: { minWidth: 60 }, children: prop }), jsxRuntime.jsx("input", { type: "number", value: typeof selectedNode.style[prop] === 'number'
|
|
519
523
|
? selectedNode.style[prop]
|
|
520
524
|
: 0, onChange: (e) => designer.updateNode(selectedNode.id, {
|
|
521
525
|
style: { [prop]: Number(e.target.value) },
|
|
@@ -545,7 +549,7 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
|
|
|
545
549
|
color: 'var(--nice-text-secondary, #5c5f66)',
|
|
546
550
|
padding: 8,
|
|
547
551
|
textAlign: 'center',
|
|
548
|
-
}, children:
|
|
552
|
+
}, children: t('uiDesigner.selectAnElement', 'Select an element') }))] }));
|
|
549
553
|
return (jsxRuntime.jsxs("div", { className: `nice-ui-designer ${className}`, style: { position: 'relative', height: '100%' }, children: [jsxRuntime.jsx("span", { style: { position: 'absolute', top: 6, right: 6, zIndex: 20 }, children: jsxRuntime.jsx(ui.NiceTutorialButton, { steps: ui.resolveTutorial(tutorial, UI_DESIGNER_TOUR) }) }), jsxRuntime.jsx(ui.NiceAppShell, { flush: true, toolbar: toolbarContent, sidebar: leftPanel, rightSidebar: rightPanel, children: mainContent })] }));
|
|
550
554
|
};
|
|
551
555
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
4
|
var uiCore = require('@nice2dev/ui-core');
|
|
5
5
|
var React = require('react');
|
|
6
|
+
var i18n = require('../core/i18n.js');
|
|
6
7
|
|
|
7
8
|
/* ── Default config ── */
|
|
8
9
|
const DEFAULT_CONFIG = {
|
|
@@ -419,6 +420,7 @@ function useSvgToCode() {
|
|
|
419
420
|
}
|
|
420
421
|
/* ── Component ── */
|
|
421
422
|
const NiceSvgToCode = ({ initialSvg = '', onConvert, className = '', }) => {
|
|
423
|
+
const { t } = i18n.useNiceTranslation();
|
|
422
424
|
const { svgInput, setSvgInput, config, updateConfig, convert, result } = useSvgToCode();
|
|
423
425
|
// Initialize with provided SVG
|
|
424
426
|
React.useEffect(() => {
|
|
@@ -437,21 +439,21 @@ const NiceSvgToCode = ({ initialSvg = '', onConvert, className = '', }) => {
|
|
|
437
439
|
navigator.clipboard.writeText(result.code);
|
|
438
440
|
}
|
|
439
441
|
};
|
|
440
|
-
return (jsxRuntime.jsxs("div", { className: `nice-svg-to-code ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsxRuntime.jsxs("strong", { children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "refresh", size: 14 }), " SVG
|
|
442
|
+
return (jsxRuntime.jsxs("div", { className: `nice-svg-to-code ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsxRuntime.jsxs("strong", { children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "refresh", size: 14 }), " ", t('svgToCode.svgCode', 'SVG → Code')] }), jsxRuntime.jsx("textarea", { value: svgInput, onChange: (e) => setSvgInput(e.target.value), placeholder: t('svgToCode.pasteSvgMarkupHere', 'Paste SVG markup here…'), rows: 6, style: {
|
|
441
443
|
width: '100%',
|
|
442
444
|
fontFamily: 'monospace',
|
|
443
445
|
fontSize: 11,
|
|
444
446
|
padding: 6,
|
|
445
447
|
borderRadius: 4,
|
|
446
448
|
border: '1px solid var(--nice-border, #e2e8f0)',
|
|
447
|
-
} }), jsxRuntime.jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxRuntime.jsxs("label", { children: [
|
|
449
|
+
} }), jsxRuntime.jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxRuntime.jsxs("label", { children: [t('svgToCode.framework', 'Framework:'), jsxRuntime.jsxs("select", { value: config.framework, onChange: (e) => updateConfig({ framework: e.target.value }), style: { width: '100%' }, children: [jsxRuntime.jsx("option", { value: "react", children: "React (JSX/TSX)" }), jsxRuntime.jsx("option", { value: "vue", children: "Vue (SFC)" }), jsxRuntime.jsx("option", { value: "web-component", children: t('svgToCode.webComponent', 'Web Component') }), jsxRuntime.jsx("option", { value: "typescript", children: t('svgToCode.typescriptUtility', 'TypeScript (utility)') })] })] }), jsxRuntime.jsxs("label", { children: [t('svgToCode.componentName', 'Component name:'), jsxRuntime.jsx("input", { type: "text", value: config.componentName, onChange: (e) => updateConfig({ componentName: e.target.value.replace(/[^a-zA-Z0-9]/g, '') }), style: { width: '100%', padding: '3px 6px' } })] }), jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 8, flexWrap: 'wrap', fontSize: 11 }, children: [jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.typescript, onChange: (e) => updateConfig({ typescript: e.target.checked }) }), ' ', t('svgToCode.typescript', 'TypeScript')] }), jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.optimize, onChange: (e) => updateConfig({ optimize: e.target.checked }) }), ' ', t('svgToCode.optimize', 'Optimize')] }), jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.sizeProps, onChange: (e) => updateConfig({ sizeProps: e.target.checked }) }), ' ', t('svgToCode.sizeProps', 'Size props')] }), jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.useCurrentColor, onChange: (e) => updateConfig({ useCurrentColor: e.target.checked }) }), ' ', "currentColor"] }), jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.memo, onChange: (e) => updateConfig({ memo: e.target.checked }) }), ' ', "memo"] })] })] }), jsxRuntime.jsxs("button", { type: "button", onClick: handleConvert, disabled: !svgInput.trim(), style: {
|
|
448
450
|
background: 'var(--nice-color-accent, #6366f1)',
|
|
449
451
|
color: 'var(--nice-text-on-primary, #fff)',
|
|
450
452
|
border: 'none',
|
|
451
453
|
borderRadius: 4,
|
|
452
454
|
padding: '8px 12px',
|
|
453
455
|
cursor: 'pointer',
|
|
454
|
-
}, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "refresh", size: 14 }),
|
|
456
|
+
}, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "refresh", size: 14 }), ' ', t('svgToCode.convertTo', 'Convert to {framework}').replace('{framework}', config.framework.charAt(0).toUpperCase() + config.framework.slice(1))] }), result && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("div", { style: {
|
|
455
457
|
display: 'flex',
|
|
456
458
|
justifyContent: 'space-between',
|
|
457
459
|
alignItems: 'center',
|
|
@@ -463,7 +465,7 @@ const NiceSvgToCode = ({ initialSvg = '', onConvert, className = '', }) => {
|
|
|
463
465
|
borderRadius: 3,
|
|
464
466
|
border: '1px solid var(--nice-border, #e2e8f0)',
|
|
465
467
|
background: 'transparent',
|
|
466
|
-
}, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "clipboard", size: 12 }), " Copy
|
|
468
|
+
}, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "clipboard", size: 12 }), " ", t('svgToCode.copy', 'Copy')] })] }), jsxRuntime.jsx("pre", { style: {
|
|
467
469
|
background: 'var(--nice-code-bg, #1e293b)',
|
|
468
470
|
color: 'var(--nice-code-color, #e2e8f0)',
|
|
469
471
|
padding: 8,
|
|
@@ -472,7 +474,7 @@ const NiceSvgToCode = ({ initialSvg = '', onConvert, className = '', }) => {
|
|
|
472
474
|
overflow: 'auto',
|
|
473
475
|
maxHeight: 300,
|
|
474
476
|
whiteSpace: 'pre-wrap',
|
|
475
|
-
}, children: result.code }), result.extractedColors.length > 0 && (jsxRuntime.jsxs("div", { style: { fontSize: 11, marginTop: 4 }, children: [
|
|
477
|
+
}, children: result.code }), result.extractedColors.length > 0 && (jsxRuntime.jsxs("div", { style: { fontSize: 11, marginTop: 4 }, children: [t('svgToCode.colors', 'Colors:'), ' ', result.extractedColors.map((c) => (jsxRuntime.jsx("span", { style: {
|
|
476
478
|
display: 'inline-block',
|
|
477
479
|
width: 12,
|
|
478
480
|
height: 12,
|
|
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
4
4
|
var ui = require('@nice2dev/ui');
|
|
5
5
|
var uiCore = require('@nice2dev/ui-core');
|
|
6
6
|
var React = require('react');
|
|
7
|
+
var editorAsk = require('../core/editorAsk.js');
|
|
7
8
|
var i18n = require('../core/i18n.js');
|
|
8
9
|
var useVectorEditor = require('./useVectorEditor.js');
|
|
9
10
|
var vectorAnimation = require('./vectorAnimation.js');
|
|
@@ -65,7 +66,7 @@ const NiceVectorEditor = ({ width: artW = 512, height: artH = 512, className, on
|
|
|
65
66
|
}, [initialIcon, artW, artH, api]);
|
|
66
67
|
const { shapes, selId, tool, zoom, px, py, dlgSvg, svgText, gridSize, boxRef, fInp, animations, previewTime, isPlaying, onDown, onMove, onUp, onWheel, onDblClick, openFile, switchTool, importSVGString, setDlgSvg, setSvgText, } = api;
|
|
67
68
|
const toolRail = (jsxRuntime.jsx("div", { className: VectorEditor_module.toolbar, children: vectorEditorTypes.TOOLS.map((t) => (jsxRuntime.jsx(ui.NiceButton, { className: `${VectorEditor_module.toolBtn} ${tool === t.id ? VectorEditor_module.toolBtnActive : ''}`, onClick: () => switchTool(t.id), "aria-label": `${t.label} (${t.key})`, variant: tool === t.id ? 'primary' : 'ghost', size: "sm", children: jsxRuntime.jsx(uiCore.NiceIcon, { name: t.icon, size: "sm" }) }, t.id))) }));
|
|
68
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("input", { ref: fInp, type: "file", accept: ".svg", style: { display: 'none' }, onChange: (e) => {
|
|
69
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(editorAsk.EditorAskDialog, { ask: api.pendingAsk }), jsxRuntime.jsx("input", { ref: fInp, type: "file", accept: ".svg", style: { display: 'none' }, onChange: (e) => {
|
|
69
70
|
var _a;
|
|
70
71
|
const f = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
71
72
|
if (f) {
|
|
@@ -111,7 +112,7 @@ const NiceVectorEditor = ({ width: artW = 512, height: artH = 512, className, on
|
|
|
111
112
|
}
|
|
112
113
|
const node = (jsxRuntime.jsx(VectorEditorShapeRenderer.ShapeRenderer, { shape: s, selected: s.id === selId, zoom: zoom }, s.id));
|
|
113
114
|
return previewTr ? (jsxRuntime.jsx("g", { transform: previewTr, children: node }, s.id)) : (node);
|
|
114
|
-
})] }) }) }), jsxRuntime.jsxs(ui.NiceModal, { open: dlgSvg, onClose: () => setDlgSvg(false), title:
|
|
115
|
+
})] }) }) }), jsxRuntime.jsxs(ui.NiceModal, { open: dlgSvg, onClose: () => setDlgSvg(false), title: t('vectorEditor.pasteSvg', 'Paste SVG'), children: [jsxRuntime.jsx("label", { children: t('vectorEditor.pasteSvgSourceCodeBelow', 'Paste SVG source code below:') }), jsxRuntime.jsx(ui.NiceTextArea, { value: svgText, onChange: (val) => setSvgText(val), placeholder: t('vectorEditor.svgXmlnsHttpWwwW3', '<svg xmlns="http://www.w3.org/2000/svg" ...>') }), jsxRuntime.jsxs("div", { className: VectorEditor_module.dialogActions, children: [jsxRuntime.jsx(ui.NiceButton, { variant: "ghost", onClick: () => setDlgSvg(false), children: t('vectorEditor.cancel', 'Cancel') }), jsxRuntime.jsx(ui.NiceButton, { variant: "primary", onClick: () => importSVGString(svgText), children: t('vectorEditor.import', 'Import') })] })] }), jsxRuntime.jsxs(ui.NiceModal, { open: snippetOpen, onClose: () => setSnippetOpen(false), title: t('vectorEditor.snippet.title', 'Icon snippet copied to clipboard'), children: [jsxRuntime.jsxs("p", { style: { marginTop: 0 }, children: [t('vectorEditor.snippet.pasteHint', 'Paste into'), ' ', jsxRuntime.jsxs("code", { children: ["packages/icons/src/icons/", iconCategory ? `${iconCategory.toLowerCase()}` : '<category>', ".tsx"] }), ":"] }), jsxRuntime.jsx(ui.NiceTextArea, { value: snippetText, onChange: (val) => setSnippetText(val), rows: 16 }), jsxRuntime.jsxs("div", { className: VectorEditor_module.dialogActions, children: [jsxRuntime.jsx(ui.NiceButton, { variant: "primary", onClick: () => {
|
|
115
116
|
if (typeof navigator !== 'undefined' && navigator.clipboard) {
|
|
116
117
|
navigator.clipboard.writeText(snippetText).catch(() => { });
|
|
117
118
|
}
|
|
@@ -264,7 +264,13 @@ const VectorEditorMenuBar = ({ api, hasOnSaveToLibrary, onExportIconSnippet }) =
|
|
|
264
264
|
{ value: '', label: t('vectorEditor.menu.secondShape', '2nd Shape') },
|
|
265
265
|
...shapes
|
|
266
266
|
.filter((s) => s.id !== selId)
|
|
267
|
-
.map((s, i) => {
|
|
267
|
+
.map((s, i) => {
|
|
268
|
+
var _a;
|
|
269
|
+
return ({
|
|
270
|
+
value: s.id,
|
|
271
|
+
label: (_a = s.name) !== null && _a !== void 0 ? _a : t('vectorEditor.shape', 'Shape {i}').replace('{i}', String(i + 1)),
|
|
272
|
+
});
|
|
273
|
+
}),
|
|
268
274
|
], style: { minWidth: 100 } })), jsxRuntime.jsxs("label", { className: VectorEditor_module.menuLabel, children: [jsxRuntime.jsx(ui.NiceCheckbox, { checked: snapGrid, onChange: () => setSnapGrid((v) => !v) }), t('vectorEditor.menu.snap', 'Snap')] }), onExportIconSnippet && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: VectorEditor_module.menuSep }), jsxRuntime.jsxs(ui.NiceButton, { className: VectorEditor_module.menuBtn, variant: "primary", size: "sm", onClick: onExportIconSnippet, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "clipboard", size: 14 }), ' ', t('vectorEditor.menu.exportIcon', 'Export Icon')] })] }))] }));
|
|
269
275
|
};
|
|
270
276
|
var VectorEditorMenuBar$1 = React.memo(VectorEditorMenuBar);
|
|
@@ -46,7 +46,7 @@ const VectorEditorRightPanel = ({ api, library, artW = 512, artH = 512 }) => {
|
|
|
46
46
|
border: (selectedGradient === null || selectedGradient === void 0 ? void 0 : selectedGradient.id) === grad.id
|
|
47
47
|
? '2px solid var(--nice-bg, #FFF)'
|
|
48
48
|
: '1px solid var(--nice-border-color, #444)',
|
|
49
|
-
}, onClick: () => applyGradient(grad), title:
|
|
49
|
+
}, onClick: () => applyGradient(grad), title: t('vectorEditor.gradient', '{type} gradient').replace('{type}', String(grad.type)) }, i))) }), jsxRuntime.jsx("div", { className: VectorEditor_module.propRow, children: jsxRuntime.jsxs(ui.NiceButton, { className: VectorEditor_module.menuBtn, variant: "ghost", size: "sm", style: { width: '100%', fontSize: 10 }, onClick: () => setShowGradientEditor(!showGradientEditor), children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: showGradientEditor ? 'chevron-down' : 'chevron-right', size: 14 }), ' ', showGradientEditor
|
|
50
50
|
? t('vectorEditor.properties.hideEditor', 'Hide Editor')
|
|
51
51
|
: t('vectorEditor.properties.editGradient', 'Edit Gradient')] }) }), showGradientEditor && selectedGradient && (jsxRuntime.jsxs("div", { style: {
|
|
52
52
|
padding: 4,
|
|
@@ -8,7 +8,9 @@ const ShapeRenderer = ({ shape: s, selected, zoom }) => {
|
|
|
8
8
|
if (s.hidden) {
|
|
9
9
|
return null;
|
|
10
10
|
}
|
|
11
|
-
const selStroke = selected
|
|
11
|
+
const selStroke = selected
|
|
12
|
+
? 'color-mix(in srgb, var(--nice-color-primary, #5566cc) 60%, transparent)'
|
|
13
|
+
: undefined;
|
|
12
14
|
const gProps = {};
|
|
13
15
|
if (s.opacity < 1) {
|
|
14
16
|
gProps.opacity = s.opacity;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var React = require('react');
|
|
4
|
+
var editorAsk = require('../core/editorAsk.js');
|
|
4
5
|
var vectorBooleanOps = require('./vectorBooleanOps.js');
|
|
5
6
|
var vectorEditorExport = require('./vectorEditorExport.js');
|
|
6
7
|
var vectorEditorImport = require('./vectorEditorImport.js');
|
|
@@ -12,10 +13,13 @@ var vectorEditorTypes = require('./vectorEditorTypes.js');
|
|
|
12
13
|
* so the component file is pure layout.
|
|
13
14
|
*/
|
|
14
15
|
function useVectorEditor({ artW, artH, onSaveToLibrary }) {
|
|
16
|
+
// the library's dialogs for the text tool and the library name (not window.prompt)
|
|
17
|
+
const { pendingAsk, ask } = editorAsk.useEditorAsk();
|
|
15
18
|
/* ── state ── */
|
|
16
19
|
const [shapes, setShapes] = React.useState([]);
|
|
17
20
|
const [selId, setSelId] = React.useState(null);
|
|
18
21
|
const [tool, setTool] = React.useState('pointer');
|
|
22
|
+
// audit-allow: literal-color — the first fill of a new shape - document data
|
|
19
23
|
const [fillCol, setFillCol] = React.useState('#5566cc');
|
|
20
24
|
const [strokeCol, setStrokeCol] = React.useState('#000000');
|
|
21
25
|
const [strokeW, setStrokeW] = React.useState(2);
|
|
@@ -178,28 +182,30 @@ function useVectorEditor({ artW, artH, onSaveToLibrary }) {
|
|
|
178
182
|
return;
|
|
179
183
|
}
|
|
180
184
|
if (tool === 'text') {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
185
|
+
// the text comes from a dialog: the shape is added once it is entered
|
|
186
|
+
void ask('prompt', 'Enter text:', 'Hello').then((txt) => {
|
|
187
|
+
if (!txt) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const ns = {
|
|
191
|
+
id: vectorEditorTypes.sid(),
|
|
192
|
+
type: 'text',
|
|
193
|
+
x: p.x,
|
|
194
|
+
y: p.y,
|
|
195
|
+
w: txt.length * fontSize * 0.6,
|
|
196
|
+
h: fontSize,
|
|
197
|
+
...defaultBase,
|
|
198
|
+
stroke: 'none',
|
|
199
|
+
strokeWidth: 0,
|
|
200
|
+
text: txt,
|
|
201
|
+
fontSize,
|
|
202
|
+
fontFamily,
|
|
203
|
+
};
|
|
204
|
+
const next = [...shapes, ns];
|
|
205
|
+
snap(next);
|
|
206
|
+
setShapes(next);
|
|
207
|
+
setSelId(ns.id);
|
|
208
|
+
});
|
|
203
209
|
return;
|
|
204
210
|
}
|
|
205
211
|
if (tool === 'polygon') {
|
|
@@ -285,6 +291,7 @@ function useVectorEditor({ artW, artH, onSaveToLibrary }) {
|
|
|
285
291
|
starPoints2,
|
|
286
292
|
starInnerRatio,
|
|
287
293
|
cornerRadius,
|
|
294
|
+
ask,
|
|
288
295
|
]);
|
|
289
296
|
const onMove = React.useCallback((e) => {
|
|
290
297
|
if (panning.current) {
|
|
@@ -699,15 +706,15 @@ function useVectorEditor({ artW, artH, onSaveToLibrary }) {
|
|
|
699
706
|
const blob = new Blob([svg], { type: 'image/svg+xml' });
|
|
700
707
|
const reader = new FileReader();
|
|
701
708
|
reader.onload = () => {
|
|
702
|
-
const
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
709
|
+
const dataUrl = reader.result;
|
|
710
|
+
void ask('prompt', 'Save to library as:', 'drawing.svg').then((name) => {
|
|
711
|
+
if (name && name.trim()) {
|
|
712
|
+
onSaveToLibrary(dataUrl, name.trim(), 'image/svg+xml');
|
|
713
|
+
}
|
|
714
|
+
});
|
|
708
715
|
};
|
|
709
716
|
reader.readAsDataURL(blob);
|
|
710
|
-
}, [shapes, artW, artH, onSaveToLibrary]);
|
|
717
|
+
}, [shapes, artW, artH, onSaveToLibrary, ask]);
|
|
711
718
|
/* ── export shortcuts ── */
|
|
712
719
|
const exportSVG = React.useCallback(() => vectorEditorExport.exportSVGFile(shapes, artW, artH, animations), [shapes, artW, artH, animations]);
|
|
713
720
|
const exportPNG = React.useCallback(() => vectorEditorExport.exportPNGFile(shapes, artW, artH), [shapes, artW, artH]);
|
|
@@ -793,6 +800,8 @@ function useVectorEditor({ artW, artH, onSaveToLibrary }) {
|
|
|
793
800
|
/* ── return ── */
|
|
794
801
|
return {
|
|
795
802
|
// state
|
|
803
|
+
/** The question the editor waits on; render it with `EditorAskDialog`. */
|
|
804
|
+
pendingAsk,
|
|
796
805
|
shapes,
|
|
797
806
|
selId,
|
|
798
807
|
tool,
|
|
@@ -18,6 +18,7 @@ function parseSVGToShapes(raw) {
|
|
|
18
18
|
const elems = doc.querySelectorAll('rect, ellipse, circle, line, polyline, polygon, text, path');
|
|
19
19
|
elems.forEach((el) => {
|
|
20
20
|
const tag = el.tagName.toLowerCase();
|
|
21
|
+
// audit-allow: literal-color — an imported shape without a fill gets a visible default - document data
|
|
21
22
|
const fill = el.getAttribute('fill') || (tag === 'line' ? 'none' : '#cccccc');
|
|
22
23
|
const stroke = el.getAttribute('stroke') || 'none';
|
|
23
24
|
const sw = parseFloat(el.getAttribute('stroke-width') || '1');
|
|
@@ -39,6 +39,7 @@ function iconComponentToShapes(Icon, options) {
|
|
|
39
39
|
// Force a generous size and visible colors so the parser sees concrete strokes/fills.
|
|
40
40
|
const renderProps = {
|
|
41
41
|
size: 'lg',
|
|
42
|
+
// audit-allow: literal-color +2 — an imported icon's default colours - document data
|
|
42
43
|
primaryColor: '#5566cc',
|
|
43
44
|
secondaryColor: '#a29bfe',
|
|
44
45
|
strokeWidth: 2,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { motion } from 'framer-motion';
|
|
3
3
|
import { useMemo, useEffect } from 'react';
|
|
4
|
+
import { useNiceTranslation } from '../core/i18n.js';
|
|
4
5
|
import { usePersonControls, playPose } from './animationHelper.js';
|
|
5
6
|
import { DEFAULT_CHARACTER, pick } from './characterTypes.js';
|
|
6
7
|
|
|
@@ -9,6 +10,7 @@ const NiceAnimatedPerson = ({ id, name, size, score = 10, startPose = 'idle', on
|
|
|
9
10
|
// colors prop is destructured for API completeness but not yet fully wired into character defaults.
|
|
10
11
|
}) => {
|
|
11
12
|
var _a, _b, _c, _d, _e;
|
|
13
|
+
const { t } = useNiceTranslation();
|
|
12
14
|
const fx = usePersonControls();
|
|
13
15
|
const effectiveStartPose = pose !== null && pose !== void 0 ? pose : startPose;
|
|
14
16
|
const effectiveCharacter = character !== null && character !== void 0 ? character : config;
|
|
@@ -20,7 +22,7 @@ const NiceAnimatedPerson = ({ id, name, size, score = 10, startPose = 'idle', on
|
|
|
20
22
|
}, [fx, onReady, effectiveStartPose]);
|
|
21
23
|
const W = (_c = (_b = (_a = cfg.size) !== null && _a !== void 0 ? _a : effectiveSize) !== null && _b !== void 0 ? _b : DEFAULT_CHARACTER.size) !== null && _c !== void 0 ? _c : 180;
|
|
22
24
|
const skin = pick(cfg.face.colors, 0, 'var(--anim-skin, #FFD2B3)');
|
|
23
|
-
return (jsx(motion.div, { id: id, style: { width: W, height: W * 1.35, overflow: 'visible' }, animate: fx.root, children: jsxs("svg", { viewBox: "0 0 180 240", width: "100%", height: "100%", role: "img", "aria-label": (_e = (_d = cfg.name) !== null && _d !== void 0 ? _d : name) !== null && _e !== void 0 ? _e : 'Juror', children: [jsxs(motion.g, { animate: fx.body, children: [jsx("rect", { x: "68", y: "158", width: "14", height: "46", rx: "6", fill: pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg, #334155)') }), jsx("ellipse", { cx: "75", cy: "204", rx: "10", ry: "5", fill: pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg-shadow, #1E293B)') }), jsx("rect", { x: "98", y: "158", width: "14", height: "46", rx: "6", fill: pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg, #334155)') }), jsx("ellipse", { cx: "105", cy: "204", rx: "10", ry: "5", fill: pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg-shadow, #1E293B)') })] }), jsx(motion.g, { animate: fx.body, children: renderOutfit(cfg.outfit) }), jsx("rect", { x: "84", y: "82", width: "12", height: "12", rx: "4", fill: skin }), jsxs(motion.g, { animate: fx.leftArm, style: { transformOrigin: '64px 96px' }, children: [jsx("rect", { x: "36", y: "96", width: "16", height: "36", rx: "7", fill: skin }), jsx("circle", { cx: "44", cy: "136", r: "7", fill: skin })] }), jsxs(motion.g, { animate: fx.rightArm, style: { transformOrigin: '116px 96px' }, children: [jsx("rect", { x: "128", y: "96", width: "16", height: "36", rx: "7", fill: skin }), jsx("circle", { cx: "136", cy: "136", r: "7", fill: skin }), jsxs(motion.g, { animate: fx.card, children: [jsx("rect", { x: "126", y: "50", width: "42", height: "30", rx: "4", fill: "var(--nice-color-surface, #fff)", stroke: "var(--nice-text-primary, #111)", strokeWidth: 1.2 }), jsx("text", { x: "147", y: "70", textAnchor: "middle", fontSize: "16", fontWeight: 700, fill: "var(--nice-text-primary, #111)", children: String(score) })] }), renderProp(cfg.prop)] }), jsxs(motion.g, { animate: fx.head, children: [renderHairBack(cfg.hair), renderFace(cfg.face), jsx(motion.g, { animate: fx.eyes, children: renderEyes(cfg.eyes) }), renderNose(cfg.nose), jsx(motion.g, { animate: fx.mouth, children: renderMouth(cfg.mouth) }), renderHeadwear(cfg.headwear)] }), jsxs(motion.g, { animate: fx.curse, initial: { opacity: 0 }, children: [jsx("rect", { x: "62", y: "10", width: "56", height: "22", rx: "8", fill: "var(--nice-text-primary, #111)" }), jsx("text", { x: "90", y: "26", textAnchor: "middle", fontSize: "12", fill: "var(--nice-text-inverse, #fff)", children: "#!%&*" }), jsx("path", { d: "M82 32 l4 8 l4 -8", fill: "var(--nice-text-primary, #111)" })] })] }) }));
|
|
25
|
+
return (jsx(motion.div, { id: id, style: { width: W, height: W * 1.35, overflow: 'visible' }, animate: fx.root, children: jsxs("svg", { viewBox: "0 0 180 240", width: "100%", height: "100%", role: "img", "aria-label": (_e = (_d = cfg.name) !== null && _d !== void 0 ? _d : name) !== null && _e !== void 0 ? _e : t('animatedPerson.juror', 'Juror'), children: [jsxs(motion.g, { animate: fx.body, children: [jsx("rect", { x: "68", y: "158", width: "14", height: "46", rx: "6", fill: pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg, #334155)') }), jsx("ellipse", { cx: "75", cy: "204", rx: "10", ry: "5", fill: pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg-shadow, #1E293B)') }), jsx("rect", { x: "98", y: "158", width: "14", height: "46", rx: "6", fill: pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg, #334155)') }), jsx("ellipse", { cx: "105", cy: "204", rx: "10", ry: "5", fill: pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg-shadow, #1E293B)') })] }), jsx(motion.g, { animate: fx.body, children: renderOutfit(cfg.outfit) }), jsx("rect", { x: "84", y: "82", width: "12", height: "12", rx: "4", fill: skin }), jsxs(motion.g, { animate: fx.leftArm, style: { transformOrigin: '64px 96px' }, children: [jsx("rect", { x: "36", y: "96", width: "16", height: "36", rx: "7", fill: skin }), jsx("circle", { cx: "44", cy: "136", r: "7", fill: skin })] }), jsxs(motion.g, { animate: fx.rightArm, style: { transformOrigin: '116px 96px' }, children: [jsx("rect", { x: "128", y: "96", width: "16", height: "36", rx: "7", fill: skin }), jsx("circle", { cx: "136", cy: "136", r: "7", fill: skin }), jsxs(motion.g, { animate: fx.card, children: [jsx("rect", { x: "126", y: "50", width: "42", height: "30", rx: "4", fill: "var(--nice-color-surface, #fff)", stroke: "var(--nice-text-primary, #111)", strokeWidth: 1.2 }), jsx("text", { x: "147", y: "70", textAnchor: "middle", fontSize: "16", fontWeight: 700, fill: "var(--nice-text-primary, #111)", children: String(score) })] }), renderProp(cfg.prop)] }), jsxs(motion.g, { animate: fx.head, children: [renderHairBack(cfg.hair), renderFace(cfg.face), jsx(motion.g, { animate: fx.eyes, children: renderEyes(cfg.eyes) }), renderNose(cfg.nose), jsx(motion.g, { animate: fx.mouth, children: renderMouth(cfg.mouth) }), renderHeadwear(cfg.headwear)] }), jsxs(motion.g, { animate: fx.curse, initial: { opacity: 0 }, children: [jsx("rect", { x: "62", y: "10", width: "56", height: "22", rx: "8", fill: "var(--nice-text-primary, #111)" }), jsx("text", { x: "90", y: "26", textAnchor: "middle", fontSize: "12", fill: "var(--nice-text-inverse, #fff)", children: "#!%&*" }), jsx("path", { d: "M82 32 l4 8 l4 -8", fill: "var(--nice-text-primary, #111)" })] })] }) }));
|
|
24
26
|
};
|
|
25
27
|
/* ---------------- RENDERERS (redesigned proportions) ---------------- */
|
|
26
28
|
function renderOutfit(cfg) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { NiceButton } from '@nice2dev/ui';
|
|
3
3
|
import React from 'react';
|
|
4
|
+
import { useNiceTranslation } from '../core/i18n.js';
|
|
4
5
|
import NiceAnimatedPerson from './AnimatedPerson.js';
|
|
5
6
|
import { DEFAULT_CHARACTER } from './characterTypes.js';
|
|
6
7
|
import { runRounds, seq } from './choreoDSL.js';
|
|
@@ -33,6 +34,7 @@ function makeAudience(n) {
|
|
|
33
34
|
}
|
|
34
35
|
/** Grid of animated audience members with wave/cheer actions. */
|
|
35
36
|
const NiceAudience = ({ count = 36, columns = 12 }) => {
|
|
37
|
+
const { t } = useNiceTranslation();
|
|
36
38
|
const people = React.useMemo(() => makeAudience(count), [count]);
|
|
37
39
|
const refs = React.useRef([]);
|
|
38
40
|
refs.current = [];
|
|
@@ -57,7 +59,7 @@ const NiceAudience = ({ count = 36, columns = 12 }) => {
|
|
|
57
59
|
display: 'grid',
|
|
58
60
|
gap: 8,
|
|
59
61
|
gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
|
|
60
|
-
}, children: people.map((c, i) => (jsx(NiceAnimatedPerson, { character: c, score: 10, startPose: "idle", onReady: (fx) => (refs.current[i] = fx) }, i))) }), jsxs("div", { style: { display: 'flex', gap: 8 }, children: [jsx(NiceButton, { variant: "ghost", size: "sm", onClick: wave, children:
|
|
62
|
+
}, children: people.map((c, i) => (jsx(NiceAnimatedPerson, { character: c, score: 10, startPose: "idle", onReady: (fx) => (refs.current[i] = fx) }, i))) }), jsxs("div", { style: { display: 'flex', gap: 8 }, children: [jsx(NiceButton, { variant: "ghost", size: "sm", onClick: wave, children: t('audience.wave', 'Wave') }), jsx(NiceButton, { variant: "ghost", size: "sm", onClick: cheer, children: t('audience.cheer', 'Cheer') })] })] }));
|
|
61
63
|
};
|
|
62
64
|
|
|
63
65
|
export { NiceAudience as default };
|
|
@@ -7,7 +7,7 @@ import NiceRiggedBody from './RiggedBody.js';
|
|
|
7
7
|
import { solveFK } from './rigMath.js';
|
|
8
8
|
import { DEFAULT_POSE, DEFAULT_RIG } from './rigTypes.js';
|
|
9
9
|
|
|
10
|
-
const NiceRigPlayer = React.forwardRef(({ character, rig, pose, size, debug }, ref) => {
|
|
10
|
+
const NiceRigPlayer = /* @__PURE__ */ React.forwardRef(({ character, rig, pose, size, debug }, ref) => {
|
|
11
11
|
var _a;
|
|
12
12
|
const [current, setCurrent] = React.useState({ ...DEFAULT_POSE, ...(pose !== null && pose !== void 0 ? pose : {}) });
|
|
13
13
|
const r = rig !== null && rig !== void 0 ? rig : DEFAULT_RIG;
|