@nice2dev/ui-graphics 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/dist/cjs/animation/Audience.js +3 -1
  3. package/dist/cjs/animation/rig/RigPlayer.js +1 -1
  4. package/dist/cjs/animation/rig/presets.js +67 -55
  5. package/dist/cjs/animation/shapes/headwear.js +4 -1
  6. package/dist/cjs/core/NiceBrushEngine.js +5 -3
  7. package/dist/cjs/core/editorAsk.js +48 -0
  8. package/dist/cjs/core/i18n.js +7 -3
  9. package/dist/cjs/core/plugins.js +1 -1
  10. package/dist/cjs/core/rtl.js +1 -1
  11. package/dist/cjs/core/theme.js +1 -1
  12. package/dist/cjs/font/NiceFontEditor.js +13 -11
  13. package/dist/cjs/game/GameAsset2dEditor.js +28 -16
  14. package/dist/cjs/nice2dev-ui-graphics.css +1 -1
  15. package/dist/cjs/photo/FilterThumb.js +3 -3
  16. package/dist/cjs/photo/NiceAITools.js +5 -3
  17. package/dist/cjs/photo/PhotoEditor.js +3 -1
  18. package/dist/cjs/photo/PhotoEditorCanvas.js +8 -6
  19. package/dist/cjs/photo/PhotoEditorLeftPanel.js +19 -19
  20. package/dist/cjs/photo/PhotoEditorToolbar.js +4 -4
  21. package/dist/cjs/photo/photoFilters.js +1 -0
  22. package/dist/cjs/pixel/PixelEditor.js +6 -3
  23. package/dist/cjs/pixel/PixelEditorCanvas.js +10 -6
  24. package/dist/cjs/pixel/PixelEditorMenuBar.js +3 -3
  25. package/dist/cjs/pixel/PixelEditorRightPanel.js +28 -24
  26. package/dist/cjs/pixel/PixelEditorStatusBar.js +8 -4
  27. package/dist/cjs/pixel/PixelEditorTimeline.js +11 -2
  28. package/dist/cjs/pixel/PixelEditorToolbar.js +10 -6
  29. package/dist/cjs/pixel/SpriteGeneratorPanel.js +11 -7
  30. package/dist/cjs/pixel/animalGeneratorPresets.js +31 -31
  31. package/dist/cjs/pixel/buildingGeneratorPresets.js +24 -24
  32. package/dist/cjs/pixel/characterGeneratorPresets.js +25 -25
  33. package/dist/cjs/pixel/usePixelEditor.js +13 -10
  34. package/dist/cjs/texture/Nice3DTexturePainter.js +10 -8
  35. package/dist/cjs/ui/NiceUIDesigner.js +17 -13
  36. package/dist/cjs/vector/NiceSvgToCode.js +6 -4
  37. package/dist/cjs/vector/VectorEditor.js +3 -2
  38. package/dist/cjs/vector/VectorEditorShapeRenderer.js +3 -1
  39. package/dist/cjs/vector/useVectorEditor.js +38 -29
  40. package/dist/cjs/vector/vectorEditorImport.js +1 -0
  41. package/dist/cjs/vector/vectorIconImport.js +1 -0
  42. package/dist/esm/animation/Audience.js +3 -1
  43. package/dist/esm/animation/rig/RigPlayer.js +1 -1
  44. package/dist/esm/animation/rig/presets.js +68 -56
  45. package/dist/esm/animation/shapes/headwear.js +4 -1
  46. package/dist/esm/animation/spineExport.js +1 -1
  47. package/dist/esm/core/NiceBrushEngine.js +5 -3
  48. package/dist/esm/core/editorAsk.js +45 -0
  49. package/dist/esm/core/i18n.js +8 -4
  50. package/dist/esm/core/plugins.js +1 -1
  51. package/dist/esm/core/rtl.js +1 -1
  52. package/dist/esm/core/theme.js +1 -1
  53. package/dist/esm/font/NiceFontEditor.js +13 -11
  54. package/dist/esm/game/GameAsset2dEditor.js +28 -16
  55. package/dist/esm/nice2dev-ui-graphics.css +1 -1
  56. package/dist/esm/photo/FilterThumb.js +3 -3
  57. package/dist/esm/photo/NiceAITools.js +5 -3
  58. package/dist/esm/photo/PhotoEditor.js +3 -1
  59. package/dist/esm/photo/PhotoEditorCanvas.js +8 -6
  60. package/dist/esm/photo/PhotoEditorLeftPanel.js +19 -19
  61. package/dist/esm/photo/PhotoEditorToolbar.js +4 -4
  62. package/dist/esm/photo/photoFilters.js +1 -0
  63. package/dist/esm/pixel/PixelEditor.js +6 -3
  64. package/dist/esm/pixel/PixelEditorCanvas.js +10 -6
  65. package/dist/esm/pixel/PixelEditorMenuBar.js +3 -3
  66. package/dist/esm/pixel/PixelEditorRightPanel.js +28 -24
  67. package/dist/esm/pixel/PixelEditorStatusBar.js +8 -4
  68. package/dist/esm/pixel/PixelEditorTimeline.js +11 -2
  69. package/dist/esm/pixel/PixelEditorToolbar.js +10 -6
  70. package/dist/esm/pixel/SpriteGeneratorPanel.js +11 -7
  71. package/dist/esm/pixel/animalGeneratorPresets.js +32 -32
  72. package/dist/esm/pixel/buildingGeneratorPresets.js +25 -25
  73. package/dist/esm/pixel/characterGeneratorPresets.js +26 -26
  74. package/dist/esm/pixel/usePixelEditor.js +13 -10
  75. package/dist/esm/texture/Nice3DTexturePainter.js +10 -8
  76. package/dist/esm/ui/NiceUIDesigner.js +17 -13
  77. package/dist/esm/vector/NiceSvgToCode.js +6 -4
  78. package/dist/esm/vector/VectorEditor.js +3 -2
  79. package/dist/esm/vector/VectorEditorShapeRenderer.js +3 -1
  80. package/dist/esm/vector/useVectorEditor.js +38 -29
  81. package/dist/esm/vector/vectorEditorImport.js +1 -0
  82. package/dist/esm/vector/vectorIconImport.js +1 -0
  83. package/dist/types/core/LocalUI.d.ts +1 -2
  84. package/dist/types/core/editorAsk.d.ts +26 -0
  85. package/dist/types/core/i18n.d.ts +1 -17
  86. package/dist/types/pixel/usePixelEditor.d.ts +3 -0
  87. package/dist/types/vector/useVectorEditor.d.ts +2 -0
  88. package/package.json +5 -5
@@ -6,7 +6,9 @@ const ShapeRenderer = ({ shape: s, selected, zoom }) => {
6
6
  if (s.hidden) {
7
7
  return null;
8
8
  }
9
- const selStroke = selected ? 'rgba(85,102,204,0.6)' : undefined;
9
+ const selStroke = selected
10
+ ? 'color-mix(in srgb, var(--nice-color-primary, #5566cc) 60%, transparent)'
11
+ : undefined;
10
12
  const gProps = {};
11
13
  if (s.opacity < 1) {
12
14
  gProps.opacity = s.opacity;
@@ -1,4 +1,5 @@
1
1
  import { useState, useRef, useMemo, useCallback, useEffect } from 'react';
2
+ import { useEditorAsk } from '../core/editorAsk.js';
2
3
  import { booleanOperation, flattenToOutline } from './vectorBooleanOps.js';
3
4
  import { buildSVGString, exportSVGFile, exportPNGFile, exportJPGFile, exportWebPFile, exportBMPFile } from './vectorEditorExport.js';
4
5
  import { parseSVGToShapes } from './vectorEditorImport.js';
@@ -10,10 +11,13 @@ import { cloneShapes, sid, snapToGrid } from './vectorEditorTypes.js';
10
11
  * so the component file is pure layout.
11
12
  */
12
13
  function useVectorEditor({ artW, artH, onSaveToLibrary }) {
14
+ // the library's dialogs for the text tool and the library name (not window.prompt)
15
+ const { pendingAsk, ask } = useEditorAsk();
13
16
  /* ── state ── */
14
17
  const [shapes, setShapes] = useState([]);
15
18
  const [selId, setSelId] = useState(null);
16
19
  const [tool, setTool] = useState('pointer');
20
+ // audit-allow: literal-color — the first fill of a new shape - document data
17
21
  const [fillCol, setFillCol] = useState('#5566cc');
18
22
  const [strokeCol, setStrokeCol] = useState('#000000');
19
23
  const [strokeW, setStrokeW] = useState(2);
@@ -176,28 +180,30 @@ function useVectorEditor({ artW, artH, onSaveToLibrary }) {
176
180
  return;
177
181
  }
178
182
  if (tool === 'text') {
179
- const txt = prompt('Enter text:', 'Hello');
180
- if (!txt) {
181
- return;
182
- }
183
- const ns = {
184
- id: sid(),
185
- type: 'text',
186
- x: p.x,
187
- y: p.y,
188
- w: txt.length * fontSize * 0.6,
189
- h: fontSize,
190
- ...defaultBase,
191
- stroke: 'none',
192
- strokeWidth: 0,
193
- text: txt,
194
- fontSize,
195
- fontFamily,
196
- };
197
- const next = [...shapes, ns];
198
- snap(next);
199
- setShapes(next);
200
- setSelId(ns.id);
183
+ // the text comes from a dialog: the shape is added once it is entered
184
+ void ask('prompt', 'Enter text:', 'Hello').then((txt) => {
185
+ if (!txt) {
186
+ return;
187
+ }
188
+ const ns = {
189
+ id: sid(),
190
+ type: 'text',
191
+ x: p.x,
192
+ y: p.y,
193
+ w: txt.length * fontSize * 0.6,
194
+ h: fontSize,
195
+ ...defaultBase,
196
+ stroke: 'none',
197
+ strokeWidth: 0,
198
+ text: txt,
199
+ fontSize,
200
+ fontFamily,
201
+ };
202
+ const next = [...shapes, ns];
203
+ snap(next);
204
+ setShapes(next);
205
+ setSelId(ns.id);
206
+ });
201
207
  return;
202
208
  }
203
209
  if (tool === 'polygon') {
@@ -283,6 +289,7 @@ function useVectorEditor({ artW, artH, onSaveToLibrary }) {
283
289
  starPoints2,
284
290
  starInnerRatio,
285
291
  cornerRadius,
292
+ ask,
286
293
  ]);
287
294
  const onMove = useCallback((e) => {
288
295
  if (panning.current) {
@@ -697,15 +704,15 @@ function useVectorEditor({ artW, artH, onSaveToLibrary }) {
697
704
  const blob = new Blob([svg], { type: 'image/svg+xml' });
698
705
  const reader = new FileReader();
699
706
  reader.onload = () => {
700
- const defaultName = 'drawing.svg';
701
- const name = window.prompt('Save to library as:', defaultName);
702
- if (!name || !name.trim()) {
703
- return;
704
- }
705
- onSaveToLibrary(reader.result, name.trim(), 'image/svg+xml');
707
+ const dataUrl = reader.result;
708
+ void ask('prompt', 'Save to library as:', 'drawing.svg').then((name) => {
709
+ if (name && name.trim()) {
710
+ onSaveToLibrary(dataUrl, name.trim(), 'image/svg+xml');
711
+ }
712
+ });
706
713
  };
707
714
  reader.readAsDataURL(blob);
708
- }, [shapes, artW, artH, onSaveToLibrary]);
715
+ }, [shapes, artW, artH, onSaveToLibrary, ask]);
709
716
  /* ── export shortcuts ── */
710
717
  const exportSVG = useCallback(() => exportSVGFile(shapes, artW, artH, animations), [shapes, artW, artH, animations]);
711
718
  const exportPNG = useCallback(() => exportPNGFile(shapes, artW, artH), [shapes, artW, artH]);
@@ -791,6 +798,8 @@ function useVectorEditor({ artW, artH, onSaveToLibrary }) {
791
798
  /* ── return ── */
792
799
  return {
793
800
  // state
801
+ /** The question the editor waits on; render it with `EditorAskDialog`. */
802
+ pendingAsk,
794
803
  shapes,
795
804
  selId,
796
805
  tool,
@@ -16,6 +16,7 @@ function parseSVGToShapes(raw) {
16
16
  const elems = doc.querySelectorAll('rect, ellipse, circle, line, polyline, polygon, text, path');
17
17
  elems.forEach((el) => {
18
18
  const tag = el.tagName.toLowerCase();
19
+ // audit-allow: literal-color — an imported shape without a fill gets a visible default - document data
19
20
  const fill = el.getAttribute('fill') || (tag === 'line' ? 'none' : '#cccccc');
20
21
  const stroke = el.getAttribute('stroke') || 'none';
21
22
  const sw = parseFloat(el.getAttribute('stroke-width') || '1');
@@ -37,6 +37,7 @@ function iconComponentToShapes(Icon, options) {
37
37
  // Force a generous size and visible colors so the parser sees concrete strokes/fills.
38
38
  const renderProps = {
39
39
  size: 'lg',
40
+ // audit-allow: literal-color +2 — an imported icon's default colours - document data
40
41
  primaryColor: '#5566cc',
41
42
  secondaryColor: '#a29bfe',
42
43
  strokeWidth: 2,
@@ -16,6 +16,5 @@ export interface RangeInputProps extends Omit<React.InputHTMLAttributes<HTMLInpu
16
16
  onChange?: React.ChangeEventHandler<HTMLInputElement>;
17
17
  }
18
18
  export declare const RangeInput: React.FC<RangeInputProps>;
19
- export interface TextAreaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
20
- }
19
+ export type TextAreaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
21
20
  export declare const TextArea: React.FC<TextAreaProps>;
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ /** A question an editor hook waits on; {@link EditorAskDialog} shows it. */
3
+ export interface EditorAsk {
4
+ /** `prompt` asks for a value, `confirm` for a yes / no. */
5
+ kind: 'prompt' | 'confirm';
6
+ /** The prompt's title, or the question to confirm. */
7
+ text: string;
8
+ /** Initial value of a prompt. */
9
+ defaultValue?: string;
10
+ /** Settles it: the entered value (`''` for an accepted confirm), or `null` when cancelled. */
11
+ answer: (value: string | null) => void;
12
+ }
13
+ /**
14
+ * The library's dialogs in place of `window.prompt` / `window.confirm` for the editor hooks,
15
+ * which hold state but render nothing: `ask` resolves with the answer (`null` when cancelled)
16
+ * and `pendingAsk` is the open question, for the editor component to pass to
17
+ * {@link EditorAskDialog}. One question at a time: a new one settles the open one as cancelled.
18
+ */
19
+ export declare function useEditorAsk(): {
20
+ pendingAsk: EditorAsk | null;
21
+ ask: (kind: EditorAsk["kind"], text: string, defaultValue?: string) => Promise<string | null>;
22
+ };
23
+ /** Shows the question {@link useEditorAsk} is waiting on, if any. */
24
+ export declare const EditorAskDialog: React.FC<{
25
+ ask: EditorAsk | null;
26
+ }>;
@@ -1,26 +1,10 @@
1
- /**
2
- * i18n — Lightweight translation context for @nice2dev/ui-graphics.
3
- *
4
- * Drop-in replacement for react-i18next's useTranslation, requires NO
5
- * external dependencies. Consumers can optionally wrap with NiceI18nProvider
6
- * to supply their own translation function. Falls back to the default
7
- * (English key strings) when no provider is present.
8
- *
9
- * Usage in components:
10
- * const { t } = useNiceTranslation();
11
- * <button>{t('actions.save', 'Save')}</button>
12
- *
13
- * Usage in apps:
14
- * <NiceI18nProvider t={(key, fallback) => myI18n(key) ?? fallback}>
15
- * <App />
16
- * </NiceI18nProvider>
17
- */
18
1
  import React from 'react';
19
2
  export type NiceTranslateFn = (key: string, defaultValue: string) => string;
20
3
  export interface NiceI18nProviderProps {
21
4
  t: NiceTranslateFn;
22
5
  children: React.ReactNode;
23
6
  }
7
+ /** Overrides the app's global translator for the graphics controls below it. */
24
8
  export declare const NiceI18nProvider: React.FC<NiceI18nProviderProps>;
25
9
  export declare function useNiceTranslation(): {
26
10
  t: NiceTranslateFn;
@@ -2,6 +2,7 @@
2
2
  * usePixelEditor — custom hook encapsulating ALL state, refs, callbacks,
3
3
  * side-effects and memos for the PixelEditor component.
4
4
  */
5
+ import { type EditorAsk } from '../core/editorAsk';
5
6
  import { type Tool, type Layer, type BlendMode, type Selection } from './pixelEditorTypes';
6
7
  import { type BrushConfig, type SymmetryMode } from './pixelEditorUtils';
7
8
  export interface UsePixelEditorProps {
@@ -81,6 +82,8 @@ export interface PixelEditorAPI {
81
82
  exportSVG: () => void;
82
83
  exportSheet: () => void;
83
84
  saveToLibrary: () => void;
85
+ /** The question the editor waits on (library name); render it with `EditorAskDialog`. */
86
+ pendingAsk: EditorAsk | null;
84
87
  addLayer: () => void;
85
88
  rmLayer: () => void;
86
89
  toggleVis: (i: number) => void;
@@ -11,6 +11,8 @@ export interface UseVectorEditorOptions {
11
11
  onSaveToLibrary?: (dataUrl: string, name: string, mimeType: string) => void;
12
12
  }
13
13
  export declare function useVectorEditor({ artW, artH, onSaveToLibrary }: UseVectorEditorOptions): {
14
+ /** The question the editor waits on; render it with `EditorAskDialog`. */
15
+ readonly pendingAsk: import("../core/editorAsk").EditorAsk | null;
14
16
  readonly shapes: VShape[];
15
17
  readonly selId: string | null;
16
18
  readonly tool: VTool;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nice2dev/ui-graphics",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Nice2Dev Graphics Editors — Pixel, Vector, Photo and Animation editors for React",
5
5
  "author": "NiceToDev <contact@nicetodev.com>",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -64,15 +64,15 @@
64
64
  "nicetodev"
65
65
  ],
66
66
  "peerDependencies": {
67
- "@nice2dev/ui-core": "^1.0.20",
68
- "@nice2dev/ui": "^1.0.20",
67
+ "@nice2dev/ui-core": "^1.2.0",
68
+ "@nice2dev/ui": "^1.2.0",
69
69
  "framer-motion": ">=10.0.0",
70
70
  "react": ">=17.0.0",
71
71
  "react-dom": ">=17.0.0"
72
72
  },
73
73
  "devDependencies": {
74
- "@nice2dev/ui": "^1.2.0",
75
- "@nice2dev/ui-core": "^1.2.0",
74
+ "@nice2dev/ui": "^1.2.1",
75
+ "@nice2dev/ui-core": "^1.2.1",
76
76
  "@rollup/plugin-commonjs": "^29.0.3",
77
77
  "@rollup/plugin-node-resolve": "^16.0.3",
78
78
  "@rollup/plugin-typescript": "^12.3.0",