@stfrigerio/sito-template 0.1.7 → 0.1.9
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/dist/components/atoms/ArrayInput/ArrayInput.d.ts +5 -0
- package/dist/components/atoms/ArrayInput/ArrayInput.d.ts.map +1 -1
- package/dist/components/atoms/Toggle/Toggle.d.ts +4 -0
- package/dist/components/atoms/Toggle/Toggle.d.ts.map +1 -1
- package/dist/components/atoms/Toggle/Toggle.stories.d.ts +1 -0
- package/dist/components/atoms/Toggle/Toggle.stories.d.ts.map +1 -1
- package/dist/components/molecules/SearchBar/SearchBar.d.ts +11 -2
- package/dist/components/molecules/SearchBar/SearchBar.d.ts.map +1 -1
- package/dist/components/molecules/SearchBar/index.d.ts +1 -1
- package/dist/components/molecules/SearchBar/index.d.ts.map +1 -1
- package/dist/components/molecules/Tabs/Tabs.d.ts +11 -5
- package/dist/components/molecules/Tabs/Tabs.d.ts.map +1 -1
- package/dist/components/molecules/index.d.ts +2 -2
- package/dist/components/molecules/index.d.ts.map +1 -1
- package/dist/components/organisms/charts/PieChart/PieChart.d.ts +2 -0
- package/dist/components/organisms/charts/PieChart/PieChart.d.ts.map +1 -1
- package/dist/components/organisms/charts/QuantifiableHabitsChart/QuantifiableHabitsChart.d.ts.map +1 -1
- package/dist/components/organisms/charts/SunburstChart/SunburstChart.d.ts +2 -0
- package/dist/components/organisms/charts/SunburstChart/SunburstChart.d.ts.map +1 -1
- package/dist/index.esm.js +182 -46
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +182 -46
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -198,7 +198,7 @@ function TextInput({ label, value, onChange, type = "text", onFocus, onBlur, pla
|
|
|
198
198
|
return (jsxs("div", { className: getContainerClassName(), children: [jsxs("label", { htmlFor: inputId, children: [label, required && jsx("span", { className: styles$l.required, children: "*" })] }), jsxs("div", { style: { position: 'relative' }, children: [icon && jsx("div", { className: styles$l.inputIcon, children: icon }), jsx("input", { id: inputId, type: type, value: value, onChange: (e) => onChange(e.target.value), onFocus: onFocus, onBlur: onBlur, placeholder: placeholder, className: error ? styles$l.inputError : '', "aria-invalid": !!error, "aria-describedby": error ? `${inputId}-error` : undefined, disabled: disabled || loading, maxLength: maxLength, autoComplete: autoComplete }), actionButton && (jsx("button", { type: "button", className: styles$l.actionButton, onClick: actionButton.onClick, disabled: disabled || loading, children: actionButton.label }))] }), error && (jsx("span", { id: `${inputId}-error`, className: styles$l.errorMessage, children: error }))] }));
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
-
var styles$k = {"arrayInput":"ArrayInput-module_arrayInput__FNrd2","arrayInputLabel":"ArrayInput-module_arrayInputLabel__7Rpkj","arrayInputItem":"ArrayInput-module_arrayInputItem__eSH-6","inputWrapper":"ArrayInput-module_inputWrapper__i-MB-","input":"ArrayInput-module_input__792ru","complexItem":"ArrayInput-module_complexItem__iX3v6","fieldsWrapper":"ArrayInput-module_fieldsWrapper__fzqvn"};
|
|
201
|
+
var styles$k = {"arrayInput":"ArrayInput-module_arrayInput__FNrd2","arrayInputLabel":"ArrayInput-module_arrayInputLabel__7Rpkj","arrayInputItem":"ArrayInput-module_arrayInputItem__eSH-6","inputWrapper":"ArrayInput-module_inputWrapper__i-MB-","input":"ArrayInput-module_input__792ru","complexItem":"ArrayInput-module_complexItem__iX3v6","fieldsWrapper":"ArrayInput-module_fieldsWrapper__fzqvn","removeButton":"ArrayInput-module_removeButton__khhdR","addButton":"ArrayInput-module_addButton__10o-9"};
|
|
202
202
|
|
|
203
203
|
/**
|
|
204
204
|
* ArrayInput component - Versatile dynamic list manager
|
|
@@ -238,7 +238,7 @@ function ArrayInput(props) {
|
|
|
238
238
|
return jsx(SimpleArrayInput, { ...props });
|
|
239
239
|
}
|
|
240
240
|
// Simple string array implementation
|
|
241
|
-
function SimpleArrayInput({ label, values, onChange, placeholder }) {
|
|
241
|
+
function SimpleArrayInput({ label, values, onChange, placeholder, itemStyle, inputStyle }) {
|
|
242
242
|
const handleChange = (index, value) => {
|
|
243
243
|
const newValues = [...values];
|
|
244
244
|
newValues[index] = value;
|
|
@@ -251,10 +251,14 @@ function SimpleArrayInput({ label, values, onChange, placeholder }) {
|
|
|
251
251
|
const newValues = values.filter((_, i) => i !== index);
|
|
252
252
|
onChange(newValues);
|
|
253
253
|
};
|
|
254
|
-
return (jsxs("div", { className: styles$k.arrayInput, children: [jsx("h3", { className: styles$k.arrayInputLabel, children: label }),
|
|
254
|
+
return (jsxs("div", { className: styles$k.arrayInput, children: [jsx("h3", { className: styles$k.arrayInputLabel, children: label }), jsx("div", { children: jsx(AnimatePresence, { children: values.map((value, index) => (jsxs(motion.div, { className: styles$k.arrayInputItem, style: itemStyle, initial: { opacity: 0, y: 10 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0 }, transition: {
|
|
255
|
+
duration: 0.3,
|
|
256
|
+
ease: "easeInOut",
|
|
257
|
+
layout: { duration: 0.2 }
|
|
258
|
+
}, children: [jsx("div", { className: styles$k.inputWrapper, children: jsx("input", { type: "text", value: value, onChange: (e) => handleChange(index, e.target.value), placeholder: placeholder, className: styles$k.input, style: inputStyle }) }), jsx(Button, { variant: "ghost", size: "small", onClick: () => handleRemove(index), "aria-label": "Remove item", className: styles$k.removeButton, children: jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: jsx("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) }) })] }, `item-${index}`))) }) }), jsxs(Button, { variant: "primary", size: "small", onClick: handleAdd, className: styles$k.addButton, children: ["Add ", label] })] }));
|
|
255
259
|
}
|
|
256
260
|
// Complex object array implementation
|
|
257
|
-
function ComplexArrayInput({ label, values, onChange, fields, getKey }) {
|
|
261
|
+
function ComplexArrayInput({ label, values, onChange, fields, getKey, itemStyle, inputStyle }) {
|
|
258
262
|
const handleChange = (index, field, value) => {
|
|
259
263
|
const newValues = [...values];
|
|
260
264
|
newValues[index] = { ...newValues[index], [field]: value };
|
|
@@ -276,7 +280,11 @@ function ComplexArrayInput({ label, values, onChange, fields, getKey }) {
|
|
|
276
280
|
// Generate key from all field values
|
|
277
281
|
return fields.map(f => item[f.name] || '').join('-') + `-${index}`;
|
|
278
282
|
};
|
|
279
|
-
return (jsxs("div", { className: styles$k.arrayInput, children: [jsx("h3", { className: styles$k.arrayInputLabel, children: label }), values.map((value, index) => (jsxs(
|
|
283
|
+
return (jsxs("div", { className: styles$k.arrayInput, children: [jsx("h3", { className: styles$k.arrayInputLabel, children: label }), jsx("div", { children: jsx(AnimatePresence, { children: values.map((value, index) => (jsxs(motion.div, { className: `${styles$k.arrayInputItem} ${fields.length > 1 ? styles$k.complexItem : ''}`, style: itemStyle, initial: { opacity: 0, y: 10 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0 }, transition: {
|
|
284
|
+
duration: 0.3,
|
|
285
|
+
ease: "easeInOut",
|
|
286
|
+
layout: { duration: 0.2 }
|
|
287
|
+
}, children: [jsx("div", { className: styles$k.fieldsWrapper, children: fields.map((field) => (jsx("div", { style: inputStyle, children: jsx(TextInput, { value: value[field.name] || '', onChange: (newValue) => handleChange(index, field.name, newValue), label: field.label, type: field.type, placeholder: field.placeholder }) }, field.name))) }), jsx(Button, { variant: "ghost", size: "small", onClick: () => handleRemove(index), "aria-label": "Remove item", className: styles$k.removeButton, children: jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: jsx("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) }) })] }, generateKey(value, index)))) }) }), jsxs(Button, { variant: "primary", size: "small", onClick: handleAdd, className: styles$k.addButton, children: ["Add ", label] })] }));
|
|
280
288
|
}
|
|
281
289
|
|
|
282
290
|
var styles$j = {"checkboxLabel":"Checkbox-module_checkboxLabel__4tBVg","checkbox":"Checkbox-module_checkbox__BbJul","checkboxText":"Checkbox-module_checkboxText__oJsc9"};
|
|
@@ -825,8 +833,15 @@ var styles$e = {"toggleContainer":"Toggle-module_toggleContainer__QxqQb","toggle
|
|
|
825
833
|
* @returns {JSX.Element} The rendered Toggle component
|
|
826
834
|
*/
|
|
827
835
|
function Toggle(props) {
|
|
828
|
-
const { isOn, onToggle, leftLabel, rightLabel, leftIcon, rightIcon } = props;
|
|
829
|
-
|
|
836
|
+
const { isOn, onToggle, leftLabel, rightLabel, leftIcon, rightIcon, className, style } = props;
|
|
837
|
+
// Ensure content stays centered by merging styles
|
|
838
|
+
const buttonStyle = {
|
|
839
|
+
display: 'flex',
|
|
840
|
+
alignItems: 'center',
|
|
841
|
+
justifyContent: 'center',
|
|
842
|
+
...style
|
|
843
|
+
};
|
|
844
|
+
return (jsxs("div", { className: `${styles$e.toggleContainer} ${className || ''}`, children: [jsxs("button", { className: `${styles$e.toggleButton} ${!isOn ? styles$e.active : ''}`, onClick: () => onToggle(false), style: buttonStyle, children: [leftIcon, leftLabel] }), jsxs("button", { className: `${styles$e.toggleButton} ${isOn ? styles$e.active : ''}`, onClick: () => onToggle(true), style: buttonStyle, children: [rightIcon, rightLabel] })] }));
|
|
830
845
|
}
|
|
831
846
|
|
|
832
847
|
var styles$d = {"container":"NumberStepper-module_container__WSGlU","header":"NumberStepper-module_header__qXI1Y","icon":"NumberStepper-module_icon__vHgsw","label":"NumberStepper-module_label__AYr3g","stepper":"NumberStepper-module_stepper__oQhTp","disabled":"NumberStepper-module_disabled__kGB-g","button":"NumberStepper-module_button__YcjRt","buttonIcon":"NumberStepper-module_buttonIcon__odXec","valueContainer":"NumberStepper-module_valueContainer__87w2D","valueWrapper":"NumberStepper-module_valueWrapper__TH65N","value":"NumberStepper-module_value__BxJeD","limits":"NumberStepper-module_limits__-UrRE","limit":"NumberStepper-module_limit__7nbIP","small":"NumberStepper-module_small__P-k96","large":"NumberStepper-module_large__Lz6lk","outlined":"NumberStepper-module_outlined__CIXv7","filled":"NumberStepper-module_filled__IxOg-","pulse":"NumberStepper-module_pulse__51oUo"};
|
|
@@ -1178,7 +1193,8 @@ const EditFAB = ({ canEdit, isEditMode, hasUnsavedChanges = false, isSaving = fa
|
|
|
1178
1193
|
|
|
1179
1194
|
var styles$a = {"searchContainer":"SearchBar-module_searchContainer__TdM1w","searchInputWrapper":"SearchBar-module_searchInputWrapper__kCZLU","searchIcon":"SearchBar-module_searchIcon__IIxEu","searchInput":"SearchBar-module_searchInput__V4gkE","clearButton":"SearchBar-module_clearButton__7fNIY","filterSelect":"SearchBar-module_filterSelect__xIVE4","resultsDropdown":"SearchBar-module_resultsDropdown__yh6NF","loadingState":"SearchBar-module_loadingState__4gidK","emptyState":"SearchBar-module_emptyState__RbI4s","spinner":"SearchBar-module_spinner__PMc6-","resultsGroups":"SearchBar-module_resultsGroups__U24DC","resultGroup":"SearchBar-module_resultGroup__SoTQH","groupHeader":"SearchBar-module_groupHeader__bFRHA","groupIcon":"SearchBar-module_groupIcon__9ENM-","groupTitle":"SearchBar-module_groupTitle__ZekZs","groupCount":"SearchBar-module_groupCount__PQIqw","groupResults":"SearchBar-module_groupResults__xTF52","resultItem":"SearchBar-module_resultItem__VaKKy","highlighted":"SearchBar-module_highlighted__Q-3sH","resultTitle":"SearchBar-module_resultTitle__i1uqL","resultSubtitle":"SearchBar-module_resultSubtitle__LQOJ1","resultMeta":"SearchBar-module_resultMeta__Kmkrn","resultContent":"SearchBar-module_resultContent__TzVzL","highlight":"SearchBar-module_highlight__Q3PSP"};
|
|
1180
1195
|
|
|
1181
|
-
|
|
1196
|
+
// Default filter options for backwards compatibility
|
|
1197
|
+
const defaultFilterOptions = [
|
|
1182
1198
|
{ value: 'all', label: 'All', icon: FiSearch },
|
|
1183
1199
|
{ value: 'projects', label: 'Projects', icon: FiFolder },
|
|
1184
1200
|
{ value: 'clients', label: 'Clients', icon: FiUsers },
|
|
@@ -1186,16 +1202,18 @@ const filterOptions = [
|
|
|
1186
1202
|
{ value: 'interactions', label: 'Interactions', icon: FiMessageSquare },
|
|
1187
1203
|
{ value: 'team', label: 'Team', icon: FiUserPlus },
|
|
1188
1204
|
];
|
|
1189
|
-
const
|
|
1205
|
+
const defaultEntityIcons = {
|
|
1190
1206
|
projects: FiFolder,
|
|
1191
1207
|
clients: FiUsers,
|
|
1192
1208
|
contacts: FiBook,
|
|
1193
1209
|
interactions: FiMessageSquare,
|
|
1194
1210
|
team: FiUserPlus,
|
|
1195
1211
|
};
|
|
1196
|
-
const SearchBar = ({ className, placeholder = "Search (Ctrl+Space)...", onSearch, onResultClick, onClear, debounceDelay = 300, minSearchLength = 2, showFilter = true, enableKeyboardShortcut = true }) => {
|
|
1212
|
+
const SearchBar = ({ className, placeholder = "Search (Ctrl+Space)...", onSearch, onResultClick, onClear, debounceDelay = 300, minSearchLength = 2, showFilter = true, enableKeyboardShortcut = true, filterOptions: customFilterOptions, entityIcons: customEntityIcons }) => {
|
|
1213
|
+
const filterOptions = customFilterOptions ?? defaultFilterOptions;
|
|
1214
|
+
const entityIcons = customEntityIcons ?? defaultEntityIcons;
|
|
1197
1215
|
const [query, setQuery] = useState('');
|
|
1198
|
-
const [filter, setFilter] = useState('all');
|
|
1216
|
+
const [filter, setFilter] = useState(filterOptions[0]?.value ?? 'all');
|
|
1199
1217
|
const [results, setResults] = useState([]);
|
|
1200
1218
|
const [isLoading, setIsLoading] = useState(false);
|
|
1201
1219
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
|
@@ -1524,15 +1542,16 @@ function SiJira (props) {
|
|
|
1524
1542
|
|
|
1525
1543
|
var styles$6 = {"tabs":"Tabs-module_tabs__Vlvn7","tab":"Tabs-module_tab__uQKim","tabIcon":"Tabs-module_tabIcon__AgN-O"};
|
|
1526
1544
|
|
|
1527
|
-
|
|
1545
|
+
// Default tabs for backwards compatibility
|
|
1546
|
+
const defaultTabs = [
|
|
1528
1547
|
{ id: 'details', icon: FiInfo, label: 'Dettagli' },
|
|
1529
1548
|
{ id: 'github', icon: FiGithub, label: 'GitHub' },
|
|
1530
1549
|
{ id: 'jira', icon: SiJira, label: 'Jira' },
|
|
1531
1550
|
{ id: 'functional', icon: FiInfo, label: 'Analisi funzionale' }
|
|
1532
1551
|
];
|
|
1533
|
-
const Tabs = ({ activeTab, onTabChange }) => {
|
|
1534
|
-
|
|
1535
|
-
|
|
1552
|
+
const Tabs = ({ activeTab, onTabChange, tabs: customTabs, className = '' }) => {
|
|
1553
|
+
const tabs = customTabs ?? defaultTabs;
|
|
1554
|
+
return (jsx("div", { className: `${styles$6.tabs} ${className}`, children: tabs.map((tab) => {
|
|
1536
1555
|
const isActive = activeTab === tab.id;
|
|
1537
1556
|
return (jsxs(motion.button, { className: styles$6.tab, "data-active": isActive, onClick: () => onTabChange(tab.id), style: { position: 'relative' }, children: [jsx(motion.div, { animate: {
|
|
1538
1557
|
rotate: isActive ? [0, -10, 10, -5, 5, 0] : 0,
|
|
@@ -1541,7 +1560,7 @@ const Tabs = ({ activeTab, onTabChange }) => {
|
|
|
1541
1560
|
duration: 0.5,
|
|
1542
1561
|
ease: 'easeInOut'
|
|
1543
1562
|
}
|
|
1544
|
-
}, children: jsx(
|
|
1563
|
+
}, children: tab.icon && (typeof tab.icon === 'function' ? (jsx("span", { className: styles$6.tabIcon, children: React.createElement(tab.icon) })) : (jsx("span", { className: styles$6.tabIcon, children: tab.icon }))) }), jsx("span", { children: tab.label })] }, tab.id));
|
|
1545
1564
|
}) }));
|
|
1546
1565
|
};
|
|
1547
1566
|
|
|
@@ -2225,11 +2244,22 @@ const BooleansHeatmap = ({ data, habitName, width = 800, height = 200, habitColo
|
|
|
2225
2244
|
var styles$1 = {"container":"SunburstChart-module_container__w1ZYc","title":"SunburstChart-module_title__T6Ak7","chart":"SunburstChart-module_chart__BFM6E","tooltip":"SunburstChart-module_tooltip__TuTAN"};
|
|
2226
2245
|
|
|
2227
2246
|
const COLOR_PALETTE = [
|
|
2228
|
-
'#
|
|
2229
|
-
'#
|
|
2230
|
-
'#
|
|
2247
|
+
'#6366f1', '#8b5cf6', '#06b6d4', '#10b981',
|
|
2248
|
+
'#f59e0b', '#ef4444', '#ec4899', '#84cc16',
|
|
2249
|
+
'#f97316', '#3b82f6', '#14b8a6', '#f59e0b'
|
|
2231
2250
|
];
|
|
2232
|
-
|
|
2251
|
+
// Calculate text color based on background luminance for optimal contrast
|
|
2252
|
+
const getTextColor$1 = (backgroundColor) => {
|
|
2253
|
+
const color = d3.color(backgroundColor);
|
|
2254
|
+
if (!color)
|
|
2255
|
+
return '#ffffff';
|
|
2256
|
+
const rgb = color.rgb();
|
|
2257
|
+
// Calculate relative luminance using WCAG formula
|
|
2258
|
+
const luminance = (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;
|
|
2259
|
+
// Return white text for dark backgrounds, black for light backgrounds
|
|
2260
|
+
return luminance > 0.5 ? '#000000' : '#ffffff';
|
|
2261
|
+
};
|
|
2262
|
+
const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Chart', tagColors = {}, unit = 'items', centerLabel }) => {
|
|
2233
2263
|
const svgRef = useRef(null);
|
|
2234
2264
|
const colorMap = useRef(new Map()).current;
|
|
2235
2265
|
const colorIndex = useRef(0);
|
|
@@ -2272,7 +2302,8 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
|
|
|
2272
2302
|
.startAngle(d => d.x0)
|
|
2273
2303
|
.endAngle(d => d.x1)
|
|
2274
2304
|
.innerRadius(d => Math.sqrt(d.y0))
|
|
2275
|
-
.outerRadius(d => Math.sqrt(d.y1))
|
|
2305
|
+
.outerRadius(d => Math.sqrt(d.y1))
|
|
2306
|
+
.cornerRadius(3);
|
|
2276
2307
|
const tooltip = d3.select('body').append('div')
|
|
2277
2308
|
.attr('class', styles$1.tooltip)
|
|
2278
2309
|
.style('opacity', 0)
|
|
@@ -2291,11 +2322,20 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
|
|
|
2291
2322
|
.attr('stroke', 'var(--bg-primary)')
|
|
2292
2323
|
.attr('stroke-width', 2)
|
|
2293
2324
|
.style('cursor', 'pointer')
|
|
2325
|
+
.style('filter', 'drop-shadow(0 1px 3px rgba(0,0,0,0.12))')
|
|
2294
2326
|
.on('mouseover', function (event, d) {
|
|
2327
|
+
const hoverArc = d3.arc()
|
|
2328
|
+
.startAngle(d => d.x0)
|
|
2329
|
+
.endAngle(d => d.x1)
|
|
2330
|
+
.innerRadius(d => Math.sqrt(d.y0) - 2)
|
|
2331
|
+
.outerRadius(d => Math.sqrt(d.y1) + 4)
|
|
2332
|
+
.cornerRadius(3);
|
|
2295
2333
|
d3.select(this)
|
|
2296
2334
|
.transition()
|
|
2297
|
-
.duration(
|
|
2298
|
-
.
|
|
2335
|
+
.duration(150)
|
|
2336
|
+
.attr('d', d => hoverArc(d))
|
|
2337
|
+
.style('filter', 'drop-shadow(0 2px 8px rgba(0,0,0,0.2))')
|
|
2338
|
+
.style('opacity', 0.9);
|
|
2299
2339
|
tooltip.transition()
|
|
2300
2340
|
.duration(200)
|
|
2301
2341
|
.style('opacity', 1);
|
|
@@ -2309,10 +2349,12 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
|
|
|
2309
2349
|
.style('left', (event.pageX + 10) + 'px')
|
|
2310
2350
|
.style('top', (event.pageY - 28) + 'px');
|
|
2311
2351
|
})
|
|
2312
|
-
.on('mouseout', function () {
|
|
2352
|
+
.on('mouseout', function (event, d) {
|
|
2313
2353
|
d3.select(this)
|
|
2314
2354
|
.transition()
|
|
2315
|
-
.duration(
|
|
2355
|
+
.duration(150)
|
|
2356
|
+
.attr('d', d => arc(d))
|
|
2357
|
+
.style('filter', 'drop-shadow(0 1px 3px rgba(0,0,0,0.12))')
|
|
2316
2358
|
.style('opacity', 1);
|
|
2317
2359
|
tooltip.transition()
|
|
2318
2360
|
.duration(500)
|
|
@@ -2322,9 +2364,32 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
|
|
|
2322
2364
|
const angle = d.x1 - d.x0;
|
|
2323
2365
|
return angle > 0.15 && d.depth <= 2;
|
|
2324
2366
|
};
|
|
2325
|
-
|
|
2367
|
+
// Calculate average background color for center text contrast
|
|
2368
|
+
const allSegments = nodes.filter(d => d.depth === 1);
|
|
2369
|
+
const avgColor = allSegments.length > 0 ? d3.interpolateRgb.gamma(2.2)(...allSegments.map(d => getColor(d.data.name, 1)))(0.5) : '#ffffff';
|
|
2370
|
+
const centerTextColor = getTextColor$1(avgColor);
|
|
2371
|
+
// Add center text
|
|
2372
|
+
g.append('text')
|
|
2373
|
+
.attr('text-anchor', 'middle')
|
|
2374
|
+
.attr('alignment-baseline', 'middle')
|
|
2375
|
+
.attr('font-size', '18px')
|
|
2376
|
+
.attr('font-weight', 'bold')
|
|
2377
|
+
.attr('fill', centerTextColor)
|
|
2378
|
+
.style('text-shadow', centerTextColor === '#ffffff' ? '0 1px 3px rgba(0,0,0,0.5)' : '0 1px 3px rgba(255,255,255,0.5)')
|
|
2379
|
+
.text(centerLabel || data.name || 'Total');
|
|
2380
|
+
g.append('text')
|
|
2381
|
+
.attr('text-anchor', 'middle')
|
|
2382
|
+
.attr('alignment-baseline', 'middle')
|
|
2383
|
+
.attr('y', 20)
|
|
2384
|
+
.attr('font-size', '14px')
|
|
2385
|
+
.attr('font-weight', '500')
|
|
2386
|
+
.attr('fill', centerTextColor)
|
|
2387
|
+
.style('text-shadow', centerTextColor === '#ffffff' ? '0 1px 2px rgba(0,0,0,0.4)' : '0 1px 2px rgba(255,255,255,0.4)')
|
|
2388
|
+
.text(`${(root.value || 0).toLocaleString()} ${unit}`);
|
|
2389
|
+
g.selectAll('text.segment-label')
|
|
2326
2390
|
.data(nodes.filter(d => d.depth > 0 && d.value && d.value > 0 && shouldDisplayLabel(d)))
|
|
2327
2391
|
.enter().append('text')
|
|
2392
|
+
.attr('class', 'segment-label')
|
|
2328
2393
|
.attr('transform', d => {
|
|
2329
2394
|
const angle = (d.x0 + d.x1) / 2;
|
|
2330
2395
|
const radius = (Math.sqrt(d.y0) + Math.sqrt(d.y1)) / 2;
|
|
@@ -2334,11 +2399,32 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
|
|
|
2334
2399
|
})
|
|
2335
2400
|
.attr('text-anchor', 'middle')
|
|
2336
2401
|
.attr('alignment-baseline', 'middle')
|
|
2337
|
-
.attr('font-size', '
|
|
2338
|
-
.attr('fill',
|
|
2339
|
-
|
|
2402
|
+
.attr('font-size', d => d.depth === 1 ? '13px' : '11px')
|
|
2403
|
+
.attr('fill', d => {
|
|
2404
|
+
let ancestor = d;
|
|
2405
|
+
while (ancestor.depth > 1 && ancestor.parent) {
|
|
2406
|
+
ancestor = ancestor.parent;
|
|
2407
|
+
}
|
|
2408
|
+
const segmentColor = getColor(ancestor.data.name, d.depth);
|
|
2409
|
+
return getTextColor$1(segmentColor);
|
|
2410
|
+
})
|
|
2411
|
+
.attr('font-weight', '600')
|
|
2340
2412
|
.style('pointer-events', 'none')
|
|
2341
|
-
.text
|
|
2413
|
+
.style('text-shadow', d => {
|
|
2414
|
+
let ancestor = d;
|
|
2415
|
+
while (ancestor.depth > 1 && ancestor.parent) {
|
|
2416
|
+
ancestor = ancestor.parent;
|
|
2417
|
+
}
|
|
2418
|
+
const segmentColor = getColor(ancestor.data.name, d.depth);
|
|
2419
|
+
const textColor = getTextColor$1(segmentColor);
|
|
2420
|
+
// Use contrasting shadow color
|
|
2421
|
+
const shadowColor = textColor === '#ffffff' ? 'rgba(0,0,0,0.5)' : 'rgba(255,255,255,0.5)';
|
|
2422
|
+
return `0 1px 2px ${shadowColor}`;
|
|
2423
|
+
})
|
|
2424
|
+
.text(d => {
|
|
2425
|
+
const maxLength = d.depth === 1 ? 12 : 8;
|
|
2426
|
+
return d.data.name.substring(0, maxLength);
|
|
2427
|
+
});
|
|
2342
2428
|
return () => {
|
|
2343
2429
|
tooltip.remove();
|
|
2344
2430
|
};
|
|
@@ -2349,12 +2435,23 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
|
|
|
2349
2435
|
var styles = {"container":"PieChart-module_container__tXjbe","title":"PieChart-module_title__61o0R","chartContainer":"PieChart-module_chartContainer__uLmOz","chart":"PieChart-module_chart__3nqON","legend":"PieChart-module_legend__rAWgh","legendItem":"PieChart-module_legendItem__Nb031","legendColor":"PieChart-module_legendColor__fLuv9","legendLabel":"PieChart-module_legendLabel__xbjBr","legendValue":"PieChart-module_legendValue__h2WS2","tooltip":"PieChart-module_tooltip__140RU"};
|
|
2350
2436
|
|
|
2351
2437
|
const DEFAULT_COLORS = [
|
|
2352
|
-
'#
|
|
2353
|
-
'#
|
|
2354
|
-
'#
|
|
2355
|
-
'#
|
|
2438
|
+
'#6366f1', '#8b5cf6', '#06b6d4', '#10b981',
|
|
2439
|
+
'#f59e0b', '#ef4444', '#ec4899', '#84cc16',
|
|
2440
|
+
'#f97316', '#3b82f6', '#8b5cf6', '#14b8a6',
|
|
2441
|
+
'#f59e0b', '#ef4444', '#06b6d4', '#10b981'
|
|
2356
2442
|
];
|
|
2357
|
-
|
|
2443
|
+
// Calculate text color based on background luminance for optimal contrast
|
|
2444
|
+
const getTextColor = (backgroundColor) => {
|
|
2445
|
+
const color = d3.color(backgroundColor);
|
|
2446
|
+
if (!color)
|
|
2447
|
+
return '#ffffff';
|
|
2448
|
+
const rgb = color.rgb();
|
|
2449
|
+
// Calculate relative luminance using WCAG formula
|
|
2450
|
+
const luminance = (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;
|
|
2451
|
+
// Return white text for dark backgrounds, black for light backgrounds
|
|
2452
|
+
return luminance > 0.5 ? '#000000' : '#ffffff';
|
|
2453
|
+
};
|
|
2454
|
+
const PieChart = ({ data, width = 400, height = 400, title = 'Distribution', showLegend = true, unit = 'items', centerLabel }) => {
|
|
2358
2455
|
const svgRef = useRef(null);
|
|
2359
2456
|
const radius = Math.min(width, height) / 2 - 20;
|
|
2360
2457
|
useEffect(() => {
|
|
@@ -2368,8 +2465,9 @@ const PieChart = ({ data, width = 400, height = 400, title = 'Distribution', sho
|
|
|
2368
2465
|
.value(d => d.value)
|
|
2369
2466
|
.sort(null);
|
|
2370
2467
|
const arc = d3.arc()
|
|
2371
|
-
.innerRadius(0)
|
|
2372
|
-
.outerRadius(radius)
|
|
2468
|
+
.innerRadius(radius * 0.4)
|
|
2469
|
+
.outerRadius(radius)
|
|
2470
|
+
.cornerRadius(4);
|
|
2373
2471
|
const labelArc = d3.arc()
|
|
2374
2472
|
.innerRadius(radius * 0.7)
|
|
2375
2473
|
.outerRadius(radius * 0.7);
|
|
@@ -2387,13 +2485,19 @@ const PieChart = ({ data, width = 400, height = 400, title = 'Distribution', sho
|
|
|
2387
2485
|
.attr('d', d => arc(d))
|
|
2388
2486
|
.attr('fill', (d, i) => d.data.color || DEFAULT_COLORS[i % DEFAULT_COLORS.length])
|
|
2389
2487
|
.attr('stroke', 'var(--bg-primary)')
|
|
2390
|
-
.attr('stroke-width',
|
|
2488
|
+
.attr('stroke-width', 3)
|
|
2391
2489
|
.style('cursor', 'pointer')
|
|
2490
|
+
.style('filter', 'drop-shadow(0 2px 8px rgba(0,0,0,0.15))')
|
|
2392
2491
|
.on('mouseover', function (event, d) {
|
|
2492
|
+
const hoverArc = d3.arc()
|
|
2493
|
+
.innerRadius(radius * 0.4)
|
|
2494
|
+
.outerRadius(radius * 1.05)
|
|
2495
|
+
.cornerRadius(4);
|
|
2393
2496
|
d3.select(this)
|
|
2394
2497
|
.transition()
|
|
2395
2498
|
.duration(200)
|
|
2396
|
-
.
|
|
2499
|
+
.attr('d', d => hoverArc(d))
|
|
2500
|
+
.style('filter', 'drop-shadow(0 4px 12px rgba(0,0,0,0.25))');
|
|
2397
2501
|
tooltip.transition()
|
|
2398
2502
|
.duration(200)
|
|
2399
2503
|
.style('opacity', 1);
|
|
@@ -2406,27 +2510,59 @@ const PieChart = ({ data, width = 400, height = 400, title = 'Distribution', sho
|
|
|
2406
2510
|
.style('left', (event.pageX + 10) + 'px')
|
|
2407
2511
|
.style('top', (event.pageY - 28) + 'px');
|
|
2408
2512
|
})
|
|
2409
|
-
.on('mouseout', function () {
|
|
2513
|
+
.on('mouseout', function (event, d) {
|
|
2410
2514
|
d3.select(this)
|
|
2411
2515
|
.transition()
|
|
2412
2516
|
.duration(200)
|
|
2413
|
-
.
|
|
2517
|
+
.attr('d', d => arc(d))
|
|
2518
|
+
.style('filter', 'drop-shadow(0 2px 8px rgba(0,0,0,0.15))');
|
|
2414
2519
|
tooltip.transition()
|
|
2415
2520
|
.duration(500)
|
|
2416
2521
|
.style('opacity', 0);
|
|
2417
2522
|
});
|
|
2418
|
-
|
|
2523
|
+
// Calculate average background color for center text contrast
|
|
2524
|
+
const avgColor = data.length > 0 ? d3.interpolateRgb.gamma(2.2)(...data.map((d, i) => d.color || DEFAULT_COLORS[i % DEFAULT_COLORS.length]))(0.5) : '#ffffff';
|
|
2525
|
+
const centerTextColor = getTextColor(avgColor);
|
|
2526
|
+
// Add center text for donut
|
|
2527
|
+
g.append('text')
|
|
2528
|
+
.attr('text-anchor', 'middle')
|
|
2529
|
+
.attr('alignment-baseline', 'middle')
|
|
2530
|
+
.attr('font-size', '20px')
|
|
2531
|
+
.attr('font-weight', 'bold')
|
|
2532
|
+
.attr('fill', centerTextColor)
|
|
2533
|
+
.style('text-shadow', centerTextColor === '#ffffff' ? '0 1px 3px rgba(0,0,0,0.5)' : '0 1px 3px rgba(255,255,255,0.5)')
|
|
2534
|
+
.text(centerLabel || 'Total');
|
|
2535
|
+
g.append('text')
|
|
2536
|
+
.attr('text-anchor', 'middle')
|
|
2537
|
+
.attr('alignment-baseline', 'middle')
|
|
2538
|
+
.attr('y', 22)
|
|
2539
|
+
.attr('font-size', '16px')
|
|
2540
|
+
.attr('font-weight', '600')
|
|
2541
|
+
.attr('fill', centerTextColor)
|
|
2542
|
+
.style('text-shadow', centerTextColor === '#ffffff' ? '0 1px 2px rgba(0,0,0,0.4)' : '0 1px 2px rgba(255,255,255,0.4)')
|
|
2543
|
+
.text(`${total.toLocaleString()} ${unit}`);
|
|
2544
|
+
arcs.filter(d => (d.endAngle - d.startAngle) > 0.2)
|
|
2419
2545
|
.append('text')
|
|
2420
2546
|
.attr('transform', d => `translate(${labelArc.centroid(d)})`)
|
|
2421
2547
|
.attr('text-anchor', 'middle')
|
|
2422
2548
|
.attr('alignment-baseline', 'middle')
|
|
2423
|
-
.attr('font-size', '
|
|
2424
|
-
.attr('fill',
|
|
2425
|
-
.
|
|
2549
|
+
.attr('font-size', '13px')
|
|
2550
|
+
.attr('fill', (d, i) => {
|
|
2551
|
+
const segmentColor = d.data.color || DEFAULT_COLORS[i % DEFAULT_COLORS.length];
|
|
2552
|
+
return getTextColor(segmentColor);
|
|
2553
|
+
})
|
|
2554
|
+
.attr('font-weight', '600')
|
|
2426
2555
|
.style('pointer-events', 'none')
|
|
2556
|
+
.style('text-shadow', (d, i) => {
|
|
2557
|
+
const segmentColor = d.data.color || DEFAULT_COLORS[i % DEFAULT_COLORS.length];
|
|
2558
|
+
const textColor = getTextColor(segmentColor);
|
|
2559
|
+
// Use contrasting shadow color
|
|
2560
|
+
const shadowColor = textColor === '#ffffff' ? 'rgba(0,0,0,0.4)' : 'rgba(255,255,255,0.4)';
|
|
2561
|
+
return `0 1px 2px ${shadowColor}`;
|
|
2562
|
+
})
|
|
2427
2563
|
.text(d => {
|
|
2428
2564
|
const percentage = ((d.data.value / total) * 100);
|
|
2429
|
-
return percentage >
|
|
2565
|
+
return percentage > 8 ? `${percentage.toFixed(0)}%` : '';
|
|
2430
2566
|
});
|
|
2431
2567
|
return () => {
|
|
2432
2568
|
tooltip.remove();
|