mithril-materialized 3.12.0 → 3.14.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/index.js CHANGED
@@ -620,6 +620,85 @@ const Icon = () => ({
620
620
  },
621
621
  });
622
622
 
623
+ const iconPaths = {
624
+ caret: [
625
+ 'M7 10l5 5 5-5z', // arrow
626
+ 'M0 0h24v24H0z', // background
627
+ ],
628
+ close: [
629
+ 'M18.3 5.71a1 1 0 0 0-1.41 0L12 10.59 7.11 5.7A1 1 0 0 0 5.7 7.11L10.59 12l-4.89 4.89a1 1 0 1 0 1.41 1.41L12 13.41l4.89 4.89a1 1 0 0 0 1.41-1.41L13.41 12l4.89-4.89a1 1 0 0 0 0-1.4z',
630
+ 'M0 0h24v24H0z',
631
+ ],
632
+ chevron: [
633
+ 'M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z', // chevron down
634
+ 'M0 0h24v24H0z', // background
635
+ ],
636
+ chevron_left: [
637
+ 'M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z', // chevron left
638
+ 'M0 0h24v24H0z', // background
639
+ ],
640
+ chevron_right: [
641
+ 'M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z', // chevron right
642
+ 'M0 0h24v24H0z', // background
643
+ ],
644
+ menu: [
645
+ 'M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z', // hamburger menu
646
+ 'M0 0h24v24H0z', // background
647
+ ],
648
+ expand: [
649
+ 'M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z', // plus
650
+ 'M0 0h24v24H0z', // background
651
+ ],
652
+ collapse: [
653
+ 'M19 13H5v-2h14v2z', // minus
654
+ 'M0 0h24v24H0z', // background
655
+ ],
656
+ check: [
657
+ 'M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z', // checkmark
658
+ 'M0 0h24v24H0z', // background
659
+ ],
660
+ radio_checked: [
661
+ 'M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z', // radio button checked
662
+ 'M0 0h24v24H0z', // background
663
+ ],
664
+ radio_unchecked: [
665
+ 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z', // radio button unchecked
666
+ 'M0 0h24v24H0z', // background
667
+ ],
668
+ light_mode: [
669
+ 'M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5M2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1m18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1M11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1m0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1M5.99 4.58a.996.996 0 0 0-1.41 0 .996.996 0 0 0 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41zm12.4 12.4a.996.996 0 0 0-1.41 0 .996.996 0 0 0 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0a.996.996 0 0 0 0-1.41zm1.06-11a.996.996 0 0 0 0-1.41.996.996 0 0 0-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0zM7.05 18.4a.996.996 0 0 0 0-1.41.996.996 0 0 0-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0z',
670
+ 'M0 0h24v24H0z', // background
671
+ ],
672
+ dark_mode: [
673
+ 'M12 3a9 9 0 1 0 9 9c0-.46-.04-.92-.1-1.36a5.39 5.39 0 0 1-4.4 2.26 5.4 5.4 0 0 1-3.14-9.8c-.44-.06-.9-.1-1.36-.1z',
674
+ 'M0 0h24v24H0z', // background
675
+ ],
676
+ delete: [
677
+ 'M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z',
678
+ 'M0 0h24v24H0z', // background
679
+ ],
680
+ };
681
+ const MaterialIcon = () => {
682
+ return {
683
+ view: ({ attrs }) => {
684
+ var _a;
685
+ const { name, direction = 'down', style } = attrs, props = __rest(attrs, ["name", "direction", "style"]);
686
+ const rotationMap = {
687
+ down: 0,
688
+ up: 180,
689
+ left: 90,
690
+ right: -90,
691
+ };
692
+ const rotation = (_a = rotationMap[direction]) !== null && _a !== void 0 ? _a : 0;
693
+ const transform = rotation ? `rotate(${rotation}deg)` : undefined;
694
+ return m('svg', Object.assign(Object.assign({}, props), { style: Object.assign({ transform }, style), height: '24px', width: '24px', viewBox: '0 0 24 24', xmlns: 'http://www.w3.org/2000/svg' }), iconPaths[name].map((d) => m('path', {
695
+ d,
696
+ fill: d.includes('M0 0h24v24H0z') ? 'none' : 'currentColor',
697
+ })));
698
+ },
699
+ };
700
+ };
701
+
623
702
  /*!
624
703
  * Waves Effect for Mithril Materialized
625
704
  * Based on Waves v0.6.4 by Alfiana E. Sibuea
@@ -725,7 +804,7 @@ WavesEffect.onTouchEnd = (e) => {
725
804
  const ButtonFactory = (element, defaultClassNames, type = '') => {
726
805
  return () => {
727
806
  return {
728
- view: ({ attrs }) => {
807
+ view: ({ attrs, children }) => {
729
808
  const { tooltip, tooltipPosition, tooltipPostion, // Keep for backwards compatibility
730
809
  iconName, iconClass, label, className, variant } = attrs, params = __rest(attrs, ["tooltip", "tooltipPosition", "tooltipPostion", "iconName", "iconClass", "label", "className", "variant"]);
731
810
  // Use variant or fallback to factory type
@@ -741,7 +820,7 @@ const ButtonFactory = (element, defaultClassNames, type = '') => {
741
820
  ontouchstart: WavesEffect.onTouchStart,
742
821
  ontouchend: WavesEffect.onTouchEnd
743
822
  } : {};
744
- return m(element, Object.assign(Object.assign(Object.assign({}, params), wavesHandlers), { className: cn, 'data-position': tooltip ? position : undefined, 'data-tooltip': tooltip || undefined, type: buttonType }), iconName ? m(Icon, { iconName, className: iconClass || 'left' }) : undefined, label ? label : undefined);
823
+ return m(element, Object.assign(Object.assign(Object.assign({}, params), wavesHandlers), { className: cn, 'data-position': tooltip ? position : undefined, 'data-tooltip': tooltip || undefined, type: buttonType }), iconName ? m(Icon, { iconName, className: iconClass || 'left' }) : undefined, label ? label : undefined, children);
745
824
  },
746
825
  };
747
826
  };
@@ -753,6 +832,54 @@ const FlatButton = ButtonFactory('a', 'waves-effect waves-teal btn-flat', 'butto
753
832
  const IconButton = ButtonFactory('button', 'btn-flat btn-icon waves-effect waves-teal', 'button');
754
833
  const RoundIconButton = ButtonFactory('button', 'btn-floating btn-large waves-effect waves-light', 'button');
755
834
  const SubmitButton = ButtonFactory('button', 'btn waves-effect waves-light', 'submit');
835
+ const RaisedIconButton = ButtonFactory('button', 'btn waves-effect waves-light', 'button');
836
+ const ConfirmButton = () => {
837
+ let isConfirming = false;
838
+ let isBlocked = false;
839
+ let timeoutId;
840
+ let blockTimeoutId;
841
+ const reset = () => {
842
+ isConfirming = false;
843
+ isBlocked = false;
844
+ m.redraw();
845
+ };
846
+ const unblock = () => {
847
+ isBlocked = false;
848
+ };
849
+ return {
850
+ onremove: () => {
851
+ window.clearTimeout(timeoutId);
852
+ window.clearTimeout(blockTimeoutId);
853
+ },
854
+ view: ({ attrs }) => {
855
+ const { iconName = 'delete', confirmIconName = 'check', confirmColor = 'red', timeout = 3000, clickDelay = 500, onFirstClick, onclick } = attrs, props = __rest(attrs, ["iconName", "confirmIconName", "confirmColor", "timeout", "clickDelay", "onFirstClick", "onclick"]);
856
+ const handleClick = (e) => {
857
+ e.preventDefault();
858
+ if (isBlocked)
859
+ return;
860
+ if (isConfirming) {
861
+ window.clearTimeout(timeoutId);
862
+ window.clearTimeout(blockTimeoutId); // Clean up safety
863
+ isConfirming = false;
864
+ onclick === null || onclick === void 0 ? void 0 : onclick(e);
865
+ }
866
+ else {
867
+ isConfirming = true;
868
+ isBlocked = true;
869
+ onFirstClick === null || onFirstClick === void 0 ? void 0 : onFirstClick();
870
+ timeoutId = window.setTimeout(reset, timeout);
871
+ blockTimeoutId = window.setTimeout(unblock, clickDelay);
872
+ }
873
+ };
874
+ const cn = isConfirming ? confirmColor : 'red-text';
875
+ const commonProps = Object.assign(Object.assign({}, props), { className: `${props.className || ''} ${cn}`, style: Object.assign(Object.assign(Object.assign({}, props.style), { display: 'flex', alignItems: 'center', justifyContent: 'center' }), (isConfirming ? { padding: '0 8px', width: 'auto', minWidth: 'auto' } : {})), onclick: handleClick });
876
+ if (isConfirming) {
877
+ return m(RaisedIconButton, commonProps, m(MaterialIcon, { name: confirmIconName }));
878
+ }
879
+ return m(IconButton, commonProps, m(MaterialIcon, { name: iconName }));
880
+ },
881
+ };
882
+ };
756
883
 
757
884
  /**
758
885
  * Materialize CSS Carousel component with dynamic positioning
@@ -1144,81 +1271,6 @@ const Carousel = () => {
1144
1271
  };
1145
1272
  };
1146
1273
 
1147
- const iconPaths = {
1148
- caret: [
1149
- 'M7 10l5 5 5-5z', // arrow
1150
- 'M0 0h24v24H0z', // background
1151
- ],
1152
- close: [
1153
- 'M18.3 5.71a1 1 0 0 0-1.41 0L12 10.59 7.11 5.7A1 1 0 0 0 5.7 7.11L10.59 12l-4.89 4.89a1 1 0 1 0 1.41 1.41L12 13.41l4.89 4.89a1 1 0 0 0 1.41-1.41L13.41 12l4.89-4.89a1 1 0 0 0 0-1.4z',
1154
- 'M0 0h24v24H0z',
1155
- ],
1156
- chevron: [
1157
- 'M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z', // chevron down
1158
- 'M0 0h24v24H0z', // background
1159
- ],
1160
- chevron_left: [
1161
- 'M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z', // chevron left
1162
- 'M0 0h24v24H0z', // background
1163
- ],
1164
- chevron_right: [
1165
- 'M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z', // chevron right
1166
- 'M0 0h24v24H0z', // background
1167
- ],
1168
- menu: [
1169
- 'M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z', // hamburger menu
1170
- 'M0 0h24v24H0z', // background
1171
- ],
1172
- expand: [
1173
- 'M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z', // plus
1174
- 'M0 0h24v24H0z', // background
1175
- ],
1176
- collapse: [
1177
- 'M19 13H5v-2h14v2z', // minus
1178
- 'M0 0h24v24H0z', // background
1179
- ],
1180
- check: [
1181
- 'M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z', // checkmark
1182
- 'M0 0h24v24H0z', // background
1183
- ],
1184
- radio_checked: [
1185
- 'M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z', // radio button checked
1186
- 'M0 0h24v24H0z', // background
1187
- ],
1188
- radio_unchecked: [
1189
- 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z', // radio button unchecked
1190
- 'M0 0h24v24H0z', // background
1191
- ],
1192
- light_mode: [
1193
- 'M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5M2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1m18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1M11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1m0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1M5.99 4.58a.996.996 0 0 0-1.41 0 .996.996 0 0 0 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41zm12.4 12.4a.996.996 0 0 0-1.41 0 .996.996 0 0 0 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0a.996.996 0 0 0 0-1.41zm1.06-11a.996.996 0 0 0 0-1.41.996.996 0 0 0-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0zM7.05 18.4a.996.996 0 0 0 0-1.41.996.996 0 0 0-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0z',
1194
- 'M0 0h24v24H0z', // background
1195
- ],
1196
- dark_mode: [
1197
- 'M12 3a9 9 0 1 0 9 9c0-.46-.04-.92-.1-1.36a5.39 5.39 0 0 1-4.4 2.26 5.4 5.4 0 0 1-3.14-9.8c-.44-.06-.9-.1-1.36-.1z',
1198
- 'M0 0h24v24H0z', // background
1199
- ],
1200
- };
1201
- const MaterialIcon = () => {
1202
- return {
1203
- view: ({ attrs }) => {
1204
- var _a;
1205
- const { name, direction = 'down', style } = attrs, props = __rest(attrs, ["name", "direction", "style"]);
1206
- const rotationMap = {
1207
- down: 0,
1208
- up: 180,
1209
- left: 90,
1210
- right: -90,
1211
- };
1212
- const rotation = (_a = rotationMap[direction]) !== null && _a !== void 0 ? _a : 0;
1213
- const transform = rotation ? `rotate(${rotation}deg)` : undefined;
1214
- return m('svg', Object.assign(Object.assign({}, props), { style: Object.assign({ transform }, style), height: '24px', width: '24px', viewBox: '0 0 24 24', xmlns: 'http://www.w3.org/2000/svg' }), iconPaths[name].map((d) => m('path', {
1215
- d,
1216
- fill: d.includes('M0 0h24v24H0z') ? 'none' : 'currentColor',
1217
- })));
1218
- },
1219
- };
1220
- };
1221
-
1222
1274
  const Chips = () => {
1223
1275
  const state = {
1224
1276
  chipsData: [],
@@ -1637,7 +1689,7 @@ const avatarIsImage = (avatar = '') => /\./.test(avatar);
1637
1689
  const ListItem = () => {
1638
1690
  return {
1639
1691
  view: ({ attrs: { item, mode } }) => {
1640
- const { title, content = '', active, iconName, avatar, className, onclick } = item;
1692
+ const { title, content, active, iconName, avatar, className, onclick } = item;
1641
1693
  return mode === exports.CollectionMode.AVATAR
1642
1694
  ? m('li.collection-item.avatar', {
1643
1695
  className: active ? 'active' : '',
@@ -1647,12 +1699,21 @@ const ListItem = () => {
1647
1699
  ? m('img.circle', { src: avatar })
1648
1700
  : m('i.material-icons.circle', { className }, avatar),
1649
1701
  m('span.title', title),
1650
- m('p', m.trust(content)),
1651
- m(SecondaryContent, item),
1652
- ])
1702
+ content ? (typeof content === 'string' ? m('p', m.trust(content)) : m('p', content)) : undefined,
1703
+ iconName ? m(SecondaryContent, item) : undefined,
1704
+ ].filter(Boolean))
1653
1705
  : m('li.collection-item', {
1654
1706
  className: active ? 'active' : '',
1655
- }, iconName ? m('div', [title, m(SecondaryContent, item)]) : title);
1707
+ onclick: onclick ? () => onclick(item) : undefined,
1708
+ }, content
1709
+ ? m('div', [
1710
+ m('div', title),
1711
+ typeof content === 'string' ? m('p.secondary-text', content) : content,
1712
+ iconName ? m(SecondaryContent, item) : undefined,
1713
+ ].filter(Boolean))
1714
+ : iconName
1715
+ ? m('div', [title, m(SecondaryContent, item)])
1716
+ : title);
1656
1717
  },
1657
1718
  };
1658
1719
  };
@@ -10753,12 +10814,12 @@ const Rating = () => {
10753
10814
  const RatingItem = () => {
10754
10815
  return {
10755
10816
  view: ({ attrs }) => {
10756
- const { index, displayValue, step, icons, allowHalfSteps, disabled, onclick, onmouseover } = attrs;
10817
+ const { index, displayValue, step, icons, allowHalfSteps, disabled, showTooltip, tooltipLabel, onclick, onmouseover, } = attrs;
10757
10818
  const itemValue = (index + 1) * step;
10758
10819
  // Calculate fill state based on displayValue vs itemValue
10759
10820
  const diff = displayValue - itemValue;
10760
10821
  const fillState = diff >= 0 ? 'full' : allowHalfSteps && diff >= -step / 2 ? 'half' : 'empty';
10761
- return m('.rating__item', {
10822
+ return m('.rating__item.no-select', {
10762
10823
  className: [
10763
10824
  fillState === 'full' ? 'rating__item--filled' : '',
10764
10825
  fillState === 'half' ? 'rating__item--half' : '',
@@ -10779,6 +10840,8 @@ const Rating = () => {
10779
10840
  clipPath: fillState === 'half' ? 'inset(0 50% 0 0)' : undefined,
10780
10841
  },
10781
10842
  }, typeof icons.filled === 'string' ? icons.filled : m(icons.filled)),
10843
+ // Tooltip
10844
+ showTooltip && tooltipLabel && m('.rating__tooltip', tooltipLabel),
10782
10845
  ]);
10783
10846
  },
10784
10847
  };
@@ -10850,6 +10913,7 @@ const Rating = () => {
10850
10913
  },
10851
10914
  // Array.from({ length: itemCount }, (_, i) => renderRatingItem(attrs, i))
10852
10915
  [...Array(itemCount)].map((_, i) => {
10916
+ var _a;
10853
10917
  const itemValue = (i + 1) * step;
10854
10918
  return m(RatingItem, {
10855
10919
  key: `rating-item-${i}`,
@@ -10859,6 +10923,8 @@ const Rating = () => {
10859
10923
  icons: Object.assign(Object.assign({}, DEFAULT_ICONS), attrs.icon),
10860
10924
  allowHalfSteps: attrs.allowHalfSteps,
10861
10925
  disabled: attrs.disabled,
10926
+ showTooltip: attrs.showTooltips,
10927
+ tooltipLabel: (_a = attrs.tooltipLabels) === null || _a === void 0 ? void 0 : _a[i],
10862
10928
  onclick: () => handleItemClick(attrs, itemValue),
10863
10929
  onmouseover: () => handleItemHover(attrs, itemValue),
10864
10930
  });
@@ -10874,6 +10940,237 @@ const Rating = () => {
10874
10940
  };
10875
10941
  };
10876
10942
 
10943
+ /** Create a LikertScale component */
10944
+ const LikertScale = () => {
10945
+ const state = {
10946
+ id: uniqueId(),
10947
+ groupId: uniqueId(),
10948
+ internalValue: undefined,
10949
+ isFocused: false,
10950
+ };
10951
+ const isControlled = (attrs) => typeof attrs.value !== 'undefined' && typeof attrs.onchange === 'function';
10952
+ const getCurrentValue = (attrs) => {
10953
+ var _a, _b;
10954
+ const controlled = isControlled(attrs);
10955
+ const isNonInteractive = attrs.readonly || attrs.disabled;
10956
+ if (controlled) {
10957
+ return attrs.value;
10958
+ }
10959
+ // Non-interactive components: prefer defaultValue, fallback to value
10960
+ if (isNonInteractive) {
10961
+ return (_a = attrs.defaultValue) !== null && _a !== void 0 ? _a : attrs.value;
10962
+ }
10963
+ // Interactive uncontrolled: use internal state (user can change it)
10964
+ return (_b = state.internalValue) !== null && _b !== void 0 ? _b : attrs.defaultValue;
10965
+ };
10966
+ const getLabelText = (value, min, max, getLabelFn) => {
10967
+ if (getLabelFn && value !== undefined) {
10968
+ return getLabelFn(value, min, max);
10969
+ }
10970
+ if (value === undefined) {
10971
+ return `No selection, please choose a value between ${min} and ${max}`;
10972
+ }
10973
+ return `Selected ${value} out of ${min} to ${max}`;
10974
+ };
10975
+ const getSizeClass = (size = 'medium') => {
10976
+ switch (size) {
10977
+ case 'small':
10978
+ return 'likert-scale--small';
10979
+ case 'large':
10980
+ return 'likert-scale--large';
10981
+ default:
10982
+ return 'likert-scale--medium';
10983
+ }
10984
+ };
10985
+ const getDensityClass = (density = 'standard') => {
10986
+ switch (density) {
10987
+ case 'compact':
10988
+ return 'likert-scale--compact';
10989
+ case 'comfortable':
10990
+ return 'likert-scale--comfortable';
10991
+ default:
10992
+ return 'likert-scale--standard';
10993
+ }
10994
+ };
10995
+ const getLayoutClass = (layout = 'responsive') => {
10996
+ switch (layout) {
10997
+ case 'horizontal':
10998
+ return 'likert-scale--horizontal';
10999
+ case 'vertical':
11000
+ return 'likert-scale--vertical';
11001
+ default:
11002
+ return 'likert-scale--responsive';
11003
+ }
11004
+ };
11005
+ const handleChange = (attrs, newValue) => {
11006
+ var _a;
11007
+ if (attrs.readonly || attrs.disabled)
11008
+ return;
11009
+ if (!isControlled(attrs)) {
11010
+ state.internalValue = newValue;
11011
+ }
11012
+ (_a = attrs.onchange) === null || _a === void 0 ? void 0 : _a.call(attrs, newValue);
11013
+ };
11014
+ const handleKeyDown = (attrs, e) => {
11015
+ if (attrs.readonly || attrs.disabled)
11016
+ return;
11017
+ const min = attrs.min || 1;
11018
+ const max = attrs.max || 5;
11019
+ const step = attrs.step || 1;
11020
+ const currentValue = getCurrentValue(attrs);
11021
+ let newValue = currentValue;
11022
+ switch (e.key) {
11023
+ case 'ArrowRight':
11024
+ case 'ArrowUp':
11025
+ e.preventDefault();
11026
+ newValue = currentValue !== undefined ? Math.min(max, currentValue + step) : min;
11027
+ break;
11028
+ case 'ArrowLeft':
11029
+ case 'ArrowDown':
11030
+ e.preventDefault();
11031
+ newValue = currentValue !== undefined ? Math.max(min, currentValue - step) : min;
11032
+ break;
11033
+ case 'Home':
11034
+ e.preventDefault();
11035
+ newValue = min;
11036
+ break;
11037
+ case 'End':
11038
+ e.preventDefault();
11039
+ newValue = max;
11040
+ break;
11041
+ default:
11042
+ return;
11043
+ }
11044
+ if (newValue !== currentValue) {
11045
+ handleChange(attrs, newValue);
11046
+ }
11047
+ };
11048
+ const LikertScaleItem = () => {
11049
+ return {
11050
+ view: ({ attrs }) => {
11051
+ const { value, currentValue, showNumber, showTooltip, tooltipLabel, groupId, name, disabled, readonly, onchange, } = attrs;
11052
+ const radioId = `${groupId}-${value}`;
11053
+ const isChecked = currentValue === value;
11054
+ return m('.likert-scale__item.no-select', {
11055
+ className: [
11056
+ isChecked ? 'likert-scale__item--checked' : '',
11057
+ disabled ? 'likert-scale__item--disabled' : '',
11058
+ readonly ? 'likert-scale__item--readonly' : '',
11059
+ ]
11060
+ .filter(Boolean)
11061
+ .join(' '),
11062
+ }, [
11063
+ // Number label (optional)
11064
+ showNumber && m('.likert-scale__number', value),
11065
+ // Radio button input
11066
+ m('input[type=radio].likert-scale__input', {
11067
+ id: radioId,
11068
+ name: name || groupId,
11069
+ value: value,
11070
+ checked: isChecked,
11071
+ disabled: disabled || readonly,
11072
+ onchange: () => onchange(value),
11073
+ }),
11074
+ // Label for radio button
11075
+ m('label.likert-scale__label', {
11076
+ for: radioId,
11077
+ }),
11078
+ // Tooltip (optional)
11079
+ showTooltip && tooltipLabel && m('.likert-scale__tooltip', tooltipLabel),
11080
+ ]);
11081
+ },
11082
+ };
11083
+ };
11084
+ return {
11085
+ oninit: ({ attrs }) => {
11086
+ const controlled = isControlled(attrs);
11087
+ const isNonInteractive = attrs.readonly || attrs.disabled;
11088
+ // Warn developer for improper controlled usage
11089
+ if (attrs.value !== undefined && !controlled && !isNonInteractive) {
11090
+ console.warn(`LikertScale component received 'value' prop without 'onchange' handler. ` +
11091
+ `Use 'defaultValue' for uncontrolled components or add 'onchange' for controlled components.`);
11092
+ }
11093
+ if (!controlled) {
11094
+ state.internalValue = attrs.defaultValue;
11095
+ }
11096
+ },
11097
+ view: ({ attrs }) => {
11098
+ 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"]);
11099
+ const currentValue = getCurrentValue(attrs);
11100
+ const itemCount = Math.floor((max - min) / step) + 1;
11101
+ // Generate scale values
11102
+ const scaleValues = Array.from({ length: itemCount }, (_, i) => min + i * step);
11103
+ return m('.likert-scale', {
11104
+ className: [
11105
+ 'likert-scale',
11106
+ getSizeClass(size),
11107
+ getDensityClass(density),
11108
+ getLayoutClass(layout),
11109
+ readonly ? 'likert-scale--readonly' : '',
11110
+ disabled ? 'likert-scale--disabled' : '',
11111
+ state.isFocused ? 'likert-scale--focused' : '',
11112
+ alignLabels ? 'likert-scale--aligned' : '',
11113
+ className,
11114
+ ]
11115
+ .filter(Boolean)
11116
+ .join(' '),
11117
+ style,
11118
+ id,
11119
+ role: 'radiogroup',
11120
+ 'aria-label': ariaAttrs['aria-label'] || attrs.ariaLabel || label || `Rating scale from ${min} to ${max}`,
11121
+ 'aria-labelledby': ariaAttrs['aria-labelledby'],
11122
+ 'aria-readonly': readonly,
11123
+ 'aria-disabled': disabled,
11124
+ onkeydown: (e) => handleKeyDown(attrs, e),
11125
+ onfocus: () => {
11126
+ state.isFocused = true;
11127
+ },
11128
+ onblur: () => {
11129
+ state.isFocused = false;
11130
+ },
11131
+ tabindex: readonly || disabled ? -1 : 0,
11132
+ }, [
11133
+ // Label section (only text label, not the description)
11134
+ label &&
11135
+ m('.likert-scale__question-label', [
11136
+ m('span', label + (isMandatory ? ' *' : '')),
11137
+ description && m('.likert-scale__description', m.trust(description)),
11138
+ ]),
11139
+ // Scale section container
11140
+ m('.likert-scale__scale-container', [
11141
+ // Scale items with numbers
11142
+ m('.likert-scale__scale', scaleValues.map((value) => m(LikertScaleItem, {
11143
+ key: `likert-item-${value}`,
11144
+ value,
11145
+ currentValue,
11146
+ showNumber: showNumbers,
11147
+ showTooltip: showTooltips,
11148
+ tooltipLabel: tooltipLabels === null || tooltipLabels === void 0 ? void 0 : tooltipLabels[value - min],
11149
+ groupId: state.groupId,
11150
+ name,
11151
+ disabled,
11152
+ readonly,
11153
+ onchange: (v) => handleChange(attrs, v),
11154
+ }))),
11155
+ // Scale anchors
11156
+ (startLabel || middleLabel || endLabel) &&
11157
+ m('.likert-scale__anchors', [
11158
+ startLabel && m('.likert-scale__anchor.likert-scale__anchor--start', startLabel),
11159
+ middleLabel && m('.likert-scale__anchor.likert-scale__anchor--middle', middleLabel),
11160
+ endLabel && m('.likert-scale__anchor.likert-scale__anchor--end', endLabel),
11161
+ ]),
11162
+ ]),
11163
+ // Screen reader text
11164
+ m('.likert-scale__sr-only', {
11165
+ className: 'likert-scale__sr-only',
11166
+ 'aria-live': 'polite',
11167
+ 'aria-atomic': 'true',
11168
+ }, getLabelText(currentValue, min, max, attrs.getLabelText)),
11169
+ ]);
11170
+ },
11171
+ };
11172
+ };
11173
+
10877
11174
  /**
10878
11175
  * ToggleButton component.
10879
11176
  *
@@ -11208,6 +11505,7 @@ exports.Collapsible = Collapsible;
11208
11505
  exports.CollapsibleItem = CollapsibleItem;
11209
11506
  exports.Collection = Collection;
11210
11507
  exports.ColorInput = ColorInput;
11508
+ exports.ConfirmButton = ConfirmButton;
11211
11509
  exports.DataTable = DataTable;
11212
11510
  exports.DatePicker = DatePicker;
11213
11511
  exports.DigitalClock = DigitalClock;
@@ -11225,6 +11523,7 @@ exports.ImageList = ImageList;
11225
11523
  exports.InputCheckbox = InputCheckbox;
11226
11524
  exports.Label = Label;
11227
11525
  exports.LargeButton = LargeButton;
11526
+ exports.LikertScale = LikertScale;
11228
11527
  exports.LinearProgress = LinearProgress;
11229
11528
  exports.ListItem = ListItem;
11230
11529
  exports.Mandatory = Mandatory;