@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.
Files changed (26) hide show
  1. package/dist/components/atoms/ArrayInput/ArrayInput.d.ts +5 -0
  2. package/dist/components/atoms/ArrayInput/ArrayInput.d.ts.map +1 -1
  3. package/dist/components/atoms/Toggle/Toggle.d.ts +4 -0
  4. package/dist/components/atoms/Toggle/Toggle.d.ts.map +1 -1
  5. package/dist/components/atoms/Toggle/Toggle.stories.d.ts +1 -0
  6. package/dist/components/atoms/Toggle/Toggle.stories.d.ts.map +1 -1
  7. package/dist/components/molecules/SearchBar/SearchBar.d.ts +11 -2
  8. package/dist/components/molecules/SearchBar/SearchBar.d.ts.map +1 -1
  9. package/dist/components/molecules/SearchBar/index.d.ts +1 -1
  10. package/dist/components/molecules/SearchBar/index.d.ts.map +1 -1
  11. package/dist/components/molecules/Tabs/Tabs.d.ts +11 -5
  12. package/dist/components/molecules/Tabs/Tabs.d.ts.map +1 -1
  13. package/dist/components/molecules/index.d.ts +2 -2
  14. package/dist/components/molecules/index.d.ts.map +1 -1
  15. package/dist/components/organisms/charts/PieChart/PieChart.d.ts +2 -0
  16. package/dist/components/organisms/charts/PieChart/PieChart.d.ts.map +1 -1
  17. package/dist/components/organisms/charts/QuantifiableHabitsChart/QuantifiableHabitsChart.d.ts.map +1 -1
  18. package/dist/components/organisms/charts/SunburstChart/SunburstChart.d.ts +2 -0
  19. package/dist/components/organisms/charts/SunburstChart/SunburstChart.d.ts.map +1 -1
  20. package/dist/index.esm.js +182 -46
  21. package/dist/index.esm.js.map +1 -1
  22. package/dist/index.js +182 -46
  23. package/dist/index.js.map +1 -1
  24. package/dist/styles.css +1 -1
  25. package/dist/styles.css.map +1 -1
  26. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -219,7 +219,7 @@ function TextInput({ label, value, onChange, type = "text", onFocus, onBlur, pla
219
219
  return (jsxRuntime.jsxs("div", { className: getContainerClassName(), children: [jsxRuntime.jsxs("label", { htmlFor: inputId, children: [label, required && jsxRuntime.jsx("span", { className: styles$l.required, children: "*" })] }), jsxRuntime.jsxs("div", { style: { position: 'relative' }, children: [icon && jsxRuntime.jsx("div", { className: styles$l.inputIcon, children: icon }), jsxRuntime.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 && (jsxRuntime.jsx("button", { type: "button", className: styles$l.actionButton, onClick: actionButton.onClick, disabled: disabled || loading, children: actionButton.label }))] }), error && (jsxRuntime.jsx("span", { id: `${inputId}-error`, className: styles$l.errorMessage, children: error }))] }));
220
220
  }
221
221
 
222
- 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"};
222
+ 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"};
223
223
 
224
224
  /**
225
225
  * ArrayInput component - Versatile dynamic list manager
@@ -259,7 +259,7 @@ function ArrayInput(props) {
259
259
  return jsxRuntime.jsx(SimpleArrayInput, { ...props });
260
260
  }
261
261
  // Simple string array implementation
262
- function SimpleArrayInput({ label, values, onChange, placeholder }) {
262
+ function SimpleArrayInput({ label, values, onChange, placeholder, itemStyle, inputStyle }) {
263
263
  const handleChange = (index, value) => {
264
264
  const newValues = [...values];
265
265
  newValues[index] = value;
@@ -272,10 +272,14 @@ function SimpleArrayInput({ label, values, onChange, placeholder }) {
272
272
  const newValues = values.filter((_, i) => i !== index);
273
273
  onChange(newValues);
274
274
  };
275
- return (jsxRuntime.jsxs("div", { className: styles$k.arrayInput, children: [jsxRuntime.jsx("h3", { className: styles$k.arrayInputLabel, children: label }), values.map((value, index) => (jsxRuntime.jsxs("div", { className: styles$k.arrayInputItem, children: [jsxRuntime.jsx("div", { className: styles$k.inputWrapper, children: jsxRuntime.jsx("input", { type: "text", value: value, onChange: (e) => handleChange(index, e.target.value), placeholder: placeholder, className: styles$k.input }) }), jsxRuntime.jsx(Button, { variant: "ghost", size: "small", onClick: () => handleRemove(index), children: "Remove" })] }, `item-${index}`))), jsxRuntime.jsxs(Button, { variant: "primary", size: "small", onClick: handleAdd, children: ["Add ", label] })] }));
275
+ return (jsxRuntime.jsxs("div", { className: styles$k.arrayInput, children: [jsxRuntime.jsx("h3", { className: styles$k.arrayInputLabel, children: label }), jsxRuntime.jsx("div", { children: jsxRuntime.jsx(framerMotion.AnimatePresence, { children: values.map((value, index) => (jsxRuntime.jsxs(framerMotion.motion.div, { className: styles$k.arrayInputItem, style: itemStyle, initial: { opacity: 0, y: 10 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0 }, transition: {
276
+ duration: 0.3,
277
+ ease: "easeInOut",
278
+ layout: { duration: 0.2 }
279
+ }, children: [jsxRuntime.jsx("div", { className: styles$k.inputWrapper, children: jsxRuntime.jsx("input", { type: "text", value: value, onChange: (e) => handleChange(index, e.target.value), placeholder: placeholder, className: styles$k.input, style: inputStyle }) }), jsxRuntime.jsx(Button, { variant: "ghost", size: "small", onClick: () => handleRemove(index), "aria-label": "Remove item", className: styles$k.removeButton, children: jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: jsxRuntime.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}`))) }) }), jsxRuntime.jsxs(Button, { variant: "primary", size: "small", onClick: handleAdd, className: styles$k.addButton, children: ["Add ", label] })] }));
276
280
  }
277
281
  // Complex object array implementation
278
- function ComplexArrayInput({ label, values, onChange, fields, getKey }) {
282
+ function ComplexArrayInput({ label, values, onChange, fields, getKey, itemStyle, inputStyle }) {
279
283
  const handleChange = (index, field, value) => {
280
284
  const newValues = [...values];
281
285
  newValues[index] = { ...newValues[index], [field]: value };
@@ -297,7 +301,11 @@ function ComplexArrayInput({ label, values, onChange, fields, getKey }) {
297
301
  // Generate key from all field values
298
302
  return fields.map(f => item[f.name] || '').join('-') + `-${index}`;
299
303
  };
300
- return (jsxRuntime.jsxs("div", { className: styles$k.arrayInput, children: [jsxRuntime.jsx("h3", { className: styles$k.arrayInputLabel, children: label }), values.map((value, index) => (jsxRuntime.jsxs("div", { className: `${styles$k.arrayInputItem} ${fields.length > 1 ? styles$k.complexItem : ''}`, children: [jsxRuntime.jsx("div", { className: styles$k.fieldsWrapper, children: fields.map((field) => (jsxRuntime.jsx(TextInput, { value: value[field.name] || '', onChange: (newValue) => handleChange(index, field.name, newValue), label: field.label, type: field.type, placeholder: field.placeholder }, field.name))) }), jsxRuntime.jsx(Button, { variant: "ghost", size: "small", onClick: () => handleRemove(index), children: "Remove" })] }, generateKey(value, index)))), jsxRuntime.jsxs(Button, { variant: "primary", size: "small", onClick: handleAdd, children: ["Add ", label] })] }));
304
+ return (jsxRuntime.jsxs("div", { className: styles$k.arrayInput, children: [jsxRuntime.jsx("h3", { className: styles$k.arrayInputLabel, children: label }), jsxRuntime.jsx("div", { children: jsxRuntime.jsx(framerMotion.AnimatePresence, { children: values.map((value, index) => (jsxRuntime.jsxs(framerMotion.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: {
305
+ duration: 0.3,
306
+ ease: "easeInOut",
307
+ layout: { duration: 0.2 }
308
+ }, children: [jsxRuntime.jsx("div", { className: styles$k.fieldsWrapper, children: fields.map((field) => (jsxRuntime.jsx("div", { style: inputStyle, children: jsxRuntime.jsx(TextInput, { value: value[field.name] || '', onChange: (newValue) => handleChange(index, field.name, newValue), label: field.label, type: field.type, placeholder: field.placeholder }) }, field.name))) }), jsxRuntime.jsx(Button, { variant: "ghost", size: "small", onClick: () => handleRemove(index), "aria-label": "Remove item", className: styles$k.removeButton, children: jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: jsxRuntime.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)))) }) }), jsxRuntime.jsxs(Button, { variant: "primary", size: "small", onClick: handleAdd, className: styles$k.addButton, children: ["Add ", label] })] }));
301
309
  }
302
310
 
303
311
  var styles$j = {"checkboxLabel":"Checkbox-module_checkboxLabel__4tBVg","checkbox":"Checkbox-module_checkbox__BbJul","checkboxText":"Checkbox-module_checkboxText__oJsc9"};
@@ -846,8 +854,15 @@ var styles$e = {"toggleContainer":"Toggle-module_toggleContainer__QxqQb","toggle
846
854
  * @returns {JSX.Element} The rendered Toggle component
847
855
  */
848
856
  function Toggle(props) {
849
- const { isOn, onToggle, leftLabel, rightLabel, leftIcon, rightIcon } = props;
850
- return (jsxRuntime.jsxs("div", { className: styles$e.toggleContainer, children: [jsxRuntime.jsxs("button", { className: `${styles$e.toggleButton} ${!isOn ? styles$e.active : ''}`, onClick: () => onToggle(false), children: [leftIcon, leftLabel] }), jsxRuntime.jsxs("button", { className: `${styles$e.toggleButton} ${isOn ? styles$e.active : ''}`, onClick: () => onToggle(true), children: [rightIcon, rightLabel] })] }));
857
+ const { isOn, onToggle, leftLabel, rightLabel, leftIcon, rightIcon, className, style } = props;
858
+ // Ensure content stays centered by merging styles
859
+ const buttonStyle = {
860
+ display: 'flex',
861
+ alignItems: 'center',
862
+ justifyContent: 'center',
863
+ ...style
864
+ };
865
+ return (jsxRuntime.jsxs("div", { className: `${styles$e.toggleContainer} ${className || ''}`, children: [jsxRuntime.jsxs("button", { className: `${styles$e.toggleButton} ${!isOn ? styles$e.active : ''}`, onClick: () => onToggle(false), style: buttonStyle, children: [leftIcon, leftLabel] }), jsxRuntime.jsxs("button", { className: `${styles$e.toggleButton} ${isOn ? styles$e.active : ''}`, onClick: () => onToggle(true), style: buttonStyle, children: [rightIcon, rightLabel] })] }));
851
866
  }
852
867
 
853
868
  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"};
@@ -1199,7 +1214,8 @@ const EditFAB = ({ canEdit, isEditMode, hasUnsavedChanges = false, isSaving = fa
1199
1214
 
1200
1215
  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"};
1201
1216
 
1202
- const filterOptions = [
1217
+ // Default filter options for backwards compatibility
1218
+ const defaultFilterOptions = [
1203
1219
  { value: 'all', label: 'All', icon: FiSearch },
1204
1220
  { value: 'projects', label: 'Projects', icon: FiFolder },
1205
1221
  { value: 'clients', label: 'Clients', icon: FiUsers },
@@ -1207,16 +1223,18 @@ const filterOptions = [
1207
1223
  { value: 'interactions', label: 'Interactions', icon: FiMessageSquare },
1208
1224
  { value: 'team', label: 'Team', icon: FiUserPlus },
1209
1225
  ];
1210
- const entityIcons = {
1226
+ const defaultEntityIcons = {
1211
1227
  projects: FiFolder,
1212
1228
  clients: FiUsers,
1213
1229
  contacts: FiBook,
1214
1230
  interactions: FiMessageSquare,
1215
1231
  team: FiUserPlus,
1216
1232
  };
1217
- const SearchBar = ({ className, placeholder = "Search (Ctrl+Space)...", onSearch, onResultClick, onClear, debounceDelay = 300, minSearchLength = 2, showFilter = true, enableKeyboardShortcut = true }) => {
1233
+ const SearchBar = ({ className, placeholder = "Search (Ctrl+Space)...", onSearch, onResultClick, onClear, debounceDelay = 300, minSearchLength = 2, showFilter = true, enableKeyboardShortcut = true, filterOptions: customFilterOptions, entityIcons: customEntityIcons }) => {
1234
+ const filterOptions = customFilterOptions ?? defaultFilterOptions;
1235
+ const entityIcons = customEntityIcons ?? defaultEntityIcons;
1218
1236
  const [query, setQuery] = React.useState('');
1219
- const [filter, setFilter] = React.useState('all');
1237
+ const [filter, setFilter] = React.useState(filterOptions[0]?.value ?? 'all');
1220
1238
  const [results, setResults] = React.useState([]);
1221
1239
  const [isLoading, setIsLoading] = React.useState(false);
1222
1240
  const [isDropdownOpen, setIsDropdownOpen] = React.useState(false);
@@ -1545,15 +1563,16 @@ function SiJira (props) {
1545
1563
 
1546
1564
  var styles$6 = {"tabs":"Tabs-module_tabs__Vlvn7","tab":"Tabs-module_tab__uQKim","tabIcon":"Tabs-module_tabIcon__AgN-O"};
1547
1565
 
1548
- const tabs = [
1566
+ // Default tabs for backwards compatibility
1567
+ const defaultTabs = [
1549
1568
  { id: 'details', icon: FiInfo, label: 'Dettagli' },
1550
1569
  { id: 'github', icon: FiGithub, label: 'GitHub' },
1551
1570
  { id: 'jira', icon: SiJira, label: 'Jira' },
1552
1571
  { id: 'functional', icon: FiInfo, label: 'Analisi funzionale' }
1553
1572
  ];
1554
- const Tabs = ({ activeTab, onTabChange }) => {
1555
- return (jsxRuntime.jsx("div", { className: styles$6.tabs, children: tabs.map((tab) => {
1556
- const Icon = tab.icon;
1573
+ const Tabs = ({ activeTab, onTabChange, tabs: customTabs, className = '' }) => {
1574
+ const tabs = customTabs ?? defaultTabs;
1575
+ return (jsxRuntime.jsx("div", { className: `${styles$6.tabs} ${className}`, children: tabs.map((tab) => {
1557
1576
  const isActive = activeTab === tab.id;
1558
1577
  return (jsxRuntime.jsxs(framerMotion.motion.button, { className: styles$6.tab, "data-active": isActive, onClick: () => onTabChange(tab.id), style: { position: 'relative' }, children: [jsxRuntime.jsx(framerMotion.motion.div, { animate: {
1559
1578
  rotate: isActive ? [0, -10, 10, -5, 5, 0] : 0,
@@ -1562,7 +1581,7 @@ const Tabs = ({ activeTab, onTabChange }) => {
1562
1581
  duration: 0.5,
1563
1582
  ease: 'easeInOut'
1564
1583
  }
1565
- }, children: jsxRuntime.jsx(Icon, { className: styles$6.tabIcon }) }), jsxRuntime.jsx("span", { children: tab.label })] }, tab.id));
1584
+ }, children: tab.icon && (typeof tab.icon === 'function' ? (jsxRuntime.jsx("span", { className: styles$6.tabIcon, children: React.createElement(tab.icon) })) : (jsxRuntime.jsx("span", { className: styles$6.tabIcon, children: tab.icon }))) }), jsxRuntime.jsx("span", { children: tab.label })] }, tab.id));
1566
1585
  }) }));
1567
1586
  };
1568
1587
 
@@ -2246,11 +2265,22 @@ const BooleansHeatmap = ({ data, habitName, width = 800, height = 200, habitColo
2246
2265
  var styles$1 = {"container":"SunburstChart-module_container__w1ZYc","title":"SunburstChart-module_title__T6Ak7","chart":"SunburstChart-module_chart__BFM6E","tooltip":"SunburstChart-module_tooltip__TuTAN"};
2247
2266
 
2248
2267
  const COLOR_PALETTE = [
2249
- '#d4af37', '#FFD700', '#FFA500', '#FF8C00',
2250
- '#FF6347', '#DC143C', '#8B4513', '#A0522D',
2251
- '#DEB887', '#F4A460', '#D2691E', '#CD853F'
2268
+ '#6366f1', '#8b5cf6', '#06b6d4', '#10b981',
2269
+ '#f59e0b', '#ef4444', '#ec4899', '#84cc16',
2270
+ '#f97316', '#3b82f6', '#14b8a6', '#f59e0b'
2252
2271
  ];
2253
- const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Chart', tagColors = {} }) => {
2272
+ // Calculate text color based on background luminance for optimal contrast
2273
+ const getTextColor$1 = (backgroundColor) => {
2274
+ const color = d3__namespace.color(backgroundColor);
2275
+ if (!color)
2276
+ return '#ffffff';
2277
+ const rgb = color.rgb();
2278
+ // Calculate relative luminance using WCAG formula
2279
+ const luminance = (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;
2280
+ // Return white text for dark backgrounds, black for light backgrounds
2281
+ return luminance > 0.5 ? '#000000' : '#ffffff';
2282
+ };
2283
+ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Chart', tagColors = {}, unit = 'items', centerLabel }) => {
2254
2284
  const svgRef = React.useRef(null);
2255
2285
  const colorMap = React.useRef(new Map()).current;
2256
2286
  const colorIndex = React.useRef(0);
@@ -2293,7 +2323,8 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
2293
2323
  .startAngle(d => d.x0)
2294
2324
  .endAngle(d => d.x1)
2295
2325
  .innerRadius(d => Math.sqrt(d.y0))
2296
- .outerRadius(d => Math.sqrt(d.y1));
2326
+ .outerRadius(d => Math.sqrt(d.y1))
2327
+ .cornerRadius(3);
2297
2328
  const tooltip = d3__namespace.select('body').append('div')
2298
2329
  .attr('class', styles$1.tooltip)
2299
2330
  .style('opacity', 0)
@@ -2312,11 +2343,20 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
2312
2343
  .attr('stroke', 'var(--bg-primary)')
2313
2344
  .attr('stroke-width', 2)
2314
2345
  .style('cursor', 'pointer')
2346
+ .style('filter', 'drop-shadow(0 1px 3px rgba(0,0,0,0.12))')
2315
2347
  .on('mouseover', function (event, d) {
2348
+ const hoverArc = d3__namespace.arc()
2349
+ .startAngle(d => d.x0)
2350
+ .endAngle(d => d.x1)
2351
+ .innerRadius(d => Math.sqrt(d.y0) - 2)
2352
+ .outerRadius(d => Math.sqrt(d.y1) + 4)
2353
+ .cornerRadius(3);
2316
2354
  d3__namespace.select(this)
2317
2355
  .transition()
2318
- .duration(200)
2319
- .style('opacity', 0.8);
2356
+ .duration(150)
2357
+ .attr('d', d => hoverArc(d))
2358
+ .style('filter', 'drop-shadow(0 2px 8px rgba(0,0,0,0.2))')
2359
+ .style('opacity', 0.9);
2320
2360
  tooltip.transition()
2321
2361
  .duration(200)
2322
2362
  .style('opacity', 1);
@@ -2330,10 +2370,12 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
2330
2370
  .style('left', (event.pageX + 10) + 'px')
2331
2371
  .style('top', (event.pageY - 28) + 'px');
2332
2372
  })
2333
- .on('mouseout', function () {
2373
+ .on('mouseout', function (event, d) {
2334
2374
  d3__namespace.select(this)
2335
2375
  .transition()
2336
- .duration(200)
2376
+ .duration(150)
2377
+ .attr('d', d => arc(d))
2378
+ .style('filter', 'drop-shadow(0 1px 3px rgba(0,0,0,0.12))')
2337
2379
  .style('opacity', 1);
2338
2380
  tooltip.transition()
2339
2381
  .duration(500)
@@ -2343,9 +2385,32 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
2343
2385
  const angle = d.x1 - d.x0;
2344
2386
  return angle > 0.15 && d.depth <= 2;
2345
2387
  };
2346
- g.selectAll('text')
2388
+ // Calculate average background color for center text contrast
2389
+ const allSegments = nodes.filter(d => d.depth === 1);
2390
+ const avgColor = allSegments.length > 0 ? d3__namespace.interpolateRgb.gamma(2.2)(...allSegments.map(d => getColor(d.data.name, 1)))(0.5) : '#ffffff';
2391
+ const centerTextColor = getTextColor$1(avgColor);
2392
+ // Add center text
2393
+ g.append('text')
2394
+ .attr('text-anchor', 'middle')
2395
+ .attr('alignment-baseline', 'middle')
2396
+ .attr('font-size', '18px')
2397
+ .attr('font-weight', 'bold')
2398
+ .attr('fill', centerTextColor)
2399
+ .style('text-shadow', centerTextColor === '#ffffff' ? '0 1px 3px rgba(0,0,0,0.5)' : '0 1px 3px rgba(255,255,255,0.5)')
2400
+ .text(centerLabel || data.name || 'Total');
2401
+ g.append('text')
2402
+ .attr('text-anchor', 'middle')
2403
+ .attr('alignment-baseline', 'middle')
2404
+ .attr('y', 20)
2405
+ .attr('font-size', '14px')
2406
+ .attr('font-weight', '500')
2407
+ .attr('fill', centerTextColor)
2408
+ .style('text-shadow', centerTextColor === '#ffffff' ? '0 1px 2px rgba(0,0,0,0.4)' : '0 1px 2px rgba(255,255,255,0.4)')
2409
+ .text(`${(root.value || 0).toLocaleString()} ${unit}`);
2410
+ g.selectAll('text.segment-label')
2347
2411
  .data(nodes.filter(d => d.depth > 0 && d.value && d.value > 0 && shouldDisplayLabel(d)))
2348
2412
  .enter().append('text')
2413
+ .attr('class', 'segment-label')
2349
2414
  .attr('transform', d => {
2350
2415
  const angle = (d.x0 + d.x1) / 2;
2351
2416
  const radius = (Math.sqrt(d.y0) + Math.sqrt(d.y1)) / 2;
@@ -2355,11 +2420,32 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
2355
2420
  })
2356
2421
  .attr('text-anchor', 'middle')
2357
2422
  .attr('alignment-baseline', 'middle')
2358
- .attr('font-size', '12px')
2359
- .attr('fill', 'var(--text-inverse)')
2360
- .attr('font-weight', 'var(--font-medium)')
2423
+ .attr('font-size', d => d.depth === 1 ? '13px' : '11px')
2424
+ .attr('fill', d => {
2425
+ let ancestor = d;
2426
+ while (ancestor.depth > 1 && ancestor.parent) {
2427
+ ancestor = ancestor.parent;
2428
+ }
2429
+ const segmentColor = getColor(ancestor.data.name, d.depth);
2430
+ return getTextColor$1(segmentColor);
2431
+ })
2432
+ .attr('font-weight', '600')
2361
2433
  .style('pointer-events', 'none')
2362
- .text(d => d.data.name.substring(0, 10));
2434
+ .style('text-shadow', d => {
2435
+ let ancestor = d;
2436
+ while (ancestor.depth > 1 && ancestor.parent) {
2437
+ ancestor = ancestor.parent;
2438
+ }
2439
+ const segmentColor = getColor(ancestor.data.name, d.depth);
2440
+ const textColor = getTextColor$1(segmentColor);
2441
+ // Use contrasting shadow color
2442
+ const shadowColor = textColor === '#ffffff' ? 'rgba(0,0,0,0.5)' : 'rgba(255,255,255,0.5)';
2443
+ return `0 1px 2px ${shadowColor}`;
2444
+ })
2445
+ .text(d => {
2446
+ const maxLength = d.depth === 1 ? 12 : 8;
2447
+ return d.data.name.substring(0, maxLength);
2448
+ });
2363
2449
  return () => {
2364
2450
  tooltip.remove();
2365
2451
  };
@@ -2370,12 +2456,23 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
2370
2456
  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"};
2371
2457
 
2372
2458
  const DEFAULT_COLORS = [
2373
- '#d4af37', '#FFD700', '#FFA500', '#FF8C00',
2374
- '#FF6347', '#DC143C', '#8B4513', '#A0522D',
2375
- '#DEB887', '#F4A460', '#D2691E', '#CD853F',
2376
- '#B8860B', '#DAA520', '#F0E68C', '#BDB76B'
2459
+ '#6366f1', '#8b5cf6', '#06b6d4', '#10b981',
2460
+ '#f59e0b', '#ef4444', '#ec4899', '#84cc16',
2461
+ '#f97316', '#3b82f6', '#8b5cf6', '#14b8a6',
2462
+ '#f59e0b', '#ef4444', '#06b6d4', '#10b981'
2377
2463
  ];
2378
- const PieChart = ({ data, width = 400, height = 400, title = 'Distribution', showLegend = true }) => {
2464
+ // Calculate text color based on background luminance for optimal contrast
2465
+ const getTextColor = (backgroundColor) => {
2466
+ const color = d3__namespace.color(backgroundColor);
2467
+ if (!color)
2468
+ return '#ffffff';
2469
+ const rgb = color.rgb();
2470
+ // Calculate relative luminance using WCAG formula
2471
+ const luminance = (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;
2472
+ // Return white text for dark backgrounds, black for light backgrounds
2473
+ return luminance > 0.5 ? '#000000' : '#ffffff';
2474
+ };
2475
+ const PieChart = ({ data, width = 400, height = 400, title = 'Distribution', showLegend = true, unit = 'items', centerLabel }) => {
2379
2476
  const svgRef = React.useRef(null);
2380
2477
  const radius = Math.min(width, height) / 2 - 20;
2381
2478
  React.useEffect(() => {
@@ -2389,8 +2486,9 @@ const PieChart = ({ data, width = 400, height = 400, title = 'Distribution', sho
2389
2486
  .value(d => d.value)
2390
2487
  .sort(null);
2391
2488
  const arc = d3__namespace.arc()
2392
- .innerRadius(0)
2393
- .outerRadius(radius);
2489
+ .innerRadius(radius * 0.4)
2490
+ .outerRadius(radius)
2491
+ .cornerRadius(4);
2394
2492
  const labelArc = d3__namespace.arc()
2395
2493
  .innerRadius(radius * 0.7)
2396
2494
  .outerRadius(radius * 0.7);
@@ -2408,13 +2506,19 @@ const PieChart = ({ data, width = 400, height = 400, title = 'Distribution', sho
2408
2506
  .attr('d', d => arc(d))
2409
2507
  .attr('fill', (d, i) => d.data.color || DEFAULT_COLORS[i % DEFAULT_COLORS.length])
2410
2508
  .attr('stroke', 'var(--bg-primary)')
2411
- .attr('stroke-width', 2)
2509
+ .attr('stroke-width', 3)
2412
2510
  .style('cursor', 'pointer')
2511
+ .style('filter', 'drop-shadow(0 2px 8px rgba(0,0,0,0.15))')
2413
2512
  .on('mouseover', function (event, d) {
2513
+ const hoverArc = d3__namespace.arc()
2514
+ .innerRadius(radius * 0.4)
2515
+ .outerRadius(radius * 1.05)
2516
+ .cornerRadius(4);
2414
2517
  d3__namespace.select(this)
2415
2518
  .transition()
2416
2519
  .duration(200)
2417
- .style('opacity', 0.8);
2520
+ .attr('d', d => hoverArc(d))
2521
+ .style('filter', 'drop-shadow(0 4px 12px rgba(0,0,0,0.25))');
2418
2522
  tooltip.transition()
2419
2523
  .duration(200)
2420
2524
  .style('opacity', 1);
@@ -2427,27 +2531,59 @@ const PieChart = ({ data, width = 400, height = 400, title = 'Distribution', sho
2427
2531
  .style('left', (event.pageX + 10) + 'px')
2428
2532
  .style('top', (event.pageY - 28) + 'px');
2429
2533
  })
2430
- .on('mouseout', function () {
2534
+ .on('mouseout', function (event, d) {
2431
2535
  d3__namespace.select(this)
2432
2536
  .transition()
2433
2537
  .duration(200)
2434
- .style('opacity', 1);
2538
+ .attr('d', d => arc(d))
2539
+ .style('filter', 'drop-shadow(0 2px 8px rgba(0,0,0,0.15))');
2435
2540
  tooltip.transition()
2436
2541
  .duration(500)
2437
2542
  .style('opacity', 0);
2438
2543
  });
2439
- arcs.filter(d => (d.endAngle - d.startAngle) > 0.3)
2544
+ // Calculate average background color for center text contrast
2545
+ const avgColor = data.length > 0 ? d3__namespace.interpolateRgb.gamma(2.2)(...data.map((d, i) => d.color || DEFAULT_COLORS[i % DEFAULT_COLORS.length]))(0.5) : '#ffffff';
2546
+ const centerTextColor = getTextColor(avgColor);
2547
+ // Add center text for donut
2548
+ g.append('text')
2549
+ .attr('text-anchor', 'middle')
2550
+ .attr('alignment-baseline', 'middle')
2551
+ .attr('font-size', '20px')
2552
+ .attr('font-weight', 'bold')
2553
+ .attr('fill', centerTextColor)
2554
+ .style('text-shadow', centerTextColor === '#ffffff' ? '0 1px 3px rgba(0,0,0,0.5)' : '0 1px 3px rgba(255,255,255,0.5)')
2555
+ .text(centerLabel || 'Total');
2556
+ g.append('text')
2557
+ .attr('text-anchor', 'middle')
2558
+ .attr('alignment-baseline', 'middle')
2559
+ .attr('y', 22)
2560
+ .attr('font-size', '16px')
2561
+ .attr('font-weight', '600')
2562
+ .attr('fill', centerTextColor)
2563
+ .style('text-shadow', centerTextColor === '#ffffff' ? '0 1px 2px rgba(0,0,0,0.4)' : '0 1px 2px rgba(255,255,255,0.4)')
2564
+ .text(`${total.toLocaleString()} ${unit}`);
2565
+ arcs.filter(d => (d.endAngle - d.startAngle) > 0.2)
2440
2566
  .append('text')
2441
2567
  .attr('transform', d => `translate(${labelArc.centroid(d)})`)
2442
2568
  .attr('text-anchor', 'middle')
2443
2569
  .attr('alignment-baseline', 'middle')
2444
- .attr('font-size', '12px')
2445
- .attr('fill', 'var(--text-inverse)')
2446
- .attr('font-weight', 'var(--font-semibold)')
2570
+ .attr('font-size', '13px')
2571
+ .attr('fill', (d, i) => {
2572
+ const segmentColor = d.data.color || DEFAULT_COLORS[i % DEFAULT_COLORS.length];
2573
+ return getTextColor(segmentColor);
2574
+ })
2575
+ .attr('font-weight', '600')
2447
2576
  .style('pointer-events', 'none')
2577
+ .style('text-shadow', (d, i) => {
2578
+ const segmentColor = d.data.color || DEFAULT_COLORS[i % DEFAULT_COLORS.length];
2579
+ const textColor = getTextColor(segmentColor);
2580
+ // Use contrasting shadow color
2581
+ const shadowColor = textColor === '#ffffff' ? 'rgba(0,0,0,0.4)' : 'rgba(255,255,255,0.4)';
2582
+ return `0 1px 2px ${shadowColor}`;
2583
+ })
2448
2584
  .text(d => {
2449
2585
  const percentage = ((d.data.value / total) * 100);
2450
- return percentage > 5 ? `${percentage.toFixed(1)}%` : '';
2586
+ return percentage > 8 ? `${percentage.toFixed(0)}%` : '';
2451
2587
  });
2452
2588
  return () => {
2453
2589
  tooltip.remove();