@canopy-iiif/app 0.10.31 → 0.11.0
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/lib/build/dev.js +109 -11
- package/lib/build/mdx.js +10 -61
- package/lib/components/slider-runtime-entry.js +86 -0
- package/package.json +1 -1
- package/ui/dist/index.mjs +126 -5
- package/ui/dist/index.mjs.map +4 -4
- package/ui/dist/server.mjs +256 -67
- package/ui/dist/server.mjs.map +4 -4
- package/ui/styles/components/_interstitial-hero.scss +37 -48
- package/ui/styles/index.css +30 -31
package/lib/build/dev.js
CHANGED
|
@@ -3,6 +3,12 @@ const fsp = fs.promises;
|
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const { spawn, spawnSync } = require("child_process");
|
|
5
5
|
const http = require("http");
|
|
6
|
+
let sass = null;
|
|
7
|
+
try {
|
|
8
|
+
sass = require("sass");
|
|
9
|
+
} catch (_) {
|
|
10
|
+
sass = null;
|
|
11
|
+
}
|
|
6
12
|
const {
|
|
7
13
|
CONTENT_DIR,
|
|
8
14
|
OUT_DIR,
|
|
@@ -29,6 +35,13 @@ const UI_DIST_DIR = path.resolve(path.join(__dirname, "../../ui/dist"));
|
|
|
29
35
|
const APP_PACKAGE_ROOT = path.resolve(path.join(__dirname, "..", ".."));
|
|
30
36
|
const APP_LIB_DIR = path.join(APP_PACKAGE_ROOT, "lib");
|
|
31
37
|
const APP_UI_DIR = path.join(APP_PACKAGE_ROOT, "ui");
|
|
38
|
+
let loadUiTheme = null;
|
|
39
|
+
try {
|
|
40
|
+
const uiTheme = require(path.join(APP_UI_DIR, "theme.js"));
|
|
41
|
+
if (uiTheme && typeof uiTheme.loadCanopyTheme === "function") {
|
|
42
|
+
loadUiTheme = uiTheme.loadCanopyTheme;
|
|
43
|
+
}
|
|
44
|
+
} catch (_) {}
|
|
32
45
|
const APP_WATCH_TARGETS = [
|
|
33
46
|
{ dir: APP_LIB_DIR, label: "@canopy-iiif/app/lib" },
|
|
34
47
|
{ dir: APP_UI_DIR, label: "@canopy-iiif/app/ui" },
|
|
@@ -1025,6 +1038,7 @@ async function dev() {
|
|
|
1025
1038
|
);
|
|
1026
1039
|
const uiStylesDir = path.join(APP_UI_DIR, "styles");
|
|
1027
1040
|
const uiStylesCss = path.join(uiStylesDir, "index.css");
|
|
1041
|
+
const uiStylesEntry = path.join(uiStylesDir, "index.scss");
|
|
1028
1042
|
const pluginFiles = [uiPlugin, uiPreset].filter((p) => {
|
|
1029
1043
|
try {
|
|
1030
1044
|
return fs.existsSync(p);
|
|
@@ -1056,6 +1070,36 @@ async function dev() {
|
|
|
1056
1070
|
});
|
|
1057
1071
|
} catch (_) {}
|
|
1058
1072
|
}
|
|
1073
|
+
const rebuildUiStyles = () => {
|
|
1074
|
+
if (!sass || !fs.existsSync(uiStylesEntry)) return false;
|
|
1075
|
+
try {
|
|
1076
|
+
const theme = loadUiTheme ? loadUiTheme({ cwd: process.cwd() }) : null;
|
|
1077
|
+
const source = `${
|
|
1078
|
+
theme && theme.sassConfig ? theme.sassConfig : ""
|
|
1079
|
+
}@use 'index';`;
|
|
1080
|
+
const result = sass.compileString(source, {
|
|
1081
|
+
loadPaths: [uiStylesDir],
|
|
1082
|
+
style: "expanded",
|
|
1083
|
+
});
|
|
1084
|
+
let cssOutput = result && result.css ? result.css : "";
|
|
1085
|
+
const themeCss = theme && theme.css ? theme.css.trim() : "";
|
|
1086
|
+
if (themeCss) {
|
|
1087
|
+
cssOutput = `/* canopy-theme */\n${themeCss}\n/* canopy-theme:end */\n${cssOutput}`;
|
|
1088
|
+
}
|
|
1089
|
+
fs.writeFileSync(uiStylesCss, cssOutput, "utf8");
|
|
1090
|
+
console.log(
|
|
1091
|
+
"[tailwind] rebuilt @canopy-iiif/app/ui styles →",
|
|
1092
|
+
prettyPath(uiStylesCss)
|
|
1093
|
+
);
|
|
1094
|
+
return true;
|
|
1095
|
+
} catch (err) {
|
|
1096
|
+
console.warn(
|
|
1097
|
+
"[tailwind] failed to rebuild @canopy-iiif/app/ui styles:",
|
|
1098
|
+
err && err.message ? err.message : err
|
|
1099
|
+
);
|
|
1100
|
+
return false;
|
|
1101
|
+
}
|
|
1102
|
+
};
|
|
1059
1103
|
const attachCssWatcher = () => {
|
|
1060
1104
|
if (uiCssWatcherAttached) {
|
|
1061
1105
|
if (fs.existsSync(uiStylesCss)) return;
|
|
@@ -1080,11 +1124,15 @@ async function dev() {
|
|
|
1080
1124
|
} catch (_) {}
|
|
1081
1125
|
}
|
|
1082
1126
|
};
|
|
1127
|
+
if (fs.existsSync(uiStylesEntry)) rebuildUiStyles();
|
|
1083
1128
|
attachCssWatcher();
|
|
1084
1129
|
const handleUiSassChange = () => {
|
|
1130
|
+
const ok = rebuildUiStyles();
|
|
1085
1131
|
attachCssWatcher();
|
|
1086
1132
|
scheduleTailwindRestart(
|
|
1087
|
-
|
|
1133
|
+
ok
|
|
1134
|
+
? "[tailwind] detected @canopy-iiif/app/ui Sass change — restarting Tailwind"
|
|
1135
|
+
: "[tailwind] Sass compile failed — attempting Tailwind restart",
|
|
1088
1136
|
"[tailwind] compile after UI Sass change failed"
|
|
1089
1137
|
);
|
|
1090
1138
|
};
|
|
@@ -1148,16 +1196,66 @@ async function dev() {
|
|
|
1148
1196
|
const stylesDir = path.dirname(inputCss);
|
|
1149
1197
|
if (stylesDir && stylesDir.includes(path.join("app", "styles"))) {
|
|
1150
1198
|
let cssDebounce = null;
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1199
|
+
const triggerCssRebuild = () => {
|
|
1200
|
+
clearTimeout(cssDebounce);
|
|
1201
|
+
cssDebounce = setTimeout(() => {
|
|
1202
|
+
try { onBuildStart(); } catch (_) {}
|
|
1203
|
+
safeCompile("[tailwind] compile after CSS change failed");
|
|
1204
|
+
try { onCssChange(); } catch (_) {}
|
|
1205
|
+
}, 50);
|
|
1206
|
+
};
|
|
1207
|
+
const shouldHandle = (filename) => {
|
|
1208
|
+
if (!filename) return true;
|
|
1209
|
+
return /\.(css|s[ac]ss)$/i.test(String(filename));
|
|
1210
|
+
};
|
|
1211
|
+
const attachRecursiveWatch = () => {
|
|
1212
|
+
try {
|
|
1213
|
+
fs.watch(
|
|
1214
|
+
stylesDir,
|
|
1215
|
+
{ persistent: false, recursive: true },
|
|
1216
|
+
(evt, fn) => {
|
|
1217
|
+
if (!shouldHandle(fn)) return;
|
|
1218
|
+
triggerCssRebuild();
|
|
1219
|
+
}
|
|
1220
|
+
);
|
|
1221
|
+
return true;
|
|
1222
|
+
} catch (_) {
|
|
1223
|
+
return false;
|
|
1224
|
+
}
|
|
1225
|
+
};
|
|
1226
|
+
if (!attachRecursiveWatch()) {
|
|
1227
|
+
const watchers = new Map();
|
|
1228
|
+
function watchDir(dir) {
|
|
1229
|
+
if (watchers.has(dir)) return;
|
|
1230
|
+
try {
|
|
1231
|
+
const watcher = fs.watch(
|
|
1232
|
+
dir,
|
|
1233
|
+
{ persistent: false },
|
|
1234
|
+
(evt, fn) => {
|
|
1235
|
+
if (shouldHandle(fn)) triggerCssRebuild();
|
|
1236
|
+
scanDir(dir);
|
|
1237
|
+
}
|
|
1238
|
+
);
|
|
1239
|
+
watchers.set(dir, watcher);
|
|
1240
|
+
} catch (_) {}
|
|
1241
|
+
}
|
|
1242
|
+
function scanDir(dir) {
|
|
1243
|
+
let entries = [];
|
|
1244
|
+
try {
|
|
1245
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
1246
|
+
} catch (_) {
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
1249
|
+
for (const entry of entries) {
|
|
1250
|
+
if (!entry || !entry.isDirectory()) continue;
|
|
1251
|
+
const next = path.join(dir, entry.name);
|
|
1252
|
+
watchDir(next);
|
|
1253
|
+
scanDir(next);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
watchDir(stylesDir);
|
|
1257
|
+
scanDir(stylesDir);
|
|
1258
|
+
}
|
|
1161
1259
|
}
|
|
1162
1260
|
} catch (err) {
|
|
1163
1261
|
console.error("[tailwind] setup failed:", err && err.message ? err.message : err);
|
package/lib/build/mdx.js
CHANGED
|
@@ -821,6 +821,11 @@ async function ensureFacetsRuntime() {
|
|
|
821
821
|
const entry = `
|
|
822
822
|
function ready(fn){ if(document.readyState==='loading') document.addEventListener('DOMContentLoaded',fn,{once:true}); else fn(); }
|
|
823
823
|
function parseProps(el){ try{ const s=el.querySelector('script[type="application/json"]'); if(s) return JSON.parse(s.textContent||'{}'); }catch(_){ } return {}; }
|
|
824
|
+
function buildSliderProps(iiifContent, options){
|
|
825
|
+
const props = { iiifContent };
|
|
826
|
+
if (options && typeof options === 'object') props.options = options;
|
|
827
|
+
return props;
|
|
828
|
+
}
|
|
824
829
|
function rootBase(){
|
|
825
830
|
try {
|
|
826
831
|
var bp = (window && window.CANOPY_BASE_PATH) ? String(window.CANOPY_BASE_PATH) : '';
|
|
@@ -851,6 +856,7 @@ async function ensureFacetsRuntime() {
|
|
|
851
856
|
const topN = Number(props.top || 3) || 3;
|
|
852
857
|
const ver = await getApiVersion();
|
|
853
858
|
const verQ = ver ? ('?v=' + encodeURIComponent(ver)) : '';
|
|
859
|
+
const sliderOptions = (props && typeof props.sliderOptions === 'object') ? props.sliderOptions : null;
|
|
854
860
|
const res = await fetch(rootBase() + '/api/search/facets.json' + verQ).catch(()=>null);
|
|
855
861
|
if(!res || !res.ok) return;
|
|
856
862
|
const json = await res.json().catch(()=>null);
|
|
@@ -873,7 +879,7 @@ async function ensureFacetsRuntime() {
|
|
|
873
879
|
const wrap = document.createElement('div');
|
|
874
880
|
wrap.setAttribute('data-facet-label', entry.label);
|
|
875
881
|
wrap.setAttribute('class', 'canopy-slider');
|
|
876
|
-
const ph = makeSliderPlaceholder(
|
|
882
|
+
const ph = makeSliderPlaceholder(buildSliderProps(rootBase() + '/api/facet/' + (allow.slug) + '/' + pick.valueSlug + '.json' + verQ, sliderOptions));
|
|
877
883
|
if (ph) wrap.appendChild(ph);
|
|
878
884
|
el.appendChild(wrap);
|
|
879
885
|
});
|
|
@@ -887,7 +893,7 @@ async function ensureFacetsRuntime() {
|
|
|
887
893
|
const wrap = document.createElement('div');
|
|
888
894
|
wrap.setAttribute('data-facet-label', s.label);
|
|
889
895
|
wrap.setAttribute('class', 'canopy-slider');
|
|
890
|
-
const ph = makeSliderPlaceholder(
|
|
896
|
+
const ph = makeSliderPlaceholder(buildSliderProps(rootBase() + '/api/facet/' + s.labelSlug + '/' + s.valueSlug + '.json' + verQ, sliderOptions));
|
|
891
897
|
if (ph) wrap.appendChild(ph);
|
|
892
898
|
el.appendChild(wrap);
|
|
893
899
|
});
|
|
@@ -949,59 +955,7 @@ async function ensureSliderRuntime() {
|
|
|
949
955
|
const scriptsDir = path.join(OUT_DIR, "scripts");
|
|
950
956
|
ensureDirSync(scriptsDir);
|
|
951
957
|
const outFile = path.join(scriptsDir, "canopy-slider.js");
|
|
952
|
-
const
|
|
953
|
-
import CloverSlider from '@samvera/clover-iiif/slider';
|
|
954
|
-
import 'swiper/css';
|
|
955
|
-
import 'swiper/css/navigation';
|
|
956
|
-
import 'swiper/css/pagination';
|
|
957
|
-
|
|
958
|
-
function ready(fn) {
|
|
959
|
-
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', fn, { once: true });
|
|
960
|
-
else fn();
|
|
961
|
-
}
|
|
962
|
-
function parseProps(el) {
|
|
963
|
-
try {
|
|
964
|
-
const s = el.querySelector('script[type="application/json"]');
|
|
965
|
-
if (s) return JSON.parse(s.textContent || '{}');
|
|
966
|
-
const raw = el.getAttribute('data-props') || '{}';
|
|
967
|
-
return JSON.parse(raw);
|
|
968
|
-
} catch (_) { return {}; }
|
|
969
|
-
}
|
|
970
|
-
function mount(el){
|
|
971
|
-
try{
|
|
972
|
-
if (!el || el.getAttribute('data-canopy-slider-mounted')==='1') return;
|
|
973
|
-
const React = (window && window.React) || null;
|
|
974
|
-
const ReactDOMClient = (window && window.ReactDOMClient) || null;
|
|
975
|
-
const createRoot = ReactDOMClient && ReactDOMClient.createRoot;
|
|
976
|
-
if (!React || !createRoot) return;
|
|
977
|
-
const props = parseProps(el);
|
|
978
|
-
const root = createRoot(el);
|
|
979
|
-
root.render(React.createElement(CloverSlider, props));
|
|
980
|
-
el.setAttribute('data-canopy-slider-mounted','1');
|
|
981
|
-
} catch(_){}
|
|
982
|
-
}
|
|
983
|
-
function scan(){
|
|
984
|
-
try{ document.querySelectorAll('[data-canopy-slider]:not([data-canopy-slider-mounted="1"])').forEach(mount); }catch(_){ }
|
|
985
|
-
}
|
|
986
|
-
function observe(){
|
|
987
|
-
try{
|
|
988
|
-
const obs = new MutationObserver((muts)=>{
|
|
989
|
-
const toMount = [];
|
|
990
|
-
for (const m of muts){
|
|
991
|
-
m.addedNodes && m.addedNodes.forEach((n)=>{
|
|
992
|
-
if (!(n instanceof Element)) return;
|
|
993
|
-
if (n.matches && n.matches('[data-canopy-slider]')) toMount.push(n);
|
|
994
|
-
const inner = n.querySelectorAll ? n.querySelectorAll('[data-canopy-slider]') : [];
|
|
995
|
-
inner && inner.forEach && inner.forEach((x)=> toMount.push(x));
|
|
996
|
-
});
|
|
997
|
-
}
|
|
998
|
-
if (toMount.length) Promise.resolve().then(()=> toMount.forEach(mount));
|
|
999
|
-
});
|
|
1000
|
-
obs.observe(document.documentElement || document.body, { childList: true, subtree: true });
|
|
1001
|
-
}catch(_){ }
|
|
1002
|
-
}
|
|
1003
|
-
ready(function(){ scan(); observe(); });
|
|
1004
|
-
`;
|
|
958
|
+
const entryFile = path.join(__dirname, "../components/slider-runtime-entry.js");
|
|
1005
959
|
const reactShim = `
|
|
1006
960
|
const React = (typeof window !== 'undefined' && window.React) || {};
|
|
1007
961
|
export default React;
|
|
@@ -1079,12 +1033,7 @@ async function ensureSliderRuntime() {
|
|
|
1079
1033
|
};
|
|
1080
1034
|
try {
|
|
1081
1035
|
await esbuild.build({
|
|
1082
|
-
|
|
1083
|
-
contents: entry,
|
|
1084
|
-
resolveDir: process.cwd(),
|
|
1085
|
-
sourcefile: "canopy-slider-entry.js",
|
|
1086
|
-
loader: "js",
|
|
1087
|
-
},
|
|
1036
|
+
entryPoints: [entryFile],
|
|
1088
1037
|
outfile: outFile,
|
|
1089
1038
|
platform: "browser",
|
|
1090
1039
|
format: "iife",
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { createRoot } from 'react-dom/client';
|
|
3
|
+
import CloverSlider from '@samvera/clover-iiif/slider';
|
|
4
|
+
import 'swiper/css';
|
|
5
|
+
import 'swiper/css/navigation';
|
|
6
|
+
import 'swiper/css/pagination';
|
|
7
|
+
import { mergeSliderOptions, normalizeSliderOptions } from '../../ui/src/iiif/sliderOptions.js';
|
|
8
|
+
|
|
9
|
+
function ready(fn) {
|
|
10
|
+
if (document.readyState === 'loading') {
|
|
11
|
+
document.addEventListener('DOMContentLoaded', fn, { once: true });
|
|
12
|
+
} else {
|
|
13
|
+
fn();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function parseProps(el) {
|
|
18
|
+
try {
|
|
19
|
+
const script = el.querySelector('script[type="application/json"]');
|
|
20
|
+
if (script) return JSON.parse(script.textContent || '{}');
|
|
21
|
+
const raw = el.getAttribute('data-props') || '{}';
|
|
22
|
+
return JSON.parse(raw);
|
|
23
|
+
} catch (_) {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function withDefaults(rawProps) {
|
|
29
|
+
const props = rawProps && typeof rawProps === 'object' ? rawProps : {};
|
|
30
|
+
const className = ['canopy-slider', props.className]
|
|
31
|
+
.filter(Boolean)
|
|
32
|
+
.join(' ');
|
|
33
|
+
const mergedOptions = mergeSliderOptions(props.options);
|
|
34
|
+
return {
|
|
35
|
+
...props,
|
|
36
|
+
className,
|
|
37
|
+
options: normalizeSliderOptions(mergedOptions),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function mount(el) {
|
|
42
|
+
try {
|
|
43
|
+
if (!el || el.getAttribute('data-canopy-slider-mounted') === '1') return;
|
|
44
|
+
const props = withDefaults(parseProps(el));
|
|
45
|
+
const root = createRoot(el);
|
|
46
|
+
root.render(React.createElement(CloverSlider, props));
|
|
47
|
+
el.setAttribute('data-canopy-slider-mounted', '1');
|
|
48
|
+
} catch (_) {}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function scan() {
|
|
52
|
+
try {
|
|
53
|
+
document
|
|
54
|
+
.querySelectorAll('[data-canopy-slider]:not([data-canopy-slider-mounted="1"])')
|
|
55
|
+
.forEach(mount);
|
|
56
|
+
} catch (_) {}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function observe() {
|
|
60
|
+
try {
|
|
61
|
+
const obs = new MutationObserver((mutations) => {
|
|
62
|
+
const toMount = [];
|
|
63
|
+
mutations.forEach((mutation) => {
|
|
64
|
+
mutation.addedNodes &&
|
|
65
|
+
mutation.addedNodes.forEach((node) => {
|
|
66
|
+
if (!(node instanceof Element)) return;
|
|
67
|
+
if (node.matches && node.matches('[data-canopy-slider]')) toMount.push(node);
|
|
68
|
+
const inner = node.querySelectorAll
|
|
69
|
+
? node.querySelectorAll('[data-canopy-slider]')
|
|
70
|
+
: [];
|
|
71
|
+
inner && inner.forEach && inner.forEach((x) => toMount.push(x));
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
if (toMount.length) Promise.resolve().then(() => toMount.forEach(mount));
|
|
75
|
+
});
|
|
76
|
+
obs.observe(document.documentElement || document.body, {
|
|
77
|
+
childList: true,
|
|
78
|
+
subtree: true,
|
|
79
|
+
});
|
|
80
|
+
} catch (_) {}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
ready(function onReady() {
|
|
84
|
+
scan();
|
|
85
|
+
observe();
|
|
86
|
+
});
|
package/package.json
CHANGED
package/ui/dist/index.mjs
CHANGED
|
@@ -1241,7 +1241,120 @@ var Viewer = (props) => {
|
|
|
1241
1241
|
|
|
1242
1242
|
// ui/src/iiif/Slider.jsx
|
|
1243
1243
|
import React17, { useEffect as useEffect3, useState as useState3 } from "react";
|
|
1244
|
-
|
|
1244
|
+
|
|
1245
|
+
// ui/src/iiif/sliderOptions.js
|
|
1246
|
+
var UNIT_TOKEN = "__canopySliderUnit";
|
|
1247
|
+
var UNIT_REM = "rem";
|
|
1248
|
+
var DEFAULT_FONT_SIZE = 16;
|
|
1249
|
+
var cachedRootFontSize = null;
|
|
1250
|
+
var sliderOptions = {
|
|
1251
|
+
breakpoints: {
|
|
1252
|
+
400: {
|
|
1253
|
+
slidesPerView: 2,
|
|
1254
|
+
spaceBetween: rem(1)
|
|
1255
|
+
},
|
|
1256
|
+
640: {
|
|
1257
|
+
slidesPerView: 3,
|
|
1258
|
+
spaceBetween: rem(1.618)
|
|
1259
|
+
},
|
|
1260
|
+
1024: {
|
|
1261
|
+
slidesPerView: 4,
|
|
1262
|
+
spaceBetween: rem(1.618)
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
};
|
|
1266
|
+
function rem(value) {
|
|
1267
|
+
const numeric = typeof value === "number" ? value : parseFloat(value);
|
|
1268
|
+
return {
|
|
1269
|
+
[UNIT_TOKEN]: UNIT_REM,
|
|
1270
|
+
value: Number.isFinite(numeric) ? numeric : 0
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
function cloneBreakpoints(source) {
|
|
1274
|
+
if (!source || typeof source !== "object") return void 0;
|
|
1275
|
+
const clone = {};
|
|
1276
|
+
Object.entries(source).forEach(([key, entry]) => {
|
|
1277
|
+
clone[key] = entry && typeof entry === "object" ? { ...entry } : {};
|
|
1278
|
+
});
|
|
1279
|
+
return clone;
|
|
1280
|
+
}
|
|
1281
|
+
function cloneOptions(options = {}) {
|
|
1282
|
+
const clone = { ...options };
|
|
1283
|
+
if (options.breakpoints && typeof options.breakpoints === "object") {
|
|
1284
|
+
clone.breakpoints = cloneBreakpoints(options.breakpoints);
|
|
1285
|
+
}
|
|
1286
|
+
return clone;
|
|
1287
|
+
}
|
|
1288
|
+
function mergeSliderOptions(overrides) {
|
|
1289
|
+
const base = cloneOptions(sliderOptions);
|
|
1290
|
+
const incoming = cloneOptions(overrides || {});
|
|
1291
|
+
const merged = {
|
|
1292
|
+
...base,
|
|
1293
|
+
...incoming
|
|
1294
|
+
};
|
|
1295
|
+
if (base.breakpoints || incoming.breakpoints) {
|
|
1296
|
+
merged.breakpoints = {
|
|
1297
|
+
...base.breakpoints || {},
|
|
1298
|
+
...incoming.breakpoints || {}
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
return merged;
|
|
1302
|
+
}
|
|
1303
|
+
function hasBrowserEnv() {
|
|
1304
|
+
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
1305
|
+
}
|
|
1306
|
+
function measureRootFontSize() {
|
|
1307
|
+
if (!hasBrowserEnv()) return DEFAULT_FONT_SIZE;
|
|
1308
|
+
if (cachedRootFontSize !== null) return cachedRootFontSize;
|
|
1309
|
+
let size = DEFAULT_FONT_SIZE;
|
|
1310
|
+
try {
|
|
1311
|
+
const root = window.document && window.document.documentElement;
|
|
1312
|
+
if (root && window.getComputedStyle) {
|
|
1313
|
+
const computed = window.getComputedStyle(root).fontSize;
|
|
1314
|
+
const parsed = parseFloat(computed);
|
|
1315
|
+
if (Number.isFinite(parsed)) size = parsed;
|
|
1316
|
+
}
|
|
1317
|
+
} catch (_) {
|
|
1318
|
+
size = DEFAULT_FONT_SIZE;
|
|
1319
|
+
}
|
|
1320
|
+
cachedRootFontSize = size;
|
|
1321
|
+
return size;
|
|
1322
|
+
}
|
|
1323
|
+
function convertSpacing(value) {
|
|
1324
|
+
if (!hasBrowserEnv()) return value;
|
|
1325
|
+
if (value && typeof value === "object" && value[UNIT_TOKEN] === UNIT_REM) {
|
|
1326
|
+
const remValue = typeof value.value === "number" ? value.value : parseFloat(value.value);
|
|
1327
|
+
if (!Number.isFinite(remValue)) return value;
|
|
1328
|
+
return remValue * measureRootFontSize();
|
|
1329
|
+
}
|
|
1330
|
+
return value;
|
|
1331
|
+
}
|
|
1332
|
+
function normalizeBreakpoints(breakpoints) {
|
|
1333
|
+
if (!breakpoints || typeof breakpoints !== "object") return breakpoints;
|
|
1334
|
+
const normalized = {};
|
|
1335
|
+
Object.entries(breakpoints).forEach(([key, entry]) => {
|
|
1336
|
+
const clone = entry && typeof entry === "object" ? { ...entry } : {};
|
|
1337
|
+
if (Object.prototype.hasOwnProperty.call(clone, "spaceBetween")) {
|
|
1338
|
+
clone.spaceBetween = convertSpacing(clone.spaceBetween);
|
|
1339
|
+
}
|
|
1340
|
+
normalized[key] = clone;
|
|
1341
|
+
});
|
|
1342
|
+
return normalized;
|
|
1343
|
+
}
|
|
1344
|
+
function normalizeSliderOptions(options) {
|
|
1345
|
+
const clone = cloneOptions(options || {});
|
|
1346
|
+
if (!hasBrowserEnv()) return clone;
|
|
1347
|
+
if (Object.prototype.hasOwnProperty.call(clone, "spaceBetween")) {
|
|
1348
|
+
clone.spaceBetween = convertSpacing(clone.spaceBetween);
|
|
1349
|
+
}
|
|
1350
|
+
if (clone.breakpoints) {
|
|
1351
|
+
clone.breakpoints = normalizeBreakpoints(clone.breakpoints);
|
|
1352
|
+
}
|
|
1353
|
+
return clone;
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
// ui/src/iiif/Slider.jsx
|
|
1357
|
+
var Slider = (props = {}) => {
|
|
1245
1358
|
const [CloverSlider, setCloverSlider] = useState3(null);
|
|
1246
1359
|
useEffect3(() => {
|
|
1247
1360
|
let mounted = true;
|
|
@@ -1249,7 +1362,6 @@ var Slider = (props) => {
|
|
|
1249
1362
|
if (canUseDom) {
|
|
1250
1363
|
import("@samvera/clover-iiif/slider").then((mod) => {
|
|
1251
1364
|
if (!mounted) return;
|
|
1252
|
-
console.log(mod);
|
|
1253
1365
|
const Comp = mod && (mod.default || mod.Slider || mod);
|
|
1254
1366
|
setCloverSlider(() => Comp);
|
|
1255
1367
|
}).catch(() => {
|
|
@@ -1259,14 +1371,23 @@ var Slider = (props) => {
|
|
|
1259
1371
|
mounted = false;
|
|
1260
1372
|
};
|
|
1261
1373
|
}, []);
|
|
1374
|
+
const { className, ...rest } = props || {};
|
|
1375
|
+
const sliderClassName = ["canopy-slider", className].filter(Boolean).join(" ");
|
|
1376
|
+
const mergedOptions = mergeSliderOptions(rest.options);
|
|
1377
|
+
const normalizedOptions = normalizeSliderOptions(mergedOptions);
|
|
1378
|
+
const resolvedProps = {
|
|
1379
|
+
...rest,
|
|
1380
|
+
className: sliderClassName,
|
|
1381
|
+
options: normalizedOptions
|
|
1382
|
+
};
|
|
1262
1383
|
if (!CloverSlider) {
|
|
1263
1384
|
let json = "{}";
|
|
1264
1385
|
try {
|
|
1265
|
-
json = JSON.stringify(
|
|
1386
|
+
json = JSON.stringify(resolvedProps || {});
|
|
1266
1387
|
} catch (_) {
|
|
1267
1388
|
json = "{}";
|
|
1268
1389
|
}
|
|
1269
|
-
return /* @__PURE__ */ React17.createElement("div", { className:
|
|
1390
|
+
return /* @__PURE__ */ React17.createElement("div", { className: sliderClassName, "data-canopy-slider": "1" }, /* @__PURE__ */ React17.createElement(
|
|
1270
1391
|
"script",
|
|
1271
1392
|
{
|
|
1272
1393
|
type: "application/json",
|
|
@@ -1274,7 +1395,7 @@ var Slider = (props) => {
|
|
|
1274
1395
|
}
|
|
1275
1396
|
));
|
|
1276
1397
|
}
|
|
1277
|
-
return /* @__PURE__ */ React17.createElement(CloverSlider, { ...
|
|
1398
|
+
return /* @__PURE__ */ React17.createElement(CloverSlider, { ...resolvedProps });
|
|
1278
1399
|
};
|
|
1279
1400
|
|
|
1280
1401
|
// ui/src/iiif/Scroll.jsx
|