mac-human-design 0.1.9 → 0.1.11
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
CHANGED
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
This file tracks changes between published npm versions of `mac-human-design`.
|
|
4
4
|
|
|
5
|
+
## 0.1.11
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Trimmed transparent padding from generated SF Symbol PNGs before caching them,
|
|
10
|
+
so icon buttons center the visible glyph instead of the symbol image's
|
|
11
|
+
asymmetric advance box.
|
|
12
|
+
- Routed `SymbolIconButton` through the shared system-symbol hook so icon
|
|
13
|
+
buttons and standalone symbol images share the same normalized symbol data.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Bumped the package to `0.1.11`.
|
|
18
|
+
|
|
19
|
+
## 0.1.10
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Fixed `SymbolIconButton` alignment by building it on the shared
|
|
24
|
+
`MacIconButton` primitive and centering fallback glyph content within the
|
|
25
|
+
icon box.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- Bumped the package to `0.1.10`.
|
|
30
|
+
|
|
5
31
|
## 0.1.9
|
|
6
32
|
|
|
7
33
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,21 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { MacButton, MacTooltip } from "./MacBaseUI";
|
|
4
|
-
|
|
5
|
-
const SYMBOL_SCALE_MULTIPLIER = 2.5;
|
|
6
|
-
const SYMBOL_MIN_POINT_SIZE = 10;
|
|
7
|
-
const SYMBOL_MAX_POINT_SIZE = 160;
|
|
8
|
-
|
|
9
|
-
const systemSymbolIconCache = new Map<string, string | null>();
|
|
10
|
-
|
|
11
|
-
function getSymbolPointSize(cssPx: number): number {
|
|
12
|
-
const pixelRatio = typeof window === "undefined" ? 1 : window.devicePixelRatio || 1;
|
|
13
|
-
return Math.round(Math.max(SYMBOL_MIN_POINT_SIZE, Math.min(SYMBOL_MAX_POINT_SIZE, cssPx * pixelRatio * SYMBOL_SCALE_MULTIPLIER)));
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function symbolCacheKey(symbolName: string, pointSize: number) {
|
|
17
|
-
return `${symbolName}@${pointSize}`;
|
|
18
|
-
}
|
|
1
|
+
import { MacIconButton, MacTooltip } from "./MacBaseUI";
|
|
2
|
+
import { useSystemSymbol } from "../symbols/useSystemSymbol";
|
|
19
3
|
|
|
20
4
|
type SymbolIconButtonProps = {
|
|
21
5
|
label: string;
|
|
@@ -39,50 +23,17 @@ export function SymbolIconButton({
|
|
|
39
23
|
isLoading,
|
|
40
24
|
iconSizePx = 18,
|
|
41
25
|
}: SymbolIconButtonProps) {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const [symbolDataUrl, setSymbolDataUrl] = useState<string | null>(() => {
|
|
46
|
-
return systemSymbolIconCache.get(symbolCacheLookupKey) ?? null;
|
|
26
|
+
const { dataUrl: symbolDataUrl } = useSystemSymbol({
|
|
27
|
+
symbolName,
|
|
28
|
+
cssPixelSize: iconSizePx,
|
|
47
29
|
});
|
|
48
|
-
|
|
49
|
-
useEffect(() => {
|
|
50
|
-
const cachedDataUrl = systemSymbolIconCache.get(symbolCacheLookupKey);
|
|
51
|
-
if (cachedDataUrl !== undefined) {
|
|
52
|
-
setSymbolDataUrl(cachedDataUrl);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
let isMounted = true;
|
|
57
|
-
void invoke<string | null>("system_symbol_png_data_url", {
|
|
58
|
-
name: symbolName,
|
|
59
|
-
point_size: symbolPointSize,
|
|
60
|
-
})
|
|
61
|
-
.then((dataUrl) => {
|
|
62
|
-
systemSymbolIconCache.set(symbolCacheLookupKey, dataUrl);
|
|
63
|
-
if (isMounted) {
|
|
64
|
-
setSymbolDataUrl(dataUrl);
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
.catch(() => {
|
|
68
|
-
systemSymbolIconCache.set(symbolCacheLookupKey, null);
|
|
69
|
-
if (isMounted) {
|
|
70
|
-
setSymbolDataUrl(null);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
return () => {
|
|
75
|
-
isMounted = false;
|
|
76
|
-
};
|
|
77
|
-
}, [symbolName, symbolPointSize, symbolCacheLookupKey]);
|
|
78
|
-
|
|
79
30
|
const glyphPx = `${iconSizePx}px`;
|
|
80
31
|
|
|
81
32
|
return (
|
|
82
33
|
<MacTooltip.Root>
|
|
83
34
|
<MacTooltip.Trigger
|
|
84
35
|
render={
|
|
85
|
-
<
|
|
36
|
+
<MacIconButton
|
|
86
37
|
aria-label={label}
|
|
87
38
|
title={label}
|
|
88
39
|
disabled={disabled || isLoading}
|
|
@@ -95,15 +46,14 @@ export function SymbolIconButton({
|
|
|
95
46
|
height: ICON_BUTTON_SIZE,
|
|
96
47
|
minHeight: ICON_BUTTON_SIZE,
|
|
97
48
|
borderRadius: ICON_BUTTON_RADIUS,
|
|
98
|
-
padding: 0,
|
|
99
49
|
}}
|
|
100
50
|
/>
|
|
101
51
|
}
|
|
102
52
|
>
|
|
103
53
|
<span
|
|
104
54
|
aria-hidden
|
|
55
|
+
className="hd-mac-symbol-icon"
|
|
105
56
|
style={{
|
|
106
|
-
display: "block",
|
|
107
57
|
width: glyphPx,
|
|
108
58
|
height: glyphPx,
|
|
109
59
|
color: "currentColor",
|
|
@@ -242,6 +242,22 @@
|
|
|
242
242
|
min-height: 28px;
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
+
.hd-mac-symbol-icon-button {
|
|
246
|
+
width: 30px;
|
|
247
|
+
min-width: 30px;
|
|
248
|
+
height: 30px;
|
|
249
|
+
min-height: 30px;
|
|
250
|
+
border-radius: 7px;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.hd-mac-symbol-icon {
|
|
254
|
+
display: inline-flex;
|
|
255
|
+
align-items: center;
|
|
256
|
+
justify-content: center;
|
|
257
|
+
text-align: center;
|
|
258
|
+
flex-shrink: 0;
|
|
259
|
+
}
|
|
260
|
+
|
|
245
261
|
.hd-mac-help-button {
|
|
246
262
|
display: inline-grid;
|
|
247
263
|
width: 18px;
|
|
@@ -11,6 +11,7 @@ const DEFAULT_POINT_SIZE = 18;
|
|
|
11
11
|
const MIN_POINT_SIZE = 10;
|
|
12
12
|
const MAX_POINT_SIZE = 256;
|
|
13
13
|
const SYMBOL_SCALE_MULTIPLIER = 2.5;
|
|
14
|
+
const TRANSPARENT_ALPHA_THRESHOLD = 2;
|
|
14
15
|
|
|
15
16
|
export function calculateSystemSymbolPointSize(
|
|
16
17
|
cssPx: number,
|
|
@@ -58,7 +59,7 @@ export async function getSystemSymbolDataUrl(
|
|
|
58
59
|
point_size: pointSize,
|
|
59
60
|
});
|
|
60
61
|
|
|
61
|
-
const dataUrl = maybeDataUrl
|
|
62
|
+
const dataUrl = maybeDataUrl ? await trimTransparentSymbolPadding(maybeDataUrl) : null;
|
|
62
63
|
SYSTEM_SYMBOL_CACHE.set(key, dataUrl);
|
|
63
64
|
return dataUrl;
|
|
64
65
|
} catch (error) {
|
|
@@ -66,3 +67,90 @@ export async function getSystemSymbolDataUrl(
|
|
|
66
67
|
throw error;
|
|
67
68
|
}
|
|
68
69
|
}
|
|
70
|
+
|
|
71
|
+
async function trimTransparentSymbolPadding(dataUrl: string): Promise<string> {
|
|
72
|
+
if (
|
|
73
|
+
typeof window === "undefined" ||
|
|
74
|
+
typeof Image === "undefined" ||
|
|
75
|
+
typeof document === "undefined"
|
|
76
|
+
) {
|
|
77
|
+
return dataUrl;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const image = await loadImage(dataUrl);
|
|
81
|
+
const width = image.naturalWidth || image.width;
|
|
82
|
+
const height = image.naturalHeight || image.height;
|
|
83
|
+
if (width <= 0 || height <= 0) {
|
|
84
|
+
return dataUrl;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const canvas = document.createElement("canvas");
|
|
88
|
+
canvas.width = width;
|
|
89
|
+
canvas.height = height;
|
|
90
|
+
|
|
91
|
+
const context = canvas.getContext("2d", { willReadFrequently: true });
|
|
92
|
+
if (!context) {
|
|
93
|
+
return dataUrl;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
context.drawImage(image, 0, 0);
|
|
97
|
+
const pixels = context.getImageData(0, 0, width, height).data;
|
|
98
|
+
let minX = width;
|
|
99
|
+
let minY = height;
|
|
100
|
+
let maxX = -1;
|
|
101
|
+
let maxY = -1;
|
|
102
|
+
|
|
103
|
+
for (let y = 0; y < height; y += 1) {
|
|
104
|
+
for (let x = 0; x < width; x += 1) {
|
|
105
|
+
const alpha = pixels[(y * width + x) * 4 + 3];
|
|
106
|
+
if (alpha > TRANSPARENT_ALPHA_THRESHOLD) {
|
|
107
|
+
minX = Math.min(minX, x);
|
|
108
|
+
minY = Math.min(minY, y);
|
|
109
|
+
maxX = Math.max(maxX, x);
|
|
110
|
+
maxY = Math.max(maxY, y);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (maxX < minX || maxY < minY) {
|
|
116
|
+
return dataUrl;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const trimmedWidth = maxX - minX + 1;
|
|
120
|
+
const trimmedHeight = maxY - minY + 1;
|
|
121
|
+
if (trimmedWidth === width && trimmedHeight === height) {
|
|
122
|
+
return dataUrl;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const trimmedCanvas = document.createElement("canvas");
|
|
126
|
+
trimmedCanvas.width = trimmedWidth;
|
|
127
|
+
trimmedCanvas.height = trimmedHeight;
|
|
128
|
+
|
|
129
|
+
const trimmedContext = trimmedCanvas.getContext("2d");
|
|
130
|
+
if (!trimmedContext) {
|
|
131
|
+
return dataUrl;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
trimmedContext.drawImage(
|
|
135
|
+
canvas,
|
|
136
|
+
minX,
|
|
137
|
+
minY,
|
|
138
|
+
trimmedWidth,
|
|
139
|
+
trimmedHeight,
|
|
140
|
+
0,
|
|
141
|
+
0,
|
|
142
|
+
trimmedWidth,
|
|
143
|
+
trimmedHeight,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
return trimmedCanvas.toDataURL("image/png");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function loadImage(dataUrl: string): Promise<HTMLImageElement> {
|
|
150
|
+
return new Promise((resolve, reject) => {
|
|
151
|
+
const image = new Image();
|
|
152
|
+
image.onload = () => resolve(image);
|
|
153
|
+
image.onerror = () => reject(new Error("Failed to load SF Symbol PNG data."));
|
|
154
|
+
image.src = dataUrl;
|
|
155
|
+
});
|
|
156
|
+
}
|