sketchmark 0.2.6 → 0.2.7
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/README.md +3 -3
- package/dist/index.cjs +65 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +65 -42
- package/dist/index.js.map +1 -1
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/renderer/canvas/index.d.ts.map +1 -1
- package/dist/renderer/svg/index.d.ts.map +1 -1
- package/dist/sketchmark.iife.js +65 -42
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -180,9 +180,9 @@ icon id label="..." name="prefix:name"
|
|
|
180
180
|
|
|
181
181
|
> **`text` shape:** No border or background. Long labels auto word-wrap. Use `width=` to control the wrap width.
|
|
182
182
|
|
|
183
|
-
> **`image` shape:** Renders an image clipped to a rounded rect. Requires `url=` property. Label
|
|
183
|
+
> **`image` shape:** Renders an image clipped to a rounded rect. Requires `url=` property. Label renders below the image. Border only shown when `stroke=` is set.
|
|
184
184
|
|
|
185
|
-
> **`icon` shape:** Renders an icon from [Iconify](https://iconify.design/) (200,000+ open source icons). Requires `name=` property in `prefix:name` format (e.g. `mdi:database`). Defaults to `mdi` prefix if omitted. Use `color=` to tint the icon. Label
|
|
185
|
+
> **`icon` shape:** Renders an icon from [Iconify](https://iconify.design/) (200,000+ open source icons). Requires `name=` property in `prefix:name` format (e.g. `mdi:database`). Defaults to `mdi` prefix if omitted. Use `color=` to tint the icon. Label renders below the icon. Border only shown when `stroke=` is set. Default size: 48x48.
|
|
186
186
|
|
|
187
187
|
**Example:**
|
|
188
188
|
```
|
|
@@ -210,7 +210,7 @@ icon id [label="..."] name="prefix:name" [color="#hex"] [width=N] [height=N]
|
|
|
210
210
|
| `name` | `name="mdi:database"` | Icon identifier in `prefix:name` format. Defaults to `mdi` prefix if omitted |
|
|
211
211
|
| `color` | `color="#1976D2"` | Icon tint color |
|
|
212
212
|
| `stroke` | `stroke="#333"` | Optional border (not shown by default) |
|
|
213
|
-
| `label` | `label="DB"` |
|
|
213
|
+
| `label` | `label="DB"` | Label shown below the icon (defaults to id) |
|
|
214
214
|
| `width` | `width=64` | Icon width (default: 48) |
|
|
215
215
|
| `height` | `height=64` | Icon height (default: 48) |
|
|
216
216
|
|
package/dist/index.cjs
CHANGED
|
@@ -424,7 +424,7 @@ function parse(src) {
|
|
|
424
424
|
kind: "node",
|
|
425
425
|
id,
|
|
426
426
|
shape,
|
|
427
|
-
label: props.label ||
|
|
427
|
+
label: props.label || id,
|
|
428
428
|
...(groupId ? { groupId } : {}),
|
|
429
429
|
...(props.width ? { width: parseFloat(props.width) } : {}),
|
|
430
430
|
...(props.height ? { height: parseFloat(props.height) } : {}),
|
|
@@ -1509,10 +1509,13 @@ function sizeNode(n) {
|
|
|
1509
1509
|
}
|
|
1510
1510
|
break;
|
|
1511
1511
|
}
|
|
1512
|
-
case "icon":
|
|
1513
|
-
|
|
1514
|
-
|
|
1512
|
+
case "icon": {
|
|
1513
|
+
const iconBase = 48;
|
|
1514
|
+
const labelH = n.label ? 20 : 0;
|
|
1515
|
+
n.w = n.w || Math.max(iconBase, n.label ? labelW : 0);
|
|
1516
|
+
n.h = n.h || (iconBase + labelH);
|
|
1515
1517
|
break;
|
|
1518
|
+
}
|
|
1516
1519
|
default:
|
|
1517
1520
|
n.w = n.w || Math.max(MIN_W, Math.min(MAX_W, labelW));
|
|
1518
1521
|
n.h = n.h || 52;
|
|
@@ -5116,27 +5119,32 @@ function renderShape$1(rc, n, palette) {
|
|
|
5116
5119
|
const iconColor = s.color
|
|
5117
5120
|
? encodeURIComponent(String(s.color))
|
|
5118
5121
|
: encodeURIComponent(String(palette.nodeStroke));
|
|
5119
|
-
|
|
5122
|
+
// reserve bottom 20px for label when present
|
|
5123
|
+
const labelSpace = n.label ? 20 : 0;
|
|
5124
|
+
const iconAreaH = n.h - labelSpace;
|
|
5125
|
+
const iconSize = Math.min(n.w, iconAreaH) - 4;
|
|
5120
5126
|
const iconUrl = `https://api.iconify.design/${prefix}/${name}.svg?color=${iconColor}&width=${iconSize}&height=${iconSize}`;
|
|
5121
5127
|
const img = document.createElementNS(NS, "image");
|
|
5122
5128
|
img.setAttribute("href", iconUrl);
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
img.setAttribute("
|
|
5126
|
-
img.setAttribute("
|
|
5129
|
+
const iconX = n.x + (n.w - iconSize) / 2;
|
|
5130
|
+
const iconY = n.y + (iconAreaH - iconSize) / 2;
|
|
5131
|
+
img.setAttribute("x", String(iconX));
|
|
5132
|
+
img.setAttribute("y", String(iconY));
|
|
5133
|
+
img.setAttribute("width", String(iconSize));
|
|
5134
|
+
img.setAttribute("height", String(iconSize));
|
|
5127
5135
|
img.setAttribute("preserveAspectRatio", "xMidYMid meet");
|
|
5128
5136
|
if (s.opacity != null)
|
|
5129
5137
|
img.setAttribute("opacity", String(s.opacity));
|
|
5130
|
-
// clip-path for rounded corners
|
|
5138
|
+
// clip-path for rounded corners
|
|
5131
5139
|
const clipId = `clip-${n.id}`;
|
|
5132
5140
|
const defs = document.createElementNS(NS, "defs");
|
|
5133
5141
|
const clip = document.createElementNS(NS, "clipPath");
|
|
5134
5142
|
clip.setAttribute("id", clipId);
|
|
5135
5143
|
const rect = document.createElementNS(NS, "rect");
|
|
5136
|
-
rect.setAttribute("x", String(
|
|
5137
|
-
rect.setAttribute("y", String(
|
|
5138
|
-
rect.setAttribute("width", String(
|
|
5139
|
-
rect.setAttribute("height", String(
|
|
5144
|
+
rect.setAttribute("x", String(iconX));
|
|
5145
|
+
rect.setAttribute("y", String(iconY));
|
|
5146
|
+
rect.setAttribute("width", String(iconSize));
|
|
5147
|
+
rect.setAttribute("height", String(iconSize));
|
|
5140
5148
|
rect.setAttribute("rx", "6");
|
|
5141
5149
|
clip.appendChild(rect);
|
|
5142
5150
|
defs.appendChild(clip);
|
|
@@ -5162,12 +5170,15 @@ function renderShape$1(rc, n, palette) {
|
|
|
5162
5170
|
}
|
|
5163
5171
|
case "image": {
|
|
5164
5172
|
if (n.imageUrl) {
|
|
5173
|
+
// reserve bottom 20px for label when present
|
|
5174
|
+
const imgLabelSpace = n.label ? 20 : 0;
|
|
5175
|
+
const imgAreaH = n.h - imgLabelSpace;
|
|
5165
5176
|
const img = document.createElementNS(NS, "image");
|
|
5166
5177
|
img.setAttribute("href", n.imageUrl);
|
|
5167
5178
|
img.setAttribute("x", String(n.x + 1));
|
|
5168
5179
|
img.setAttribute("y", String(n.y + 1));
|
|
5169
5180
|
img.setAttribute("width", String(n.w - 2));
|
|
5170
|
-
img.setAttribute("height", String(
|
|
5181
|
+
img.setAttribute("height", String(imgAreaH - 2));
|
|
5171
5182
|
img.setAttribute("preserveAspectRatio", "xMidYMid meet");
|
|
5172
5183
|
const clipId = `clip-${n.id}`;
|
|
5173
5184
|
const defs = document.createElementNS(NS, "defs");
|
|
@@ -5177,7 +5188,7 @@ function renderShape$1(rc, n, palette) {
|
|
|
5177
5188
|
rect.setAttribute("x", String(n.x + 1));
|
|
5178
5189
|
rect.setAttribute("y", String(n.y + 1));
|
|
5179
5190
|
rect.setAttribute("width", String(n.w - 2));
|
|
5180
|
-
rect.setAttribute("height", String(
|
|
5191
|
+
rect.setAttribute("height", String(imgAreaH - 2));
|
|
5181
5192
|
rect.setAttribute("rx", "6");
|
|
5182
5193
|
clip.appendChild(rect);
|
|
5183
5194
|
defs.appendChild(clip);
|
|
@@ -5429,11 +5440,14 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
5429
5440
|
const nodeBodyBottom = n.y + n.h - pad;
|
|
5430
5441
|
const nodeBodyMid = n.y + n.h / 2;
|
|
5431
5442
|
const blockH = (lines.length - 1) * lineHeight;
|
|
5432
|
-
const
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5443
|
+
const isMediaShape = n.shape === "icon" || n.shape === "image";
|
|
5444
|
+
const textCY = isMediaShape
|
|
5445
|
+
? n.y + n.h - 10 // label below the icon/image
|
|
5446
|
+
: verticalAlign === "top"
|
|
5447
|
+
? nodeBodyTop + blockH / 2
|
|
5448
|
+
: verticalAlign === "bottom"
|
|
5449
|
+
? nodeBodyBottom - blockH / 2
|
|
5450
|
+
: nodeBodyMid;
|
|
5437
5451
|
if (n.label) {
|
|
5438
5452
|
ng.appendChild(lines.length > 1
|
|
5439
5453
|
? mkMultilineText(lines, textX, textCY, fontSize, fontWeight, textColor, textAnchor, lineHeight, nodeFont, letterSpacing)
|
|
@@ -6144,7 +6158,10 @@ function renderShape(rc, ctx, n, palette, R) {
|
|
|
6144
6158
|
const iconColor = s.color
|
|
6145
6159
|
? encodeURIComponent(String(s.color))
|
|
6146
6160
|
: encodeURIComponent(String(palette.nodeStroke));
|
|
6147
|
-
|
|
6161
|
+
// reserve bottom for label
|
|
6162
|
+
const iconLabelSpace = n.label ? 20 : 0;
|
|
6163
|
+
const iconAreaH = n.h - iconLabelSpace;
|
|
6164
|
+
const iconSize = Math.min(n.w, iconAreaH) - 4;
|
|
6148
6165
|
const iconUrl = `https://api.iconify.design/${prefix}/${name}.svg?color=${iconColor}&width=${iconSize}&height=${iconSize}`;
|
|
6149
6166
|
const img = new Image();
|
|
6150
6167
|
img.crossOrigin = 'anonymous';
|
|
@@ -6152,23 +6169,23 @@ function renderShape(rc, ctx, n, palette, R) {
|
|
|
6152
6169
|
ctx.save();
|
|
6153
6170
|
if (s.opacity != null)
|
|
6154
6171
|
ctx.globalAlpha = Number(s.opacity);
|
|
6155
|
-
|
|
6172
|
+
const iconX = n.x + (n.w - iconSize) / 2;
|
|
6173
|
+
const iconY = n.y + (iconAreaH - iconSize) / 2;
|
|
6156
6174
|
ctx.beginPath();
|
|
6157
6175
|
const r = 6;
|
|
6158
|
-
ctx.moveTo(
|
|
6159
|
-
ctx.lineTo(
|
|
6160
|
-
ctx.quadraticCurveTo(
|
|
6161
|
-
ctx.lineTo(
|
|
6162
|
-
ctx.quadraticCurveTo(
|
|
6163
|
-
ctx.lineTo(
|
|
6164
|
-
ctx.quadraticCurveTo(
|
|
6165
|
-
ctx.lineTo(
|
|
6166
|
-
ctx.quadraticCurveTo(
|
|
6176
|
+
ctx.moveTo(iconX + r, iconY);
|
|
6177
|
+
ctx.lineTo(iconX + iconSize - r, iconY);
|
|
6178
|
+
ctx.quadraticCurveTo(iconX + iconSize, iconY, iconX + iconSize, iconY + r);
|
|
6179
|
+
ctx.lineTo(iconX + iconSize, iconY + iconSize - r);
|
|
6180
|
+
ctx.quadraticCurveTo(iconX + iconSize, iconY + iconSize, iconX + iconSize - r, iconY + iconSize);
|
|
6181
|
+
ctx.lineTo(iconX + r, iconY + iconSize);
|
|
6182
|
+
ctx.quadraticCurveTo(iconX, iconY + iconSize, iconX, iconY + iconSize - r);
|
|
6183
|
+
ctx.lineTo(iconX, iconY + r);
|
|
6184
|
+
ctx.quadraticCurveTo(iconX, iconY, iconX + r, iconY);
|
|
6167
6185
|
ctx.closePath();
|
|
6168
6186
|
ctx.clip();
|
|
6169
|
-
ctx.drawImage(img,
|
|
6187
|
+
ctx.drawImage(img, iconX, iconY, iconSize, iconSize);
|
|
6170
6188
|
ctx.restore();
|
|
6171
|
-
// only draw border when stroke is explicitly set
|
|
6172
6189
|
if (s.stroke) {
|
|
6173
6190
|
rc.rectangle(n.x + 1, n.y + 1, n.w - 2, n.h - 2, { ...opts, fill: 'none' });
|
|
6174
6191
|
}
|
|
@@ -6182,6 +6199,9 @@ function renderShape(rc, ctx, n, palette, R) {
|
|
|
6182
6199
|
}
|
|
6183
6200
|
case 'image': {
|
|
6184
6201
|
if (n.imageUrl) {
|
|
6202
|
+
// reserve bottom for label
|
|
6203
|
+
const imgLblSpace = n.label ? 20 : 0;
|
|
6204
|
+
const imgAreaH = n.h - imgLblSpace;
|
|
6185
6205
|
const img = new Image();
|
|
6186
6206
|
img.crossOrigin = 'anonymous';
|
|
6187
6207
|
img.onload = () => {
|
|
@@ -6191,15 +6211,15 @@ function renderShape(rc, ctx, n, palette, R) {
|
|
|
6191
6211
|
ctx.moveTo(n.x + r, n.y);
|
|
6192
6212
|
ctx.lineTo(n.x + n.w - r, n.y);
|
|
6193
6213
|
ctx.quadraticCurveTo(n.x + n.w, n.y, n.x + n.w, n.y + r);
|
|
6194
|
-
ctx.lineTo(n.x + n.w, n.y +
|
|
6195
|
-
ctx.quadraticCurveTo(n.x + n.w, n.y +
|
|
6196
|
-
ctx.lineTo(n.x + r, n.y +
|
|
6197
|
-
ctx.quadraticCurveTo(n.x, n.y +
|
|
6214
|
+
ctx.lineTo(n.x + n.w, n.y + imgAreaH - r);
|
|
6215
|
+
ctx.quadraticCurveTo(n.x + n.w, n.y + imgAreaH, n.x + n.w - r, n.y + imgAreaH);
|
|
6216
|
+
ctx.lineTo(n.x + r, n.y + imgAreaH);
|
|
6217
|
+
ctx.quadraticCurveTo(n.x, n.y + imgAreaH, n.x, n.y + imgAreaH - r);
|
|
6198
6218
|
ctx.lineTo(n.x, n.y + r);
|
|
6199
6219
|
ctx.quadraticCurveTo(n.x, n.y, n.x + r, n.y);
|
|
6200
6220
|
ctx.closePath();
|
|
6201
6221
|
ctx.clip();
|
|
6202
|
-
ctx.drawImage(img, n.x + 1, n.y + 1, n.w - 2,
|
|
6222
|
+
ctx.drawImage(img, n.x + 1, n.y + 1, n.w - 2, imgAreaH - 2);
|
|
6203
6223
|
ctx.restore();
|
|
6204
6224
|
// only draw border when stroke is explicitly set
|
|
6205
6225
|
if (s.stroke) {
|
|
@@ -6391,9 +6411,12 @@ function renderToCanvas(sg, canvas, options = {}) {
|
|
|
6391
6411
|
const nodeBodyTop = n.y + pad;
|
|
6392
6412
|
const nodeBodyBottom = n.y + n.h - pad;
|
|
6393
6413
|
const blockH = (lines.length - 1) * lineHeight;
|
|
6394
|
-
const
|
|
6395
|
-
|
|
6396
|
-
|
|
6414
|
+
const isMediaShape = n.shape === 'icon' || n.shape === 'image';
|
|
6415
|
+
const textCY = isMediaShape
|
|
6416
|
+
? n.y + n.h - 10 // label below the icon/image
|
|
6417
|
+
: vertAlign === 'top' ? nodeBodyTop + blockH / 2
|
|
6418
|
+
: vertAlign === 'bottom' ? nodeBodyBottom - blockH / 2
|
|
6419
|
+
: n.y + n.h / 2; // middle (default)
|
|
6397
6420
|
if (n.label) {
|
|
6398
6421
|
if (lines.length > 1) {
|
|
6399
6422
|
drawMultilineText(ctx, lines, textX, textCY, fontSize, fontWeight, textColor, textAlign, lineHeight, nodeFont, letterSpacing);
|