mithril-materialized 3.11.0 → 3.13.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/dist/advanced.css +5 -0
- package/dist/collection.d.ts +2 -2
- package/dist/components.css +5 -0
- package/dist/core.css +5 -0
- package/dist/forms.css +5 -0
- package/dist/index.css +460 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +253 -8
- package/dist/index.js +253 -7
- package/dist/index.min.css +2 -2
- package/dist/index.umd.js +253 -7
- package/dist/likert-scale.d.ts +70 -0
- package/dist/utilities.css +5 -0
- package/package.json +1 -1
- package/sass/components/_global.scss +7 -0
- package/sass/components/_likert-scale.scss +610 -0
- package/sass/materialize.scss +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -1635,7 +1635,7 @@ const avatarIsImage = (avatar = '') => /\./.test(avatar);
|
|
|
1635
1635
|
const ListItem = () => {
|
|
1636
1636
|
return {
|
|
1637
1637
|
view: ({ attrs: { item, mode } }) => {
|
|
1638
|
-
const { title, content
|
|
1638
|
+
const { title, content, active, iconName, avatar, className, onclick } = item;
|
|
1639
1639
|
return mode === CollectionMode.AVATAR
|
|
1640
1640
|
? m('li.collection-item.avatar', {
|
|
1641
1641
|
className: active ? 'active' : '',
|
|
@@ -1645,12 +1645,21 @@ const ListItem = () => {
|
|
|
1645
1645
|
? m('img.circle', { src: avatar })
|
|
1646
1646
|
: m('i.material-icons.circle', { className }, avatar),
|
|
1647
1647
|
m('span.title', title),
|
|
1648
|
-
m('p', m.trust(content)),
|
|
1649
|
-
m(SecondaryContent, item),
|
|
1650
|
-
])
|
|
1648
|
+
content ? (typeof content === 'string' ? m('p', m.trust(content)) : m('p', content)) : undefined,
|
|
1649
|
+
iconName ? m(SecondaryContent, item) : undefined,
|
|
1650
|
+
].filter(Boolean))
|
|
1651
1651
|
: m('li.collection-item', {
|
|
1652
1652
|
className: active ? 'active' : '',
|
|
1653
|
-
|
|
1653
|
+
onclick: onclick ? () => onclick(item) : undefined,
|
|
1654
|
+
}, content
|
|
1655
|
+
? m('div', [
|
|
1656
|
+
m('div', title),
|
|
1657
|
+
typeof content === 'string' ? m('p.secondary-text', content) : content,
|
|
1658
|
+
iconName ? m(SecondaryContent, item) : undefined,
|
|
1659
|
+
].filter(Boolean))
|
|
1660
|
+
: iconName
|
|
1661
|
+
? m('div', [title, m(SecondaryContent, item)])
|
|
1662
|
+
: title);
|
|
1654
1663
|
},
|
|
1655
1664
|
};
|
|
1656
1665
|
};
|
|
@@ -10751,12 +10760,12 @@ const Rating = () => {
|
|
|
10751
10760
|
const RatingItem = () => {
|
|
10752
10761
|
return {
|
|
10753
10762
|
view: ({ attrs }) => {
|
|
10754
|
-
const { index, displayValue, step, icons, allowHalfSteps, disabled, onclick, onmouseover } = attrs;
|
|
10763
|
+
const { index, displayValue, step, icons, allowHalfSteps, disabled, showTooltip, tooltipLabel, onclick, onmouseover, } = attrs;
|
|
10755
10764
|
const itemValue = (index + 1) * step;
|
|
10756
10765
|
// Calculate fill state based on displayValue vs itemValue
|
|
10757
10766
|
const diff = displayValue - itemValue;
|
|
10758
10767
|
const fillState = diff >= 0 ? 'full' : allowHalfSteps && diff >= -step / 2 ? 'half' : 'empty';
|
|
10759
|
-
return m('.rating__item', {
|
|
10768
|
+
return m('.rating__item.no-select', {
|
|
10760
10769
|
className: [
|
|
10761
10770
|
fillState === 'full' ? 'rating__item--filled' : '',
|
|
10762
10771
|
fillState === 'half' ? 'rating__item--half' : '',
|
|
@@ -10777,6 +10786,8 @@ const Rating = () => {
|
|
|
10777
10786
|
clipPath: fillState === 'half' ? 'inset(0 50% 0 0)' : undefined,
|
|
10778
10787
|
},
|
|
10779
10788
|
}, typeof icons.filled === 'string' ? icons.filled : m(icons.filled)),
|
|
10789
|
+
// Tooltip
|
|
10790
|
+
showTooltip && tooltipLabel && m('.rating__tooltip', tooltipLabel),
|
|
10780
10791
|
]);
|
|
10781
10792
|
},
|
|
10782
10793
|
};
|
|
@@ -10848,6 +10859,7 @@ const Rating = () => {
|
|
|
10848
10859
|
},
|
|
10849
10860
|
// Array.from({ length: itemCount }, (_, i) => renderRatingItem(attrs, i))
|
|
10850
10861
|
[...Array(itemCount)].map((_, i) => {
|
|
10862
|
+
var _a;
|
|
10851
10863
|
const itemValue = (i + 1) * step;
|
|
10852
10864
|
return m(RatingItem, {
|
|
10853
10865
|
key: `rating-item-${i}`,
|
|
@@ -10857,6 +10869,8 @@ const Rating = () => {
|
|
|
10857
10869
|
icons: Object.assign(Object.assign({}, DEFAULT_ICONS), attrs.icon),
|
|
10858
10870
|
allowHalfSteps: attrs.allowHalfSteps,
|
|
10859
10871
|
disabled: attrs.disabled,
|
|
10872
|
+
showTooltip: attrs.showTooltips,
|
|
10873
|
+
tooltipLabel: (_a = attrs.tooltipLabels) === null || _a === void 0 ? void 0 : _a[i],
|
|
10860
10874
|
onclick: () => handleItemClick(attrs, itemValue),
|
|
10861
10875
|
onmouseover: () => handleItemHover(attrs, itemValue),
|
|
10862
10876
|
});
|
|
@@ -10872,6 +10886,237 @@ const Rating = () => {
|
|
|
10872
10886
|
};
|
|
10873
10887
|
};
|
|
10874
10888
|
|
|
10889
|
+
/** Create a LikertScale component */
|
|
10890
|
+
const LikertScale = () => {
|
|
10891
|
+
const state = {
|
|
10892
|
+
id: uniqueId(),
|
|
10893
|
+
groupId: uniqueId(),
|
|
10894
|
+
internalValue: undefined,
|
|
10895
|
+
isFocused: false,
|
|
10896
|
+
};
|
|
10897
|
+
const isControlled = (attrs) => typeof attrs.value !== 'undefined' && typeof attrs.onchange === 'function';
|
|
10898
|
+
const getCurrentValue = (attrs) => {
|
|
10899
|
+
var _a, _b;
|
|
10900
|
+
const controlled = isControlled(attrs);
|
|
10901
|
+
const isNonInteractive = attrs.readonly || attrs.disabled;
|
|
10902
|
+
if (controlled) {
|
|
10903
|
+
return attrs.value;
|
|
10904
|
+
}
|
|
10905
|
+
// Non-interactive components: prefer defaultValue, fallback to value
|
|
10906
|
+
if (isNonInteractive) {
|
|
10907
|
+
return (_a = attrs.defaultValue) !== null && _a !== void 0 ? _a : attrs.value;
|
|
10908
|
+
}
|
|
10909
|
+
// Interactive uncontrolled: use internal state (user can change it)
|
|
10910
|
+
return (_b = state.internalValue) !== null && _b !== void 0 ? _b : attrs.defaultValue;
|
|
10911
|
+
};
|
|
10912
|
+
const getLabelText = (value, min, max, getLabelFn) => {
|
|
10913
|
+
if (getLabelFn && value !== undefined) {
|
|
10914
|
+
return getLabelFn(value, min, max);
|
|
10915
|
+
}
|
|
10916
|
+
if (value === undefined) {
|
|
10917
|
+
return `No selection, please choose a value between ${min} and ${max}`;
|
|
10918
|
+
}
|
|
10919
|
+
return `Selected ${value} out of ${min} to ${max}`;
|
|
10920
|
+
};
|
|
10921
|
+
const getSizeClass = (size = 'medium') => {
|
|
10922
|
+
switch (size) {
|
|
10923
|
+
case 'small':
|
|
10924
|
+
return 'likert-scale--small';
|
|
10925
|
+
case 'large':
|
|
10926
|
+
return 'likert-scale--large';
|
|
10927
|
+
default:
|
|
10928
|
+
return 'likert-scale--medium';
|
|
10929
|
+
}
|
|
10930
|
+
};
|
|
10931
|
+
const getDensityClass = (density = 'standard') => {
|
|
10932
|
+
switch (density) {
|
|
10933
|
+
case 'compact':
|
|
10934
|
+
return 'likert-scale--compact';
|
|
10935
|
+
case 'comfortable':
|
|
10936
|
+
return 'likert-scale--comfortable';
|
|
10937
|
+
default:
|
|
10938
|
+
return 'likert-scale--standard';
|
|
10939
|
+
}
|
|
10940
|
+
};
|
|
10941
|
+
const getLayoutClass = (layout = 'responsive') => {
|
|
10942
|
+
switch (layout) {
|
|
10943
|
+
case 'horizontal':
|
|
10944
|
+
return 'likert-scale--horizontal';
|
|
10945
|
+
case 'vertical':
|
|
10946
|
+
return 'likert-scale--vertical';
|
|
10947
|
+
default:
|
|
10948
|
+
return 'likert-scale--responsive';
|
|
10949
|
+
}
|
|
10950
|
+
};
|
|
10951
|
+
const handleChange = (attrs, newValue) => {
|
|
10952
|
+
var _a;
|
|
10953
|
+
if (attrs.readonly || attrs.disabled)
|
|
10954
|
+
return;
|
|
10955
|
+
if (!isControlled(attrs)) {
|
|
10956
|
+
state.internalValue = newValue;
|
|
10957
|
+
}
|
|
10958
|
+
(_a = attrs.onchange) === null || _a === void 0 ? void 0 : _a.call(attrs, newValue);
|
|
10959
|
+
};
|
|
10960
|
+
const handleKeyDown = (attrs, e) => {
|
|
10961
|
+
if (attrs.readonly || attrs.disabled)
|
|
10962
|
+
return;
|
|
10963
|
+
const min = attrs.min || 1;
|
|
10964
|
+
const max = attrs.max || 5;
|
|
10965
|
+
const step = attrs.step || 1;
|
|
10966
|
+
const currentValue = getCurrentValue(attrs);
|
|
10967
|
+
let newValue = currentValue;
|
|
10968
|
+
switch (e.key) {
|
|
10969
|
+
case 'ArrowRight':
|
|
10970
|
+
case 'ArrowUp':
|
|
10971
|
+
e.preventDefault();
|
|
10972
|
+
newValue = currentValue !== undefined ? Math.min(max, currentValue + step) : min;
|
|
10973
|
+
break;
|
|
10974
|
+
case 'ArrowLeft':
|
|
10975
|
+
case 'ArrowDown':
|
|
10976
|
+
e.preventDefault();
|
|
10977
|
+
newValue = currentValue !== undefined ? Math.max(min, currentValue - step) : min;
|
|
10978
|
+
break;
|
|
10979
|
+
case 'Home':
|
|
10980
|
+
e.preventDefault();
|
|
10981
|
+
newValue = min;
|
|
10982
|
+
break;
|
|
10983
|
+
case 'End':
|
|
10984
|
+
e.preventDefault();
|
|
10985
|
+
newValue = max;
|
|
10986
|
+
break;
|
|
10987
|
+
default:
|
|
10988
|
+
return;
|
|
10989
|
+
}
|
|
10990
|
+
if (newValue !== currentValue) {
|
|
10991
|
+
handleChange(attrs, newValue);
|
|
10992
|
+
}
|
|
10993
|
+
};
|
|
10994
|
+
const LikertScaleItem = () => {
|
|
10995
|
+
return {
|
|
10996
|
+
view: ({ attrs }) => {
|
|
10997
|
+
const { value, currentValue, showNumber, showTooltip, tooltipLabel, groupId, name, disabled, readonly, onchange, } = attrs;
|
|
10998
|
+
const radioId = `${groupId}-${value}`;
|
|
10999
|
+
const isChecked = currentValue === value;
|
|
11000
|
+
return m('.likert-scale__item.no-select', {
|
|
11001
|
+
className: [
|
|
11002
|
+
isChecked ? 'likert-scale__item--checked' : '',
|
|
11003
|
+
disabled ? 'likert-scale__item--disabled' : '',
|
|
11004
|
+
readonly ? 'likert-scale__item--readonly' : '',
|
|
11005
|
+
]
|
|
11006
|
+
.filter(Boolean)
|
|
11007
|
+
.join(' '),
|
|
11008
|
+
}, [
|
|
11009
|
+
// Number label (optional)
|
|
11010
|
+
showNumber && m('.likert-scale__number', value),
|
|
11011
|
+
// Radio button input
|
|
11012
|
+
m('input[type=radio].likert-scale__input', {
|
|
11013
|
+
id: radioId,
|
|
11014
|
+
name: name || groupId,
|
|
11015
|
+
value: value,
|
|
11016
|
+
checked: isChecked,
|
|
11017
|
+
disabled: disabled || readonly,
|
|
11018
|
+
onchange: () => onchange(value),
|
|
11019
|
+
}),
|
|
11020
|
+
// Label for radio button
|
|
11021
|
+
m('label.likert-scale__label', {
|
|
11022
|
+
for: radioId,
|
|
11023
|
+
}),
|
|
11024
|
+
// Tooltip (optional)
|
|
11025
|
+
showTooltip && tooltipLabel && m('.likert-scale__tooltip', tooltipLabel),
|
|
11026
|
+
]);
|
|
11027
|
+
},
|
|
11028
|
+
};
|
|
11029
|
+
};
|
|
11030
|
+
return {
|
|
11031
|
+
oninit: ({ attrs }) => {
|
|
11032
|
+
const controlled = isControlled(attrs);
|
|
11033
|
+
const isNonInteractive = attrs.readonly || attrs.disabled;
|
|
11034
|
+
// Warn developer for improper controlled usage
|
|
11035
|
+
if (attrs.value !== undefined && !controlled && !isNonInteractive) {
|
|
11036
|
+
console.warn(`LikertScale component received 'value' prop without 'onchange' handler. ` +
|
|
11037
|
+
`Use 'defaultValue' for uncontrolled components or add 'onchange' for controlled components.`);
|
|
11038
|
+
}
|
|
11039
|
+
if (!controlled) {
|
|
11040
|
+
state.internalValue = attrs.defaultValue;
|
|
11041
|
+
}
|
|
11042
|
+
},
|
|
11043
|
+
view: ({ attrs }) => {
|
|
11044
|
+
const { min = 1, max = 5, step = 1, size = 'medium', density = 'standard', layout = 'responsive', className = '', style = {}, readonly = false, disabled = false, id = state.id, name, label, description, isMandatory, startLabel, middleLabel, endLabel, showNumbers = false, showTooltips = false, tooltipLabels, alignLabels = false } = attrs, ariaAttrs = __rest(attrs, ["min", "max", "step", "size", "density", "layout", "className", "style", "readonly", "disabled", "id", "name", "label", "description", "isMandatory", "startLabel", "middleLabel", "endLabel", "showNumbers", "showTooltips", "tooltipLabels", "alignLabels"]);
|
|
11045
|
+
const currentValue = getCurrentValue(attrs);
|
|
11046
|
+
const itemCount = Math.floor((max - min) / step) + 1;
|
|
11047
|
+
// Generate scale values
|
|
11048
|
+
const scaleValues = Array.from({ length: itemCount }, (_, i) => min + i * step);
|
|
11049
|
+
return m('.likert-scale', {
|
|
11050
|
+
className: [
|
|
11051
|
+
'likert-scale',
|
|
11052
|
+
getSizeClass(size),
|
|
11053
|
+
getDensityClass(density),
|
|
11054
|
+
getLayoutClass(layout),
|
|
11055
|
+
readonly ? 'likert-scale--readonly' : '',
|
|
11056
|
+
disabled ? 'likert-scale--disabled' : '',
|
|
11057
|
+
state.isFocused ? 'likert-scale--focused' : '',
|
|
11058
|
+
alignLabels ? 'likert-scale--aligned' : '',
|
|
11059
|
+
className,
|
|
11060
|
+
]
|
|
11061
|
+
.filter(Boolean)
|
|
11062
|
+
.join(' '),
|
|
11063
|
+
style,
|
|
11064
|
+
id,
|
|
11065
|
+
role: 'radiogroup',
|
|
11066
|
+
'aria-label': ariaAttrs['aria-label'] || attrs.ariaLabel || label || `Rating scale from ${min} to ${max}`,
|
|
11067
|
+
'aria-labelledby': ariaAttrs['aria-labelledby'],
|
|
11068
|
+
'aria-readonly': readonly,
|
|
11069
|
+
'aria-disabled': disabled,
|
|
11070
|
+
onkeydown: (e) => handleKeyDown(attrs, e),
|
|
11071
|
+
onfocus: () => {
|
|
11072
|
+
state.isFocused = true;
|
|
11073
|
+
},
|
|
11074
|
+
onblur: () => {
|
|
11075
|
+
state.isFocused = false;
|
|
11076
|
+
},
|
|
11077
|
+
tabindex: readonly || disabled ? -1 : 0,
|
|
11078
|
+
}, [
|
|
11079
|
+
// Label section (only text label, not the description)
|
|
11080
|
+
label &&
|
|
11081
|
+
m('.likert-scale__question-label', [
|
|
11082
|
+
m('span', label + (isMandatory ? ' *' : '')),
|
|
11083
|
+
description && m('.likert-scale__description', m.trust(description)),
|
|
11084
|
+
]),
|
|
11085
|
+
// Scale section container
|
|
11086
|
+
m('.likert-scale__scale-container', [
|
|
11087
|
+
// Scale items with numbers
|
|
11088
|
+
m('.likert-scale__scale', scaleValues.map((value) => m(LikertScaleItem, {
|
|
11089
|
+
key: `likert-item-${value}`,
|
|
11090
|
+
value,
|
|
11091
|
+
currentValue,
|
|
11092
|
+
showNumber: showNumbers,
|
|
11093
|
+
showTooltip: showTooltips,
|
|
11094
|
+
tooltipLabel: tooltipLabels === null || tooltipLabels === void 0 ? void 0 : tooltipLabels[value - min],
|
|
11095
|
+
groupId: state.groupId,
|
|
11096
|
+
name,
|
|
11097
|
+
disabled,
|
|
11098
|
+
readonly,
|
|
11099
|
+
onchange: (v) => handleChange(attrs, v),
|
|
11100
|
+
}))),
|
|
11101
|
+
// Scale anchors
|
|
11102
|
+
(startLabel || middleLabel || endLabel) &&
|
|
11103
|
+
m('.likert-scale__anchors', [
|
|
11104
|
+
startLabel && m('.likert-scale__anchor.likert-scale__anchor--start', startLabel),
|
|
11105
|
+
middleLabel && m('.likert-scale__anchor.likert-scale__anchor--middle', middleLabel),
|
|
11106
|
+
endLabel && m('.likert-scale__anchor.likert-scale__anchor--end', endLabel),
|
|
11107
|
+
]),
|
|
11108
|
+
]),
|
|
11109
|
+
// Screen reader text
|
|
11110
|
+
m('.likert-scale__sr-only', {
|
|
11111
|
+
className: 'likert-scale__sr-only',
|
|
11112
|
+
'aria-live': 'polite',
|
|
11113
|
+
'aria-atomic': 'true',
|
|
11114
|
+
}, getLabelText(currentValue, min, max, attrs.getLabelText)),
|
|
11115
|
+
]);
|
|
11116
|
+
},
|
|
11117
|
+
};
|
|
11118
|
+
};
|
|
11119
|
+
|
|
10875
11120
|
/**
|
|
10876
11121
|
* ToggleButton component.
|
|
10877
11122
|
*
|
|
@@ -11189,4 +11434,4 @@ const isValidationError = (result) => !isValidationSuccess(result);
|
|
|
11189
11434
|
// ============================================================================
|
|
11190
11435
|
// All types are already exported via individual export declarations above
|
|
11191
11436
|
|
|
11192
|
-
export { AnalogClock, AnchorItem, Autocomplete, Badge, Breadcrumb, BreadcrumbManager, Button, ButtonFactory, Carousel, CharacterCounter, Chips, CircularProgress, CodeBlock, Collapsible, CollapsibleItem, Collection, CollectionMode, ColorInput, DataTable, DatePicker, DigitalClock, DoubleRangeSlider, Dropdown, EmailInput, FileInput, FileUpload, FlatButton, FloatingActionButton, HelperText, Icon, IconButton, ImageList, InputCheckbox, Label, LargeButton, LinearProgress, ListItem, Mandatory, Masonry, MaterialBox, MaterialIcon, ModalPanel, NumberInput, Options, OptionsList, Pagination, PaginationControls, Parallax, PasswordInput, Pushpin, PushpinComponent, RadioButton, RadioButtons, RangeInput, Rating, RoundIconButton, SearchSelect, SecondaryContent, Select, Sidenav, SidenavItem, SidenavManager, SingleRangeSlider, SmallButton, Stepper, SubmitButton, Switch, Tabs, TextArea, TextInput, ThemeManager, ThemeSwitcher, ThemeToggle, TimePicker, TimeRangePicker, Timeline, Toast, ToastComponent, ToggleGroup, Tooltip, TooltipComponent, TreeView, UrlInput, Wizard, addLeadingZero, clearPortal, createBreadcrumb, formatTime, generateHourOptions, generateMinuteOptions, getDropdownStyles, getPortalContainer, initPushpins, initTooltips, isNumeric, isTimeDisabled, isValidationError, isValidationSuccess, padLeft, parseTime, range, releasePortalContainer, renderToPortal, scrollToValue, snapToNearestItem, sortOptions, timeToMinutes, toast, uniqueId, uuid4 };
|
|
11437
|
+
export { AnalogClock, AnchorItem, Autocomplete, Badge, Breadcrumb, BreadcrumbManager, Button, ButtonFactory, Carousel, CharacterCounter, Chips, CircularProgress, CodeBlock, Collapsible, CollapsibleItem, Collection, CollectionMode, ColorInput, DataTable, DatePicker, DigitalClock, DoubleRangeSlider, Dropdown, EmailInput, FileInput, FileUpload, FlatButton, FloatingActionButton, HelperText, Icon, IconButton, ImageList, InputCheckbox, Label, LargeButton, LikertScale, LinearProgress, ListItem, Mandatory, Masonry, MaterialBox, MaterialIcon, ModalPanel, NumberInput, Options, OptionsList, Pagination, PaginationControls, Parallax, PasswordInput, Pushpin, PushpinComponent, RadioButton, RadioButtons, RangeInput, Rating, RoundIconButton, SearchSelect, SecondaryContent, Select, Sidenav, SidenavItem, SidenavManager, SingleRangeSlider, SmallButton, Stepper, SubmitButton, Switch, Tabs, TextArea, TextInput, ThemeManager, ThemeSwitcher, ThemeToggle, TimePicker, TimeRangePicker, Timeline, Toast, ToastComponent, ToggleGroup, Tooltip, TooltipComponent, TreeView, UrlInput, Wizard, addLeadingZero, clearPortal, createBreadcrumb, formatTime, generateHourOptions, generateMinuteOptions, getDropdownStyles, getPortalContainer, initPushpins, initTooltips, isNumeric, isTimeDisabled, isValidationError, isValidationSuccess, padLeft, parseTime, range, releasePortalContainer, renderToPortal, scrollToValue, snapToNearestItem, sortOptions, timeToMinutes, toast, uniqueId, uuid4 };
|
package/dist/index.js
CHANGED
|
@@ -1637,7 +1637,7 @@ const avatarIsImage = (avatar = '') => /\./.test(avatar);
|
|
|
1637
1637
|
const ListItem = () => {
|
|
1638
1638
|
return {
|
|
1639
1639
|
view: ({ attrs: { item, mode } }) => {
|
|
1640
|
-
const { title, content
|
|
1640
|
+
const { title, content, active, iconName, avatar, className, onclick } = item;
|
|
1641
1641
|
return mode === exports.CollectionMode.AVATAR
|
|
1642
1642
|
? m('li.collection-item.avatar', {
|
|
1643
1643
|
className: active ? 'active' : '',
|
|
@@ -1647,12 +1647,21 @@ const ListItem = () => {
|
|
|
1647
1647
|
? m('img.circle', { src: avatar })
|
|
1648
1648
|
: m('i.material-icons.circle', { className }, avatar),
|
|
1649
1649
|
m('span.title', title),
|
|
1650
|
-
m('p', m.trust(content)),
|
|
1651
|
-
m(SecondaryContent, item),
|
|
1652
|
-
])
|
|
1650
|
+
content ? (typeof content === 'string' ? m('p', m.trust(content)) : m('p', content)) : undefined,
|
|
1651
|
+
iconName ? m(SecondaryContent, item) : undefined,
|
|
1652
|
+
].filter(Boolean))
|
|
1653
1653
|
: m('li.collection-item', {
|
|
1654
1654
|
className: active ? 'active' : '',
|
|
1655
|
-
|
|
1655
|
+
onclick: onclick ? () => onclick(item) : undefined,
|
|
1656
|
+
}, content
|
|
1657
|
+
? m('div', [
|
|
1658
|
+
m('div', title),
|
|
1659
|
+
typeof content === 'string' ? m('p.secondary-text', content) : content,
|
|
1660
|
+
iconName ? m(SecondaryContent, item) : undefined,
|
|
1661
|
+
].filter(Boolean))
|
|
1662
|
+
: iconName
|
|
1663
|
+
? m('div', [title, m(SecondaryContent, item)])
|
|
1664
|
+
: title);
|
|
1656
1665
|
},
|
|
1657
1666
|
};
|
|
1658
1667
|
};
|
|
@@ -10753,12 +10762,12 @@ const Rating = () => {
|
|
|
10753
10762
|
const RatingItem = () => {
|
|
10754
10763
|
return {
|
|
10755
10764
|
view: ({ attrs }) => {
|
|
10756
|
-
const { index, displayValue, step, icons, allowHalfSteps, disabled, onclick, onmouseover } = attrs;
|
|
10765
|
+
const { index, displayValue, step, icons, allowHalfSteps, disabled, showTooltip, tooltipLabel, onclick, onmouseover, } = attrs;
|
|
10757
10766
|
const itemValue = (index + 1) * step;
|
|
10758
10767
|
// Calculate fill state based on displayValue vs itemValue
|
|
10759
10768
|
const diff = displayValue - itemValue;
|
|
10760
10769
|
const fillState = diff >= 0 ? 'full' : allowHalfSteps && diff >= -step / 2 ? 'half' : 'empty';
|
|
10761
|
-
return m('.rating__item', {
|
|
10770
|
+
return m('.rating__item.no-select', {
|
|
10762
10771
|
className: [
|
|
10763
10772
|
fillState === 'full' ? 'rating__item--filled' : '',
|
|
10764
10773
|
fillState === 'half' ? 'rating__item--half' : '',
|
|
@@ -10779,6 +10788,8 @@ const Rating = () => {
|
|
|
10779
10788
|
clipPath: fillState === 'half' ? 'inset(0 50% 0 0)' : undefined,
|
|
10780
10789
|
},
|
|
10781
10790
|
}, typeof icons.filled === 'string' ? icons.filled : m(icons.filled)),
|
|
10791
|
+
// Tooltip
|
|
10792
|
+
showTooltip && tooltipLabel && m('.rating__tooltip', tooltipLabel),
|
|
10782
10793
|
]);
|
|
10783
10794
|
},
|
|
10784
10795
|
};
|
|
@@ -10850,6 +10861,7 @@ const Rating = () => {
|
|
|
10850
10861
|
},
|
|
10851
10862
|
// Array.from({ length: itemCount }, (_, i) => renderRatingItem(attrs, i))
|
|
10852
10863
|
[...Array(itemCount)].map((_, i) => {
|
|
10864
|
+
var _a;
|
|
10853
10865
|
const itemValue = (i + 1) * step;
|
|
10854
10866
|
return m(RatingItem, {
|
|
10855
10867
|
key: `rating-item-${i}`,
|
|
@@ -10859,6 +10871,8 @@ const Rating = () => {
|
|
|
10859
10871
|
icons: Object.assign(Object.assign({}, DEFAULT_ICONS), attrs.icon),
|
|
10860
10872
|
allowHalfSteps: attrs.allowHalfSteps,
|
|
10861
10873
|
disabled: attrs.disabled,
|
|
10874
|
+
showTooltip: attrs.showTooltips,
|
|
10875
|
+
tooltipLabel: (_a = attrs.tooltipLabels) === null || _a === void 0 ? void 0 : _a[i],
|
|
10862
10876
|
onclick: () => handleItemClick(attrs, itemValue),
|
|
10863
10877
|
onmouseover: () => handleItemHover(attrs, itemValue),
|
|
10864
10878
|
});
|
|
@@ -10874,6 +10888,237 @@ const Rating = () => {
|
|
|
10874
10888
|
};
|
|
10875
10889
|
};
|
|
10876
10890
|
|
|
10891
|
+
/** Create a LikertScale component */
|
|
10892
|
+
const LikertScale = () => {
|
|
10893
|
+
const state = {
|
|
10894
|
+
id: uniqueId(),
|
|
10895
|
+
groupId: uniqueId(),
|
|
10896
|
+
internalValue: undefined,
|
|
10897
|
+
isFocused: false,
|
|
10898
|
+
};
|
|
10899
|
+
const isControlled = (attrs) => typeof attrs.value !== 'undefined' && typeof attrs.onchange === 'function';
|
|
10900
|
+
const getCurrentValue = (attrs) => {
|
|
10901
|
+
var _a, _b;
|
|
10902
|
+
const controlled = isControlled(attrs);
|
|
10903
|
+
const isNonInteractive = attrs.readonly || attrs.disabled;
|
|
10904
|
+
if (controlled) {
|
|
10905
|
+
return attrs.value;
|
|
10906
|
+
}
|
|
10907
|
+
// Non-interactive components: prefer defaultValue, fallback to value
|
|
10908
|
+
if (isNonInteractive) {
|
|
10909
|
+
return (_a = attrs.defaultValue) !== null && _a !== void 0 ? _a : attrs.value;
|
|
10910
|
+
}
|
|
10911
|
+
// Interactive uncontrolled: use internal state (user can change it)
|
|
10912
|
+
return (_b = state.internalValue) !== null && _b !== void 0 ? _b : attrs.defaultValue;
|
|
10913
|
+
};
|
|
10914
|
+
const getLabelText = (value, min, max, getLabelFn) => {
|
|
10915
|
+
if (getLabelFn && value !== undefined) {
|
|
10916
|
+
return getLabelFn(value, min, max);
|
|
10917
|
+
}
|
|
10918
|
+
if (value === undefined) {
|
|
10919
|
+
return `No selection, please choose a value between ${min} and ${max}`;
|
|
10920
|
+
}
|
|
10921
|
+
return `Selected ${value} out of ${min} to ${max}`;
|
|
10922
|
+
};
|
|
10923
|
+
const getSizeClass = (size = 'medium') => {
|
|
10924
|
+
switch (size) {
|
|
10925
|
+
case 'small':
|
|
10926
|
+
return 'likert-scale--small';
|
|
10927
|
+
case 'large':
|
|
10928
|
+
return 'likert-scale--large';
|
|
10929
|
+
default:
|
|
10930
|
+
return 'likert-scale--medium';
|
|
10931
|
+
}
|
|
10932
|
+
};
|
|
10933
|
+
const getDensityClass = (density = 'standard') => {
|
|
10934
|
+
switch (density) {
|
|
10935
|
+
case 'compact':
|
|
10936
|
+
return 'likert-scale--compact';
|
|
10937
|
+
case 'comfortable':
|
|
10938
|
+
return 'likert-scale--comfortable';
|
|
10939
|
+
default:
|
|
10940
|
+
return 'likert-scale--standard';
|
|
10941
|
+
}
|
|
10942
|
+
};
|
|
10943
|
+
const getLayoutClass = (layout = 'responsive') => {
|
|
10944
|
+
switch (layout) {
|
|
10945
|
+
case 'horizontal':
|
|
10946
|
+
return 'likert-scale--horizontal';
|
|
10947
|
+
case 'vertical':
|
|
10948
|
+
return 'likert-scale--vertical';
|
|
10949
|
+
default:
|
|
10950
|
+
return 'likert-scale--responsive';
|
|
10951
|
+
}
|
|
10952
|
+
};
|
|
10953
|
+
const handleChange = (attrs, newValue) => {
|
|
10954
|
+
var _a;
|
|
10955
|
+
if (attrs.readonly || attrs.disabled)
|
|
10956
|
+
return;
|
|
10957
|
+
if (!isControlled(attrs)) {
|
|
10958
|
+
state.internalValue = newValue;
|
|
10959
|
+
}
|
|
10960
|
+
(_a = attrs.onchange) === null || _a === void 0 ? void 0 : _a.call(attrs, newValue);
|
|
10961
|
+
};
|
|
10962
|
+
const handleKeyDown = (attrs, e) => {
|
|
10963
|
+
if (attrs.readonly || attrs.disabled)
|
|
10964
|
+
return;
|
|
10965
|
+
const min = attrs.min || 1;
|
|
10966
|
+
const max = attrs.max || 5;
|
|
10967
|
+
const step = attrs.step || 1;
|
|
10968
|
+
const currentValue = getCurrentValue(attrs);
|
|
10969
|
+
let newValue = currentValue;
|
|
10970
|
+
switch (e.key) {
|
|
10971
|
+
case 'ArrowRight':
|
|
10972
|
+
case 'ArrowUp':
|
|
10973
|
+
e.preventDefault();
|
|
10974
|
+
newValue = currentValue !== undefined ? Math.min(max, currentValue + step) : min;
|
|
10975
|
+
break;
|
|
10976
|
+
case 'ArrowLeft':
|
|
10977
|
+
case 'ArrowDown':
|
|
10978
|
+
e.preventDefault();
|
|
10979
|
+
newValue = currentValue !== undefined ? Math.max(min, currentValue - step) : min;
|
|
10980
|
+
break;
|
|
10981
|
+
case 'Home':
|
|
10982
|
+
e.preventDefault();
|
|
10983
|
+
newValue = min;
|
|
10984
|
+
break;
|
|
10985
|
+
case 'End':
|
|
10986
|
+
e.preventDefault();
|
|
10987
|
+
newValue = max;
|
|
10988
|
+
break;
|
|
10989
|
+
default:
|
|
10990
|
+
return;
|
|
10991
|
+
}
|
|
10992
|
+
if (newValue !== currentValue) {
|
|
10993
|
+
handleChange(attrs, newValue);
|
|
10994
|
+
}
|
|
10995
|
+
};
|
|
10996
|
+
const LikertScaleItem = () => {
|
|
10997
|
+
return {
|
|
10998
|
+
view: ({ attrs }) => {
|
|
10999
|
+
const { value, currentValue, showNumber, showTooltip, tooltipLabel, groupId, name, disabled, readonly, onchange, } = attrs;
|
|
11000
|
+
const radioId = `${groupId}-${value}`;
|
|
11001
|
+
const isChecked = currentValue === value;
|
|
11002
|
+
return m('.likert-scale__item.no-select', {
|
|
11003
|
+
className: [
|
|
11004
|
+
isChecked ? 'likert-scale__item--checked' : '',
|
|
11005
|
+
disabled ? 'likert-scale__item--disabled' : '',
|
|
11006
|
+
readonly ? 'likert-scale__item--readonly' : '',
|
|
11007
|
+
]
|
|
11008
|
+
.filter(Boolean)
|
|
11009
|
+
.join(' '),
|
|
11010
|
+
}, [
|
|
11011
|
+
// Number label (optional)
|
|
11012
|
+
showNumber && m('.likert-scale__number', value),
|
|
11013
|
+
// Radio button input
|
|
11014
|
+
m('input[type=radio].likert-scale__input', {
|
|
11015
|
+
id: radioId,
|
|
11016
|
+
name: name || groupId,
|
|
11017
|
+
value: value,
|
|
11018
|
+
checked: isChecked,
|
|
11019
|
+
disabled: disabled || readonly,
|
|
11020
|
+
onchange: () => onchange(value),
|
|
11021
|
+
}),
|
|
11022
|
+
// Label for radio button
|
|
11023
|
+
m('label.likert-scale__label', {
|
|
11024
|
+
for: radioId,
|
|
11025
|
+
}),
|
|
11026
|
+
// Tooltip (optional)
|
|
11027
|
+
showTooltip && tooltipLabel && m('.likert-scale__tooltip', tooltipLabel),
|
|
11028
|
+
]);
|
|
11029
|
+
},
|
|
11030
|
+
};
|
|
11031
|
+
};
|
|
11032
|
+
return {
|
|
11033
|
+
oninit: ({ attrs }) => {
|
|
11034
|
+
const controlled = isControlled(attrs);
|
|
11035
|
+
const isNonInteractive = attrs.readonly || attrs.disabled;
|
|
11036
|
+
// Warn developer for improper controlled usage
|
|
11037
|
+
if (attrs.value !== undefined && !controlled && !isNonInteractive) {
|
|
11038
|
+
console.warn(`LikertScale component received 'value' prop without 'onchange' handler. ` +
|
|
11039
|
+
`Use 'defaultValue' for uncontrolled components or add 'onchange' for controlled components.`);
|
|
11040
|
+
}
|
|
11041
|
+
if (!controlled) {
|
|
11042
|
+
state.internalValue = attrs.defaultValue;
|
|
11043
|
+
}
|
|
11044
|
+
},
|
|
11045
|
+
view: ({ attrs }) => {
|
|
11046
|
+
const { min = 1, max = 5, step = 1, size = 'medium', density = 'standard', layout = 'responsive', className = '', style = {}, readonly = false, disabled = false, id = state.id, name, label, description, isMandatory, startLabel, middleLabel, endLabel, showNumbers = false, showTooltips = false, tooltipLabels, alignLabels = false } = attrs, ariaAttrs = __rest(attrs, ["min", "max", "step", "size", "density", "layout", "className", "style", "readonly", "disabled", "id", "name", "label", "description", "isMandatory", "startLabel", "middleLabel", "endLabel", "showNumbers", "showTooltips", "tooltipLabels", "alignLabels"]);
|
|
11047
|
+
const currentValue = getCurrentValue(attrs);
|
|
11048
|
+
const itemCount = Math.floor((max - min) / step) + 1;
|
|
11049
|
+
// Generate scale values
|
|
11050
|
+
const scaleValues = Array.from({ length: itemCount }, (_, i) => min + i * step);
|
|
11051
|
+
return m('.likert-scale', {
|
|
11052
|
+
className: [
|
|
11053
|
+
'likert-scale',
|
|
11054
|
+
getSizeClass(size),
|
|
11055
|
+
getDensityClass(density),
|
|
11056
|
+
getLayoutClass(layout),
|
|
11057
|
+
readonly ? 'likert-scale--readonly' : '',
|
|
11058
|
+
disabled ? 'likert-scale--disabled' : '',
|
|
11059
|
+
state.isFocused ? 'likert-scale--focused' : '',
|
|
11060
|
+
alignLabels ? 'likert-scale--aligned' : '',
|
|
11061
|
+
className,
|
|
11062
|
+
]
|
|
11063
|
+
.filter(Boolean)
|
|
11064
|
+
.join(' '),
|
|
11065
|
+
style,
|
|
11066
|
+
id,
|
|
11067
|
+
role: 'radiogroup',
|
|
11068
|
+
'aria-label': ariaAttrs['aria-label'] || attrs.ariaLabel || label || `Rating scale from ${min} to ${max}`,
|
|
11069
|
+
'aria-labelledby': ariaAttrs['aria-labelledby'],
|
|
11070
|
+
'aria-readonly': readonly,
|
|
11071
|
+
'aria-disabled': disabled,
|
|
11072
|
+
onkeydown: (e) => handleKeyDown(attrs, e),
|
|
11073
|
+
onfocus: () => {
|
|
11074
|
+
state.isFocused = true;
|
|
11075
|
+
},
|
|
11076
|
+
onblur: () => {
|
|
11077
|
+
state.isFocused = false;
|
|
11078
|
+
},
|
|
11079
|
+
tabindex: readonly || disabled ? -1 : 0,
|
|
11080
|
+
}, [
|
|
11081
|
+
// Label section (only text label, not the description)
|
|
11082
|
+
label &&
|
|
11083
|
+
m('.likert-scale__question-label', [
|
|
11084
|
+
m('span', label + (isMandatory ? ' *' : '')),
|
|
11085
|
+
description && m('.likert-scale__description', m.trust(description)),
|
|
11086
|
+
]),
|
|
11087
|
+
// Scale section container
|
|
11088
|
+
m('.likert-scale__scale-container', [
|
|
11089
|
+
// Scale items with numbers
|
|
11090
|
+
m('.likert-scale__scale', scaleValues.map((value) => m(LikertScaleItem, {
|
|
11091
|
+
key: `likert-item-${value}`,
|
|
11092
|
+
value,
|
|
11093
|
+
currentValue,
|
|
11094
|
+
showNumber: showNumbers,
|
|
11095
|
+
showTooltip: showTooltips,
|
|
11096
|
+
tooltipLabel: tooltipLabels === null || tooltipLabels === void 0 ? void 0 : tooltipLabels[value - min],
|
|
11097
|
+
groupId: state.groupId,
|
|
11098
|
+
name,
|
|
11099
|
+
disabled,
|
|
11100
|
+
readonly,
|
|
11101
|
+
onchange: (v) => handleChange(attrs, v),
|
|
11102
|
+
}))),
|
|
11103
|
+
// Scale anchors
|
|
11104
|
+
(startLabel || middleLabel || endLabel) &&
|
|
11105
|
+
m('.likert-scale__anchors', [
|
|
11106
|
+
startLabel && m('.likert-scale__anchor.likert-scale__anchor--start', startLabel),
|
|
11107
|
+
middleLabel && m('.likert-scale__anchor.likert-scale__anchor--middle', middleLabel),
|
|
11108
|
+
endLabel && m('.likert-scale__anchor.likert-scale__anchor--end', endLabel),
|
|
11109
|
+
]),
|
|
11110
|
+
]),
|
|
11111
|
+
// Screen reader text
|
|
11112
|
+
m('.likert-scale__sr-only', {
|
|
11113
|
+
className: 'likert-scale__sr-only',
|
|
11114
|
+
'aria-live': 'polite',
|
|
11115
|
+
'aria-atomic': 'true',
|
|
11116
|
+
}, getLabelText(currentValue, min, max, attrs.getLabelText)),
|
|
11117
|
+
]);
|
|
11118
|
+
},
|
|
11119
|
+
};
|
|
11120
|
+
};
|
|
11121
|
+
|
|
10877
11122
|
/**
|
|
10878
11123
|
* ToggleButton component.
|
|
10879
11124
|
*
|
|
@@ -11225,6 +11470,7 @@ exports.ImageList = ImageList;
|
|
|
11225
11470
|
exports.InputCheckbox = InputCheckbox;
|
|
11226
11471
|
exports.Label = Label;
|
|
11227
11472
|
exports.LargeButton = LargeButton;
|
|
11473
|
+
exports.LikertScale = LikertScale;
|
|
11228
11474
|
exports.LinearProgress = LinearProgress;
|
|
11229
11475
|
exports.ListItem = ListItem;
|
|
11230
11476
|
exports.Mandatory = Mandatory;
|