chart-factory 0.1.2
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/LICENSE +21 -0
- package/README.md +162 -0
- package/dist/chart-factory-table.js +1400 -0
- package/dist/chart-factory.css +2368 -0
- package/dist/chart-factory.js +11398 -0
- package/dist/chart-factory.min.js +7 -0
- package/docs/API.md +1815 -0
- package/docs/chart-guide.md +872 -0
- package/docs/color-guide.md +134 -0
- package/docs/conformance-matrix.md +864 -0
- package/docs/theming.md +107 -0
- package/index.d.ts +10453 -0
- package/package.json +83 -0
- package/src/core/base.css +508 -0
- package/src/core/builder-metadata.generated.mjs +406 -0
- package/src/core/d3-base.js +249 -0
- package/src/core/d3-chart-factory.js +18547 -0
- package/src/core/d3-data.js +0 -0
- package/src/core/d3-suggest.js +565 -0
- package/src/core/example-nav.css +62 -0
- package/src/core/example-nav.js +249 -0
- package/src/core/palettes.js +310 -0
- package/src/core/theme-toggle.js +126 -0
- package/src/core/tokens.css +792 -0
- package/src/table/d3-table.js +1570 -0
- package/src/table/table.css +899 -0
- package/src/table/tokens.css +157 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chart Factory - Example-page dark mode toggle
|
|
3
|
+
*
|
|
4
|
+
* Include on any example page AFTER the ChartFactory bundle but BEFORE the
|
|
5
|
+
* page's chart-creation script:
|
|
6
|
+
*
|
|
7
|
+
* <script src="../../src/core/theme-toggle.js"></script>
|
|
8
|
+
*
|
|
9
|
+
* At parse time it applies the persisted theme to <html data-theme> — before
|
|
10
|
+
* any chart renders, so there is no light-then-dark flash. It then mounts a
|
|
11
|
+
* floating sun/moon button (top-right; dropped below the example-nav bar
|
|
12
|
+
* when one is present). Toggling flips the attribute, persists the choice in
|
|
13
|
+
* localStorage, calls ChartFactory.rerenderAll() so every live chart and
|
|
14
|
+
* table re-resolves its token colors, and dispatches a 'd3t:themechange'
|
|
15
|
+
* window event for anything with bespoke re-render needs.
|
|
16
|
+
*
|
|
17
|
+
* Cross-context sync: a 'storage' listener applies theme changes made by
|
|
18
|
+
* OTHER documents sharing this localStorage — so a showcase's modal iframe
|
|
19
|
+
* follows the parent page's toggle live, and so do other open tabs.
|
|
20
|
+
*
|
|
21
|
+
* In ?modal=1 embeds the button is hidden but the stored theme still
|
|
22
|
+
* applies, so modal iframes match the parent page from first paint.
|
|
23
|
+
*/
|
|
24
|
+
(function () {
|
|
25
|
+
var STORAGE_KEY = 'chart-factory-theme';
|
|
26
|
+
var toggleButton = null;
|
|
27
|
+
|
|
28
|
+
function storedTheme() {
|
|
29
|
+
try { return localStorage.getItem(STORAGE_KEY); } catch (e) { return null; }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Apply BEFORE first render — this script must load before chart code.
|
|
33
|
+
if (storedTheme() === 'dark') {
|
|
34
|
+
document.documentElement.dataset.theme = 'dark';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isDark() {
|
|
38
|
+
return document.documentElement.dataset.theme === 'dark';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function applyTheme(theme, persist) {
|
|
42
|
+
if (theme === 'dark') {
|
|
43
|
+
document.documentElement.dataset.theme = 'dark';
|
|
44
|
+
} else {
|
|
45
|
+
delete document.documentElement.dataset.theme;
|
|
46
|
+
}
|
|
47
|
+
if (persist) {
|
|
48
|
+
try { localStorage.setItem(STORAGE_KEY, theme); } catch (e) { /* private mode */ }
|
|
49
|
+
}
|
|
50
|
+
if (window.ChartFactory && typeof window.ChartFactory.rerenderAll === 'function') {
|
|
51
|
+
window.ChartFactory.rerenderAll();
|
|
52
|
+
}
|
|
53
|
+
window.dispatchEvent(new CustomEvent('d3t:themechange', { detail: { theme: theme } }));
|
|
54
|
+
if (toggleButton) syncButton();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Follow theme changes made by other documents on this origin (parent
|
|
58
|
+
// page toggling while this page sits in its modal iframe, other tabs).
|
|
59
|
+
// 'storage' never fires in the document that wrote the value, so this
|
|
60
|
+
// cannot loop with applyTheme's own persist.
|
|
61
|
+
window.addEventListener('storage', function (e) {
|
|
62
|
+
if (e.key !== STORAGE_KEY) return;
|
|
63
|
+
var next = e.newValue === 'dark' ? 'dark' : 'light';
|
|
64
|
+
if (next !== (isDark() ? 'dark' : 'light')) applyTheme(next, false);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
function syncButton() {
|
|
68
|
+
var dark = isDark();
|
|
69
|
+
toggleButton.textContent = dark ? '☀' : '☾'; // ☀ / ☾
|
|
70
|
+
toggleButton.setAttribute('aria-label', dark ? 'Switch to light theme' : 'Switch to dark theme');
|
|
71
|
+
toggleButton.title = toggleButton.getAttribute('aria-label');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function mount() {
|
|
75
|
+
// Modal embeds inherit the theme but hide the control (the parent
|
|
76
|
+
// page owns it), mirroring how example-nav hides itself.
|
|
77
|
+
if (new URLSearchParams(window.location.search).get('modal') === '1') return;
|
|
78
|
+
|
|
79
|
+
toggleButton = document.createElement('button');
|
|
80
|
+
toggleButton.className = 'd3t-theme-toggle';
|
|
81
|
+
toggleButton.type = 'button';
|
|
82
|
+
toggleButton.style.cssText = [
|
|
83
|
+
'position: fixed',
|
|
84
|
+
'right: 16px',
|
|
85
|
+
'top: 16px',
|
|
86
|
+
'z-index: 10000',
|
|
87
|
+
'width: 40px',
|
|
88
|
+
'height: 40px',
|
|
89
|
+
'border-radius: 50%',
|
|
90
|
+
'border: 1px solid var(--color-border-light, #e5e5e5)',
|
|
91
|
+
'background: var(--color-bg-container, white)',
|
|
92
|
+
'color: var(--color-text-primary, #333)',
|
|
93
|
+
'box-shadow: var(--shadow-container, 0 1px 3px rgba(0,0,0,0.08))',
|
|
94
|
+
'font-size: 16px',
|
|
95
|
+
'line-height: 1',
|
|
96
|
+
'cursor: pointer',
|
|
97
|
+
'display: flex',
|
|
98
|
+
'align-items: center',
|
|
99
|
+
'justify-content: center'
|
|
100
|
+
].join(';');
|
|
101
|
+
toggleButton.addEventListener('click', function () {
|
|
102
|
+
applyTheme(isDark() ? 'light' : 'dark', true);
|
|
103
|
+
});
|
|
104
|
+
syncButton();
|
|
105
|
+
document.body.appendChild(toggleButton);
|
|
106
|
+
|
|
107
|
+
// Drop below the fixed example-nav bar when one is present. The nav
|
|
108
|
+
// can render after this mount (example-nav builds on its own
|
|
109
|
+
// DOMContentLoaded handler) and re-wraps on resize, so position now
|
|
110
|
+
// and again on load/resize.
|
|
111
|
+
function position() {
|
|
112
|
+
var nav = document.querySelector('.example-nav');
|
|
113
|
+
var navHeight = nav ? nav.offsetHeight : 0;
|
|
114
|
+
toggleButton.style.top = (navHeight ? navHeight + 12 : 16) + 'px';
|
|
115
|
+
}
|
|
116
|
+
position();
|
|
117
|
+
window.addEventListener('load', position);
|
|
118
|
+
window.addEventListener('resize', position);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (document.readyState === 'loading') {
|
|
122
|
+
document.addEventListener('DOMContentLoaded', mount);
|
|
123
|
+
} else {
|
|
124
|
+
mount();
|
|
125
|
+
}
|
|
126
|
+
})();
|