@canopy-iiif/app 1.2.4 → 1.2.5

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 (2) hide show
  1. package/package.json +1 -1
  2. package/ui/theme.js +102 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canopy-iiif/app",
3
- "version": "1.2.4",
3
+ "version": "1.2.5",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "author": "Mat Jordan <mat@northwestern.edu>",
package/ui/theme.js CHANGED
@@ -116,6 +116,87 @@ function lightenHex(hex, amount = 0.15) {
116
116
  return `#${toHex(adjust(r))}${toHex(adjust(g))}${toHex(adjust(b))}`;
117
117
  }
118
118
 
119
+ function adjustSaturation(hex, amount = 0.15) {
120
+ if (!hex) return hex;
121
+ const normalized = hex.replace("#", "");
122
+ if (!/^[0-9a-fA-F]{6}$/.test(normalized)) return hex;
123
+ const num = parseInt(normalized, 16);
124
+ let r = ((num >> 16) & 255) / 255;
125
+ let g = ((num >> 8) & 255) / 255;
126
+ let b = (num & 255) / 255;
127
+ const max = Math.max(r, g, b);
128
+ const min = Math.min(r, g, b);
129
+ let h = 0;
130
+ let s = 0;
131
+ const l = (max + min) / 2;
132
+ if (max !== min) {
133
+ const d = max - min;
134
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
135
+ switch (max) {
136
+ case r:
137
+ h = (g - b) / d + (g < b ? 6 : 0);
138
+ break;
139
+ case g:
140
+ h = (b - r) / d + 2;
141
+ break;
142
+ default:
143
+ h = (r - g) / d + 4;
144
+ }
145
+ h /= 6;
146
+ }
147
+ const delta = Number(amount);
148
+ if (!Number.isFinite(delta) || delta === 0) return hex;
149
+ s = Math.max(0, Math.min(1, s + delta));
150
+ const hueToRgb = (p, q, t) => {
151
+ if (t < 0) t += 1;
152
+ if (t > 1) t -= 1;
153
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
154
+ if (t < 1 / 2) return q;
155
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
156
+ return p;
157
+ };
158
+ let rOut;
159
+ let gOut;
160
+ let bOut;
161
+ if (s === 0) {
162
+ rOut = gOut = bOut = l;
163
+ } else {
164
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
165
+ const p = 2 * l - q;
166
+ rOut = hueToRgb(p, q, h + 1 / 3);
167
+ gOut = hueToRgb(p, q, h);
168
+ bOut = hueToRgb(p, q, h - 1 / 3);
169
+ }
170
+ const toHex = (value) =>
171
+ Math.round(Math.max(0, Math.min(1, value)) * 255)
172
+ .toString(16)
173
+ .padStart(2, "0");
174
+ return `#${toHex(rOut)}${toHex(gOut)}${toHex(bOut)}`;
175
+ }
176
+
177
+ function mixHexColors(colorA, colorB, amount = 0.5) {
178
+ const normalize = (hex) =>
179
+ hex && /^[0-9a-fA-F]{6}$/.test(hex.replace("#", ""))
180
+ ? hex.replace("#", "")
181
+ : null;
182
+ const first = normalize(colorA);
183
+ const second = normalize(colorB);
184
+ if (!first || !second) return colorA || colorB || null;
185
+ const a = parseInt(first, 16);
186
+ const b = parseInt(second, 16);
187
+ const clampAmount = Math.max(0, Math.min(1, Number(amount) || 0));
188
+ const mixChannel = (shift) =>
189
+ Math.round(
190
+ ((a >> shift) & 255) +
191
+ (((b >> shift) & 255) - ((a >> shift) & 255)) * clampAmount
192
+ );
193
+ const toHex = (value) => value.toString(16).padStart(2, "0");
194
+ const r = mixChannel(16);
195
+ const g = mixChannel(8);
196
+ const bl = mixChannel(0);
197
+ return `#${toHex(r)}${toHex(g)}${toHex(bl)}`;
198
+ }
199
+
119
200
  function normalizeDarkenAmount(raw) {
120
201
  const value = Number(raw);
121
202
  if (!Number.isFinite(value)) return null;
@@ -138,6 +219,18 @@ function toTailwindScale(name, options = {}) {
138
219
  if (!value) return null;
139
220
  scale[lvl] = value;
140
221
  }
222
+ if (scale["50"] && scale["100"]) {
223
+ scale["50"] = mixHexColors(scale["50"], scale["100"], 0.6);
224
+ }
225
+ const saturate700 = options.saturate700 !== false;
226
+ if (scale["700"]) {
227
+ let adjusted =
228
+ appearance === "dark"
229
+ ? lightenHex(scale["700"], 0.15)
230
+ : darkenHex(scale["700"], 0.15);
231
+ if (saturate700) adjusted = adjustSaturation(adjusted, 0.15);
232
+ scale["700"] = adjusted;
233
+ }
141
234
  const darkestKey = `${prefix}${steps["900"]}`;
142
235
  if (scale["800"] && palette[darkestKey]) {
143
236
  const amount = darken900Amount != null ? darken900Amount : 0.25;
@@ -146,6 +239,9 @@ function toTailwindScale(name, options = {}) {
146
239
  ? lightenHex(palette[darkestKey], amount)
147
240
  : darkenHex(palette[darkestKey], amount);
148
241
  }
242
+ if (scale["800"] && scale["900"]) {
243
+ scale["800"] = mixHexColors(scale["800"], scale["900"], 0.65);
244
+ }
149
245
  return scale;
150
246
  }
151
247
 
@@ -226,7 +322,11 @@ function loadCanopyTheme(options = {}) {
226
322
 
227
323
  let grayName = normalizePaletteName(grayRequested);
228
324
  let grayScale = grayName
229
- ? toTailwindScale(grayName, {appearance, darken900Amount: 0.4})
325
+ ? toTailwindScale(grayName, {
326
+ appearance,
327
+ darken900Amount: 0.4,
328
+ saturate700: false,
329
+ })
230
330
  : null;
231
331
  let grayFallback = false;
232
332
  if (!grayScale) {
@@ -235,6 +335,7 @@ function loadCanopyTheme(options = {}) {
235
335
  grayScale = toTailwindScale(DEFAULT_GRAY, {
236
336
  appearance,
237
337
  darken900Amount: 0.4,
338
+ saturate700: false,
238
339
  });
239
340
  }
240
341