@vielzeug/refine 26.10.7 → 26.10.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/README.md +6 -0
- package/dist/accordion-item.cjs +2 -2
- package/dist/accordion-item.cjs.map +1 -1
- package/dist/accordion-item.js +6 -6
- package/dist/accordion-item.js.map +1 -1
- package/dist/accordion.cjs +2 -2
- package/dist/accordion.cjs.map +1 -1
- package/dist/accordion.js +2 -2
- package/dist/accordion.js.map +1 -1
- package/dist/alert.cjs +2 -2
- package/dist/alert.cjs.map +1 -1
- package/dist/alert.js +2 -2
- package/dist/alert.js.map +1 -1
- package/dist/avatar.cjs +3 -3
- package/dist/avatar.cjs.map +1 -1
- package/dist/avatar.js +2 -2
- package/dist/avatar.js.map +1 -1
- package/dist/badge.cjs +2 -2
- package/dist/badge.cjs.map +1 -1
- package/dist/badge.js +2 -2
- package/dist/badge.js.map +1 -1
- package/dist/box.cjs +2 -2
- package/dist/box.cjs.map +1 -1
- package/dist/box.js +2 -2
- package/dist/box.js.map +1 -1
- package/dist/button-group.cjs +2 -2
- package/dist/button-group.cjs.map +1 -1
- package/dist/button-group.js +2 -2
- package/dist/button-group.js.map +1 -1
- package/dist/button.cjs +9 -9
- package/dist/button.cjs.map +1 -1
- package/dist/button.js +8 -8
- package/dist/button.js.map +1 -1
- package/dist/card.cjs +2 -2
- package/dist/card.cjs.map +1 -1
- package/dist/card.js +2 -2
- package/dist/card.js.map +1 -1
- package/dist/carousel.cjs +6 -6
- package/dist/carousel.cjs.map +1 -1
- package/dist/carousel.js +6 -6
- package/dist/carousel.js.map +1 -1
- package/dist/chat-message.cjs +4 -4
- package/dist/chat-message.cjs.map +1 -1
- package/dist/chat-message.js +4 -4
- package/dist/chat-message.js.map +1 -1
- package/dist/checkbox-group.cjs +1 -1
- package/dist/checkbox-group.cjs.map +1 -1
- package/dist/checkbox-group.js +1 -1
- package/dist/checkbox-group.js.map +1 -1
- package/dist/checkbox.cjs +1 -1
- package/dist/checkbox.cjs.map +1 -1
- package/dist/checkbox.js +1 -1
- package/dist/checkbox.js.map +1 -1
- package/dist/chip.cjs +8 -8
- package/dist/chip.cjs.map +1 -1
- package/dist/chip.js +8 -8
- package/dist/chip.js.map +1 -1
- package/dist/combobox.cjs +8 -8
- package/dist/combobox.cjs.map +1 -1
- package/dist/combobox.js +11 -11
- package/dist/combobox.js.map +1 -1
- package/dist/content/avatar/avatar.d.ts +2 -1
- package/dist/content/avatar/avatar.d.ts.map +1 -1
- package/dist/content/card/card.d.ts +6 -3
- package/dist/content/card/card.d.ts.map +1 -1
- package/dist/content/carousel/carousel.d.ts +5 -3
- package/dist/content/carousel/carousel.d.ts.map +1 -1
- package/dist/content/chat-message/chat-message.d.ts +2 -1
- package/dist/content/chat-message/chat-message.d.ts.map +1 -1
- package/dist/content/copy-command/copy-command.d.ts +1 -1
- package/dist/content/copy-command/copy-command.d.ts.map +1 -1
- package/dist/content/keyboard-key/keyboard-key.d.ts +2 -1
- package/dist/content/keyboard-key/keyboard-key.d.ts.map +1 -1
- package/dist/content/list/list.d.ts +2 -1
- package/dist/content/list/list.d.ts.map +1 -1
- package/dist/content/marquee/marquee.d.ts +3 -2
- package/dist/content/marquee/marquee.d.ts.map +1 -1
- package/dist/content/pagination/pagination.d.ts +1 -1
- package/dist/content/pagination/pagination.d.ts.map +1 -1
- package/dist/content/qr-code/qr-code.d.ts +0 -41
- package/dist/content/qr-code/qr-code.d.ts.map +1 -1
- package/dist/content/separator/separator.d.ts +1 -1
- package/dist/content/separator/separator.d.ts.map +1 -1
- package/dist/content/stepper/step.d.ts +1 -1
- package/dist/content/stepper/step.d.ts.map +1 -1
- package/dist/content/table/table.d.ts +2 -1
- package/dist/content/table/table.d.ts.map +1 -1
- package/dist/content/text/text.d.ts +13 -6
- package/dist/content/text/text.d.ts.map +1 -1
- package/dist/cookie-banner.cjs +1 -1
- package/dist/cookie-banner.cjs.map +1 -1
- package/dist/cookie-banner.js +1 -1
- package/dist/cookie-banner.js.map +1 -1
- package/dist/copy-command.cjs +6 -6
- package/dist/copy-command.cjs.map +1 -1
- package/dist/copy-command.js +7 -7
- package/dist/copy-command.js.map +1 -1
- package/dist/core/checkable.cjs +1 -1
- package/dist/core/checkable.cjs.map +1 -1
- package/dist/core/checkable.d.ts +6 -7
- package/dist/core/checkable.d.ts.map +1 -1
- package/dist/core/checkable.js +1 -1
- package/dist/core/checkable.js.map +1 -1
- package/dist/core/choice-field.cjs +1 -1
- package/dist/core/choice-field.cjs.map +1 -1
- package/dist/core/choice-field.d.ts +6 -6
- package/dist/core/choice-field.d.ts.map +1 -1
- package/dist/core/choice-field.js +1 -1
- package/dist/core/choice-field.js.map +1 -1
- package/dist/core/text-field.cjs +1 -1
- package/dist/core/text-field.cjs.map +1 -1
- package/dist/core/text-field.d.ts +8 -8
- package/dist/core/text-field.d.ts.map +1 -1
- package/dist/core/text-field.js +1 -1
- package/dist/core/text-field.js.map +1 -1
- package/dist/counter.cjs +2 -2
- package/dist/counter.cjs.map +1 -1
- package/dist/counter.js +2 -2
- package/dist/counter.js.map +1 -1
- package/dist/custom-elements.json +378 -241
- package/dist/datagrid.cjs +47 -47
- package/dist/datagrid.cjs.map +1 -1
- package/dist/datagrid.js +51 -51
- package/dist/datagrid.js.map +1 -1
- package/dist/date-picker.cjs +9 -9
- package/dist/date-picker.cjs.map +1 -1
- package/dist/date-picker.js +11 -11
- package/dist/date-picker.js.map +1 -1
- package/dist/dialog.cjs +3 -3
- package/dist/dialog.cjs.map +1 -1
- package/dist/dialog.js +3 -3
- package/dist/dialog.js.map +1 -1
- package/dist/disclosure/accordion/accordion.d.ts +4 -2
- package/dist/disclosure/accordion/accordion.d.ts.map +1 -1
- package/dist/disclosure/accordion-item/accordion-item.d.ts +1 -1
- package/dist/disclosure/accordion-item/accordion-item.d.ts.map +1 -1
- package/dist/disclosure/tab-item/tab-item.d.ts +1 -1
- package/dist/disclosure/tab-item/tab-item.d.ts.map +1 -1
- package/dist/disclosure/tabs/tabs.d.ts +1 -1
- package/dist/disclosure/tabs/tabs.d.ts.map +1 -1
- package/dist/drawer.cjs +4 -4
- package/dist/drawer.cjs.map +1 -1
- package/dist/drawer.js +2 -2
- package/dist/drawer.js.map +1 -1
- package/dist/feedback/alert/alert.d.ts +2 -1
- package/dist/feedback/alert/alert.d.ts.map +1 -1
- package/dist/feedback/badge/badge.d.ts +7 -4
- package/dist/feedback/badge/badge.d.ts.map +1 -1
- package/dist/feedback/chip/chip.d.ts +7 -5
- package/dist/feedback/chip/chip.d.ts.map +1 -1
- package/dist/grid-item.cjs +2 -2
- package/dist/grid-item.cjs.map +1 -1
- package/dist/grid-item.js +2 -2
- package/dist/grid-item.js.map +1 -1
- package/dist/grid.cjs +2 -2
- package/dist/grid.cjs.map +1 -1
- package/dist/grid.js +2 -2
- package/dist/grid.js.map +1 -1
- package/dist/input.cjs +7 -7
- package/dist/input.cjs.map +1 -1
- package/dist/input.js +8 -8
- package/dist/input.js.map +1 -1
- package/dist/inputs/button/button.d.ts +3 -2
- package/dist/inputs/button/button.d.ts.map +1 -1
- package/dist/inputs/button-group/button-group.d.ts +1 -37
- package/dist/inputs/button-group/button-group.d.ts.map +1 -1
- package/dist/inputs/combobox/combobox.types.d.ts +2 -2
- package/dist/inputs/combobox/combobox.types.d.ts.map +1 -1
- package/dist/inputs/counter/counter.d.ts +3 -2
- package/dist/inputs/counter/counter.d.ts.map +1 -1
- package/dist/inputs/datagrid/datagrid-model.cjs +1 -1
- package/dist/inputs/datagrid/datagrid-model.cjs.map +1 -1
- package/dist/inputs/datagrid/datagrid-model.d.ts +2 -1
- package/dist/inputs/datagrid/datagrid-model.d.ts.map +1 -1
- package/dist/inputs/datagrid/datagrid-model.js +1 -1
- package/dist/inputs/datagrid/datagrid-model.js.map +1 -1
- package/dist/inputs/datagrid/datagrid.d.ts +4 -2
- package/dist/inputs/datagrid/datagrid.d.ts.map +1 -1
- package/dist/inputs/date-picker/date-picker.d.ts +3 -2
- package/dist/inputs/date-picker/date-picker.d.ts.map +1 -1
- package/dist/inputs/input/input.d.ts +5 -3
- package/dist/inputs/input/input.d.ts.map +1 -1
- package/dist/inputs/message-composer/message-composer.d.ts +3 -2
- package/dist/inputs/message-composer/message-composer.d.ts.map +1 -1
- package/dist/inputs/number-input/number-input.d.ts +1 -1
- package/dist/inputs/number-input/number-input.d.ts.map +1 -1
- package/dist/inputs/otp-input/otp-input.d.ts +3 -2
- package/dist/inputs/otp-input/otp-input.d.ts.map +1 -1
- package/dist/inputs/query-bar/query-bar.d.ts.map +1 -1
- package/dist/inputs/select/select.d.ts +2 -2
- package/dist/inputs/select/select.d.ts.map +1 -1
- package/dist/inputs/textarea/textarea.d.ts +4 -3
- package/dist/inputs/textarea/textarea.d.ts.map +1 -1
- package/dist/inputs/time-picker/time-picker.d.ts +2 -2
- package/dist/inputs/time-picker/time-picker.d.ts.map +1 -1
- package/dist/keyboard-key.cjs +3 -3
- package/dist/keyboard-key.cjs.map +1 -1
- package/dist/keyboard-key.js +3 -3
- package/dist/keyboard-key.js.map +1 -1
- package/dist/layout/box/box.d.ts +6 -3
- package/dist/layout/box/box.d.ts.map +1 -1
- package/dist/layout/grid/grid.d.ts +11 -6
- package/dist/layout/grid/grid.d.ts.map +1 -1
- package/dist/layout/grid-item/grid-item.d.ts +8 -4
- package/dist/layout/grid-item/grid-item.d.ts.map +1 -1
- package/dist/layout/navbar/navbar.d.ts +4 -2
- package/dist/layout/navbar/navbar.d.ts.map +1 -1
- package/dist/layout/sidebar/sidebar.d.ts +2 -1
- package/dist/layout/sidebar/sidebar.d.ts.map +1 -1
- package/dist/list-item.cjs +1 -1
- package/dist/list-item.cjs.map +1 -1
- package/dist/list-item.js +1 -1
- package/dist/list-item.js.map +1 -1
- package/dist/list.cjs +2 -2
- package/dist/list.cjs.map +1 -1
- package/dist/list.js +2 -2
- package/dist/list.js.map +1 -1
- package/dist/marquee.cjs +3 -3
- package/dist/marquee.cjs.map +1 -1
- package/dist/marquee.js +3 -3
- package/dist/marquee.js.map +1 -1
- package/dist/menu.cjs +3 -3
- package/dist/menu.cjs.map +1 -1
- package/dist/menu.js +3 -3
- package/dist/menu.js.map +1 -1
- package/dist/message-composer.cjs +10 -10
- package/dist/message-composer.cjs.map +1 -1
- package/dist/message-composer.js +7 -7
- package/dist/message-composer.js.map +1 -1
- package/dist/navbar.cjs +10 -10
- package/dist/navbar.cjs.map +1 -1
- package/dist/navbar.js +13 -13
- package/dist/navbar.js.map +1 -1
- package/dist/number-input.cjs +3 -3
- package/dist/number-input.cjs.map +1 -1
- package/dist/number-input.js +4 -4
- package/dist/number-input.js.map +1 -1
- package/dist/otp-input.cjs +4 -4
- package/dist/otp-input.cjs.map +1 -1
- package/dist/otp-input.js +4 -4
- package/dist/otp-input.js.map +1 -1
- package/dist/overlay/dialog/dialog.d.ts +7 -4
- package/dist/overlay/dialog/dialog.d.ts.map +1 -1
- package/dist/overlay/drawer/drawer.d.ts +4 -2
- package/dist/overlay/drawer/drawer.d.ts.map +1 -1
- package/dist/overlay/menu/menu.d.ts +2 -1
- package/dist/overlay/menu/menu.d.ts.map +1 -1
- package/dist/pagination.cjs +9 -9
- package/dist/pagination.cjs.map +1 -1
- package/dist/pagination.js +9 -9
- package/dist/pagination.js.map +1 -1
- package/dist/qr-code.cjs +2 -2
- package/dist/qr-code.cjs.map +1 -1
- package/dist/qr-code.js +2 -2
- package/dist/qr-code.js.map +1 -1
- package/dist/query-bar.cjs +7 -7
- package/dist/query-bar.cjs.map +1 -1
- package/dist/query-bar.js +8 -8
- package/dist/query-bar.js.map +1 -1
- package/dist/radio-group.cjs +1 -1
- package/dist/radio-group.cjs.map +1 -1
- package/dist/radio-group.js +1 -1
- package/dist/radio-group.js.map +1 -1
- package/dist/refine.cjs +123 -123
- package/dist/refine.cjs.map +1 -1
- package/dist/refine.iife.js +123 -123
- package/dist/refine.iife.js.map +1 -1
- package/dist/refine.js +124 -124
- package/dist/refine.js.map +1 -1
- package/dist/select.cjs +13 -13
- package/dist/select.cjs.map +1 -1
- package/dist/select.js +15 -15
- package/dist/select.js.map +1 -1
- package/dist/separator.cjs +4 -4
- package/dist/separator.cjs.map +1 -1
- package/dist/separator.js +4 -4
- package/dist/separator.js.map +1 -1
- package/dist/shared/prop-bundles.cjs +1 -1
- package/dist/shared/prop-bundles.cjs.map +1 -1
- package/dist/shared/prop-bundles.d.ts +7 -8
- package/dist/shared/prop-bundles.d.ts.map +1 -1
- package/dist/shared/prop-bundles.js +1 -1
- package/dist/shared/prop-bundles.js.map +1 -1
- package/dist/shared/types.cjs +2 -0
- package/dist/shared/types.cjs.map +1 -0
- package/dist/shared/types.d.ts +27 -9
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.js +2 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/sidebar.cjs +7 -7
- package/dist/sidebar.cjs.map +1 -1
- package/dist/sidebar.js +4 -4
- package/dist/sidebar.js.map +1 -1
- package/dist/step.cjs +10 -10
- package/dist/step.cjs.map +1 -1
- package/dist/step.js +10 -10
- package/dist/step.js.map +1 -1
- package/dist/tab-item.cjs +4 -4
- package/dist/tab-item.cjs.map +1 -1
- package/dist/tab-item.js +5 -5
- package/dist/tab-item.js.map +1 -1
- package/dist/table.cjs +2 -2
- package/dist/table.cjs.map +1 -1
- package/dist/table.js +2 -2
- package/dist/table.js.map +1 -1
- package/dist/tabs.cjs +2 -2
- package/dist/tabs.cjs.map +1 -1
- package/dist/tabs.js +4 -4
- package/dist/tabs.js.map +1 -1
- package/dist/text.cjs +2 -2
- package/dist/text.cjs.map +1 -1
- package/dist/text.js +2 -2
- package/dist/text.js.map +1 -1
- package/dist/textarea.cjs +7 -7
- package/dist/textarea.cjs.map +1 -1
- package/dist/textarea.js +7 -7
- package/dist/textarea.js.map +1 -1
- package/dist/time-picker.cjs +7 -7
- package/dist/time-picker.cjs.map +1 -1
- package/dist/time-picker.js +7 -7
- package/dist/time-picker.js.map +1 -1
- package/dist/tooltip.cjs +1 -1
- package/dist/tooltip.cjs.map +1 -1
- package/dist/tooltip.js +1 -1
- package/dist/tooltip.js.map +1 -1
- package/package.json +13 -13
package/dist/counter.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./styles/mixins/states.css.cjs"),n=require("./styles/mixins/theme.css.cjs"),r=require("./styles/mixins/variants.css.cjs"),i=require("./core/spinner.cjs"),a=require("./shared/prop-bundles.cjs"),o=require("./icon.cjs"),s=require("./inputs/counter/counter.css.cjs");let c=require("@vielzeug/ripple"),l=require("@vielzeug/ore");var u=
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./styles/mixins/states.css.cjs"),n=require("./styles/mixins/theme.css.cjs"),r=require("./styles/mixins/variants.css.cjs"),i=require("./core/spinner.cjs"),a=require("./shared/prop-bundles.cjs"),o=require("./icon.cjs"),s=require("./inputs/counter/counter.css.cjs");let c=require("@vielzeug/ripple"),l=require("@vielzeug/ore");var u=[`solid`,`flat`,`bordered`,`outline`,`ghost`,`frost`],d=400,f=120,p=`ore-counter`;(0,l.define)(p,{props:{...a.themableBundle,...a.sizableBundle,...a.disablableBundle,hint:l.prop.string(),label:l.prop.string(),"large-step":l.prop.json(void 0),max:l.prop.json(void 0),min:l.prop.number(0),"quick-steps":l.prop.bool(!1),readonly:l.prop.bool(!1),step:l.prop.number(1),value:l.prop.number(0),variant:l.prop.oneOf(u)},setup(e){let t=(0,l.getHost)(),n=(0,l.useEmit)(),r=(0,l.createId)(`counter-label`),a=(0,l.createId)(`counter-hint`),o=(0,c.signal)(Number(e.value.value)||0);(0,c.watch)(e.value,e=>{let t=Number(e);Number.isFinite(t)&&t!==o.value&&(o.value=t)});let s=(0,c.computed)(()=>!!e.disabled.value),u=(0,c.computed)(()=>!!e.readonly.value),d=(0,c.computed)(()=>!s.value&&!u.value),f=(0,c.computed)(()=>!!e.hint.value),p=(0,c.signal)(!1),m=null,commit=e=>{if(e==null)return;let r=e-o.value;r!==0&&(o.value=e,t.setAttribute(`value`,String(e)),n(`change`,{delta:r,value:e}),m&&clearTimeout(m),p.value=!1,p.value=!0,m=setTimeout(()=>{p.value=!1,m=null},200))},h=i.createSpinnerControl({commit,disabled:s,largeStep:e[`large-step`],max:e.max,min:e.min,parse:()=>o.value,readonly:u,step:e.step}),stepSize=()=>Number(e.step.value)||1,largeStepSize=()=>Number(e[`large-step`].value)||stepSize()*10,g=(0,c.computed)(()=>!!e[`quick-steps`].value),_=null,v=null,y=!1,stopHold=()=>{_&&clearTimeout(_),v&&clearInterval(v),_=null,v=null},startHold=(e,t)=>{d.value&&t.button===0&&(stopHold(),y=!1,_=setTimeout(()=>{y=!0,h.incrementBy(e(),t),v=setInterval(()=>{let n=e();if(n>0&&h.atMax()||n<0&&h.atMin()){stopHold();return}h.incrementBy(n,t)},120)},400))},handleClick=(e,t)=>{if(y){y=!1;return}h.incrementBy(e(),t)},small=e=>()=>e*stepSize(),large=e=>()=>e*largeStepSize(),quickLabel=e=>()=>`${e>0?`+`:`−`}${largeStepSize()}`;(0,l.onCleanup)(()=>{stopHold(),m&&clearTimeout(m)}),(0,l.bind)({attr:{"data-bump":()=>p.value?``:null,size:e.size}});let b=(0,c.computed)(()=>f.value?a:null);return l.html`
|
|
2
2
|
<div class="counter" part="counter" role="group" aria-labelledby="${r}">
|
|
3
3
|
<div class="header" part="header">
|
|
4
4
|
<slot name="icon"></slot>
|
|
@@ -88,5 +88,5 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
|
|
|
88
88
|
</span>
|
|
89
89
|
</slot>
|
|
90
90
|
</div>
|
|
91
|
-
`},shadow:{delegatesFocus:!0},styles:[n.colorThemeMixin,t.disabledStateMixin,e.coarsePointerMixin,e.reducedMotionMixin,r.frostVariantMixin(`.counter`),s.default]}),exports.COUNTER_HOLD_DELAY_MS=
|
|
91
|
+
`},shadow:{delegatesFocus:!0},styles:[n.colorThemeMixin,t.disabledStateMixin,e.coarsePointerMixin,e.reducedMotionMixin,r.frostVariantMixin(`.counter`),s.default]}),exports.COUNTER_HOLD_DELAY_MS=d,exports.COUNTER_HOLD_REPEAT_MS=f,exports.COUNTER_TAG=p,exports.ICON_TAG=o.ICON_TAG;
|
|
92
92
|
//# sourceMappingURL=counter.cjs.map
|
package/dist/counter.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"counter.cjs","names":[],"sources":["../src/inputs/counter/counter.ts"],"sourcesContent":["import { bind, createId, define, getHost, html, onCleanup, prop, useEmit } from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\nimport { createSpinnerControl } from '../../core';\nimport { disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledStateMixin,\n frostVariantMixin,\n reducedMotionMixin,\n} from '../../styles';\nimport type { ComponentSize, ThemeColor, VisualVariant } from '../../types';\nimport componentStyles from './counter.css?inline';\n\nexport { ICON_TAG } from '../../content/icon/icon';\nexport type OreCounterChangeDetail = {\n /** Difference between the new and the previous value */\n delta: number;\n /** New value after clamping */\n value: number;\n};\n\nexport type OreCounterEvents = {\n change: OreCounterChangeDetail;\n};\n\n/** Counter props */\nexport type OreCounterProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** One-line timing or usage hint rendered under the controls */\n hint?: string;\n /** Visible label; also names the control for assistive technology */\n label?: string;\n /** Step for Page Up/Down (default: 10 × step) */\n 'large-step'?: number;\n /** Maximum allowed value */\n max?: number;\n /** Minimum allowed value (default: 0) */\n min?: number;\n /** Render an outer pair of −/+ buttons that move by `large-step` */\n 'quick-steps'?: boolean;\n /** Show the value without controls */\n readonly?: boolean;\n /** Component size */\n size?: ComponentSize;\n /** Increment/decrement step (default: 1) */\n step?: number;\n /** Surface variant; 'frost' turns the panel into frosted glass over artwork */\n variant?: Exclude<VisualVariant, 'text'>;\n /** Current value */\n value?: number;\n};\n\n/** Hold delay before auto-repeat starts, then the repeat interval. */\nexport const COUNTER_HOLD_DELAY_MS = 400;\nexport const COUNTER_HOLD_REPEAT_MS = 120;\n\n/**\n * A large-target tally control for counting things at a glance: `−` / value / `+` with an\n * optional icon, label and hint. Designed for touch and arm's-length use (tabletop trackers,\n * inventories, scoreboards) rather than typed form entry: use `ore-number-input` for that.\n *\n * Holding any button auto-repeats. The value is announced through a live region and is\n * keyboard-operable as a spinbutton (Arrow keys, Home/End, Page Up/Down). Set `quick-steps` to add an\n * outer pair of buttons that move by `large-step`, for values that change in chunks (damage, scores).\n *\n * @element ore-counter\n *\n * @attr {number} value - Current value (default: 0)\n * @attr {number} min - Minimum value (default: 0)\n * @attr {number} max - Maximum value\n * @attr {number} step - Increment/decrement step (default: 1)\n * @attr {number} large-step - Step for Page Up/Down and the quick-step buttons (default: 10 × step)\n * @attr {boolean} quick-steps - Adds an outer −/+ pair that moves by `large-step`\n * @attr {boolean} disabled - Disables the control\n * @attr {boolean} readonly - Shows the value without the −/+ buttons\n * @attr {string} label - Visible label and accessible name\n * @attr {string} hint - One-line hint under the controls\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - 'sm' | 'md' | 'lg'\n * @attr {string} variant - Surface variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost' | 'frost'\n *\n * @fires change - Fired after every accepted change. detail: { value: number, delta: number }\n *\n * @slot icon - Leading icon or token artwork shown before the label\n * @slot header-end - Trailing header action beside the label\n * @slot hint - Custom hint content (replaces the `hint` attribute)\n *\n * @cssprop --counter-value-size - Font size of the value\n * @cssprop --counter-button-size - Width/height of the −/+ buttons\n * @cssprop --counter-gap - Gap between the buttons and the value\n * @cssprop --counter-bg - Background of the control\n * @cssprop --counter-border-color - Border color of the control\n * @cssprop --counter-radius - Border radius of the control\n * @cssprop --counter-icon-size - Size of the slotted icon\n *\n * @part counter - Root container.\n * @part header - Icon and label row.\n * @part label - Label element.\n * @part controls - Buttons and value row.\n * @part decrement-large-btn - Quick-step decrement button (`quick-steps` only).\n * @part decrement-btn - Decrement button.\n * @part value - Value output.\n * @part increment-btn - Increment button.\n * @part increment-large-btn - Quick-step increment button (`quick-steps` only).\n * @part hint - Hint element.\n *\n * @example\n * ```html\n * <ore-counter label=\"Stamina\" value=\"3\" hint=\"Pay for actions\">\n * <ore-icon slot=\"icon\" name=\"zap\"></ore-icon>\n * </ore-counter>\n * <ore-counter label=\"Score\" value=\"10\" min=\"0\" max=\"99\" size=\"lg\" color=\"primary\"></ore-counter>\n * <ore-counter label=\"Damage\" value=\"12\" large-step=\"5\" quick-steps></ore-counter>\n * <ore-counter label=\"Wounds\" value=\"1\" max=\"3\" variant=\"frost\" color=\"error\"></ore-counter>\n * ```\n */\nexport const COUNTER_TAG = 'ore-counter' as const;\ndefine<OreCounterProps>(COUNTER_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n hint: prop.string(),\n label: prop.string(),\n 'large-step': prop.json(undefined as number | undefined),\n max: prop.json(undefined as number | undefined),\n min: prop.number(0),\n 'quick-steps': prop.bool(false),\n readonly: prop.bool(false),\n step: prop.number(1),\n value: prop.number(0),\n variant: prop.string<Exclude<VisualVariant, 'text'>>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreCounterEvents>();\n const labelId = createId('counter-label');\n const hintId = createId('counter-hint');\n\n const current = signal(Number(props.value.value) || 0);\n\n watch(props.value, (next) => {\n const parsed = Number(next);\n\n if (Number.isFinite(parsed) && parsed !== current.value) current.value = parsed;\n });\n\n const isDisabled = computed(() => Boolean(props.disabled.value));\n const isReadonly = computed(() => Boolean(props.readonly.value));\n const isInteractive = computed(() => !isDisabled.value && !isReadonly.value);\n const hasHint = computed(() => Boolean(props.hint.value));\n\n // Toggled briefly on every change so the CSS bump animation restarts.\n const bumped = signal(false);\n let bumpTimer: ReturnType<typeof setTimeout> | null = null;\n\n const commit = (next: number | null): void => {\n if (next == null) return;\n\n const delta = next - current.value;\n\n if (delta === 0) return;\n\n current.value = next;\n el.setAttribute('value', String(next));\n emit('change', { delta, value: next });\n\n if (bumpTimer) clearTimeout(bumpTimer);\n bumped.value = false;\n bumped.value = true;\n bumpTimer = setTimeout(() => {\n bumped.value = false;\n bumpTimer = null;\n }, 200);\n };\n\n const spinner = createSpinnerControl({\n commit,\n disabled: isDisabled,\n largeStep: props['large-step'],\n max: props.max,\n min: props.min,\n parse: () => current.value,\n readonly: isReadonly,\n step: props.step,\n });\n\n const stepSize = (): number => Number(props.step.value) || 1;\n const largeStepSize = (): number => Number(props['large-step'].value) || stepSize() * 10;\n const hasQuickSteps = computed(() => Boolean(props['quick-steps'].value));\n\n // ── Press-and-hold auto-repeat ──────────────────────────────────────────\n let holdTimer: ReturnType<typeof setTimeout> | null = null;\n let repeatTimer: ReturnType<typeof setInterval> | null = null;\n let repeated = false;\n\n const stopHold = (): void => {\n if (holdTimer) clearTimeout(holdTimer);\n if (repeatTimer) clearInterval(repeatTimer);\n holdTimer = null;\n repeatTimer = null;\n };\n\n const startHold = (amount: () => number, e: PointerEvent): void => {\n if (!isInteractive.value || e.button !== 0) return;\n\n stopHold();\n repeated = false;\n holdTimer = setTimeout(() => {\n repeated = true;\n spinner.incrementBy(amount(), e);\n repeatTimer = setInterval(() => {\n const direction = amount();\n\n if ((direction > 0 && spinner.atMax()) || (direction < 0 && spinner.atMin())) {\n stopHold();\n\n return;\n }\n\n spinner.incrementBy(direction, e);\n }, COUNTER_HOLD_REPEAT_MS);\n }, COUNTER_HOLD_DELAY_MS);\n };\n\n // The click that ends a long-press must not add one more step on top of the repeats.\n const handleClick = (amount: () => number, e: Event): void => {\n if (repeated) {\n repeated = false;\n\n return;\n }\n\n spinner.incrementBy(amount(), e);\n };\n\n const small = (direction: 1 | -1) => (): number => direction * stepSize();\n const large = (direction: 1 | -1) => (): number => direction * largeStepSize();\n const quickLabel = (direction: 1 | -1) => (): string => `${direction > 0 ? '+' : '−'}${largeStepSize()}`;\n\n onCleanup(() => {\n stopHold();\n if (bumpTimer) clearTimeout(bumpTimer);\n });\n\n bind({\n attr: {\n 'data-bump': () => (bumped.value ? '' : null),\n size: props.size,\n },\n });\n\n const describedBy = computed(() => (hasHint.value ? hintId : null));\n\n return html`\n <div class=\"counter\" part=\"counter\" role=\"group\" aria-labelledby=\"${labelId}\">\n <div class=\"header\" part=\"header\">\n <slot name=\"icon\"></slot>\n <span class=\"label\" part=\"label\" id=\"${labelId}\">${() => props.label.value ?? ''}</span>\n <span class=\"header-end\"><slot name=\"header-end\"></slot></span>\n </div>\n <div class=\"controls\" part=\"controls\" @keydown=\"${(e: KeyboardEvent) => spinner.handleKeydown(e)}\">\n <button\n class=\"btn btn-large\"\n part=\"decrement-large-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Decrease ${props.label.value ?? ''} by ${largeStepSize()}`.trim()}\"\n ?hidden=\"${() => isReadonly.value || !hasQuickSteps.value}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMin()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(large(-1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(large(-1), e)}\">\n ${quickLabel(-1)}\n </button>\n <button\n class=\"btn\"\n part=\"decrement-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Decrease ${props.label.value ?? ''}`.trim()}\"\n ?hidden=\"${isReadonly}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMin()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(small(-1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(small(-1), e)}\">\n <ore-icon name=\"minus\" size=\"var(--_icon-size)\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n <output\n class=\"value\"\n part=\"value\"\n role=\"spinbutton\"\n aria-live=\"polite\"\n aria-labelledby=\"${labelId}\"\n aria-describedby=\"${describedBy}\"\n aria-valuenow=\"${() => String(current.value)}\"\n aria-valuemin=\"${() => (props.min.value != null ? String(props.min.value) : null)}\"\n aria-valuemax=\"${() => (props.max.value != null ? String(props.max.value) : null)}\"\n aria-readonly=\"${() => (isReadonly.value ? 'true' : null)}\"\n aria-disabled=\"${() => (isDisabled.value ? 'true' : null)}\"\n tabindex=\"${() => (isDisabled.value ? '-1' : '0')}\">\n ${() => String(current.value)}\n </output>\n <button\n class=\"btn\"\n part=\"increment-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Increase ${props.label.value ?? ''}`.trim()}\"\n ?hidden=\"${isReadonly}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMax()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(small(1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(small(1), e)}\">\n <ore-icon name=\"plus\" size=\"var(--_icon-size)\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n <button\n class=\"btn btn-large\"\n part=\"increment-large-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Increase ${props.label.value ?? ''} by ${largeStepSize()}`.trim()}\"\n ?hidden=\"${() => isReadonly.value || !hasQuickSteps.value}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMax()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(large(1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(large(1), e)}\">\n ${quickLabel(1)}\n </button>\n </div>\n <slot name=\"hint\">\n <span class=\"hint\" part=\"hint\" id=\"${hintId}\" ?hidden=\"${() => !hasHint.value}\">\n ${() => props.hint.value ?? ''}\n </span>\n </slot>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [\n colorThemeMixin,\n disabledStateMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n frostVariantMixin('.counter'),\n componentStyles,\n ],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"counter.cjs","names":[],"sources":["../src/inputs/counter/counter.ts"],"sourcesContent":["import { bind, createId, define, getHost, html, onCleanup, prop, useEmit } from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\nimport { createSpinnerControl } from '../../core';\nimport { disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledStateMixin,\n frostVariantMixin,\n reducedMotionMixin,\n} from '../../styles';\nimport type { ComponentSize, ThemeColor } from '../../types';\nimport componentStyles from './counter.css?inline';\n\nconst COUNTER_VARIANTS = ['solid', 'flat', 'bordered', 'outline', 'ghost', 'frost'] as const;\n\nexport { ICON_TAG } from '../../content/icon/icon';\nexport type OreCounterChangeDetail = {\n /** Difference between the new and the previous value */\n delta: number;\n /** New value after clamping */\n value: number;\n};\n\nexport type OreCounterEvents = {\n change: OreCounterChangeDetail;\n};\n\n/** Counter props */\nexport type OreCounterProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** One-line timing or usage hint rendered under the controls */\n hint?: string;\n /** Visible label; also names the control for assistive technology */\n label?: string;\n /** Step for Page Up/Down (default: 10 × step) */\n 'large-step'?: number;\n /** Maximum allowed value */\n max?: number;\n /** Minimum allowed value (default: 0) */\n min?: number;\n /** Render an outer pair of −/+ buttons that move by `large-step` */\n 'quick-steps'?: boolean;\n /** Show the value without controls */\n readonly?: boolean;\n /** Component size */\n size?: ComponentSize;\n /** Increment/decrement step (default: 1) */\n step?: number;\n /** Surface variant; 'frost' turns the panel into frosted glass over artwork */\n variant?: (typeof COUNTER_VARIANTS)[number];\n /** Current value */\n value?: number;\n};\n\n/** Hold delay before auto-repeat starts, then the repeat interval. */\nexport const COUNTER_HOLD_DELAY_MS = 400;\nexport const COUNTER_HOLD_REPEAT_MS = 120;\n\n/**\n * A large-target tally control for counting things at a glance: `−` / value / `+` with an\n * optional icon, label and hint. Designed for touch and arm's-length use (tabletop trackers,\n * inventories, scoreboards) rather than typed form entry: use `ore-number-input` for that.\n *\n * Holding any button auto-repeats. The value is announced through a live region and is\n * keyboard-operable as a spinbutton (Arrow keys, Home/End, Page Up/Down). Set `quick-steps` to add an\n * outer pair of buttons that move by `large-step`, for values that change in chunks (damage, scores).\n *\n * @element ore-counter\n *\n * @attr {number} value - Current value (default: 0)\n * @attr {number} min - Minimum value (default: 0)\n * @attr {number} max - Maximum value\n * @attr {number} step - Increment/decrement step (default: 1)\n * @attr {number} large-step - Step for Page Up/Down and the quick-step buttons (default: 10 × step)\n * @attr {boolean} quick-steps - Adds an outer −/+ pair that moves by `large-step`\n * @attr {boolean} disabled - Disables the control\n * @attr {boolean} readonly - Shows the value without the −/+ buttons\n * @attr {string} label - Visible label and accessible name\n * @attr {string} hint - One-line hint under the controls\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - 'sm' | 'md' | 'lg'\n * @attr {string} variant - Surface variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost' | 'frost'\n *\n * @fires change - Fired after every accepted change. detail: { value: number, delta: number }\n *\n * @slot icon - Leading icon or token artwork shown before the label\n * @slot header-end - Trailing header action beside the label\n * @slot hint - Custom hint content (replaces the `hint` attribute)\n *\n * @cssprop --counter-value-size - Font size of the value\n * @cssprop --counter-button-size - Width/height of the −/+ buttons\n * @cssprop --counter-gap - Gap between the buttons and the value\n * @cssprop --counter-bg - Background of the control\n * @cssprop --counter-border-color - Border color of the control\n * @cssprop --counter-radius - Border radius of the control\n * @cssprop --counter-icon-size - Size of the slotted icon\n *\n * @part counter - Root container.\n * @part header - Icon and label row.\n * @part label - Label element.\n * @part controls - Buttons and value row.\n * @part decrement-large-btn - Quick-step decrement button (`quick-steps` only).\n * @part decrement-btn - Decrement button.\n * @part value - Value output.\n * @part increment-btn - Increment button.\n * @part increment-large-btn - Quick-step increment button (`quick-steps` only).\n * @part hint - Hint element.\n *\n * @example\n * ```html\n * <ore-counter label=\"Stamina\" value=\"3\" hint=\"Pay for actions\">\n * <ore-icon slot=\"icon\" name=\"zap\"></ore-icon>\n * </ore-counter>\n * <ore-counter label=\"Score\" value=\"10\" min=\"0\" max=\"99\" size=\"lg\" color=\"primary\"></ore-counter>\n * <ore-counter label=\"Damage\" value=\"12\" large-step=\"5\" quick-steps></ore-counter>\n * <ore-counter label=\"Wounds\" value=\"1\" max=\"3\" variant=\"frost\" color=\"error\"></ore-counter>\n * ```\n */\nexport const COUNTER_TAG = 'ore-counter' as const;\ndefine<OreCounterProps>(COUNTER_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n hint: prop.string(),\n label: prop.string(),\n 'large-step': prop.json(undefined as number | undefined),\n max: prop.json(undefined as number | undefined),\n min: prop.number(0),\n 'quick-steps': prop.bool(false),\n readonly: prop.bool(false),\n step: prop.number(1),\n value: prop.number(0),\n variant: prop.oneOf(COUNTER_VARIANTS),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreCounterEvents>();\n const labelId = createId('counter-label');\n const hintId = createId('counter-hint');\n\n const current = signal(Number(props.value.value) || 0);\n\n watch(props.value, (next) => {\n const parsed = Number(next);\n\n if (Number.isFinite(parsed) && parsed !== current.value) current.value = parsed;\n });\n\n const isDisabled = computed(() => Boolean(props.disabled.value));\n const isReadonly = computed(() => Boolean(props.readonly.value));\n const isInteractive = computed(() => !isDisabled.value && !isReadonly.value);\n const hasHint = computed(() => Boolean(props.hint.value));\n\n // Toggled briefly on every change so the CSS bump animation restarts.\n const bumped = signal(false);\n let bumpTimer: ReturnType<typeof setTimeout> | null = null;\n\n const commit = (next: number | null): void => {\n if (next == null) return;\n\n const delta = next - current.value;\n\n if (delta === 0) return;\n\n current.value = next;\n el.setAttribute('value', String(next));\n emit('change', { delta, value: next });\n\n if (bumpTimer) clearTimeout(bumpTimer);\n bumped.value = false;\n bumped.value = true;\n bumpTimer = setTimeout(() => {\n bumped.value = false;\n bumpTimer = null;\n }, 200);\n };\n\n const spinner = createSpinnerControl({\n commit,\n disabled: isDisabled,\n largeStep: props['large-step'],\n max: props.max,\n min: props.min,\n parse: () => current.value,\n readonly: isReadonly,\n step: props.step,\n });\n\n const stepSize = (): number => Number(props.step.value) || 1;\n const largeStepSize = (): number => Number(props['large-step'].value) || stepSize() * 10;\n const hasQuickSteps = computed(() => Boolean(props['quick-steps'].value));\n\n // ── Press-and-hold auto-repeat ──────────────────────────────────────────\n let holdTimer: ReturnType<typeof setTimeout> | null = null;\n let repeatTimer: ReturnType<typeof setInterval> | null = null;\n let repeated = false;\n\n const stopHold = (): void => {\n if (holdTimer) clearTimeout(holdTimer);\n if (repeatTimer) clearInterval(repeatTimer);\n holdTimer = null;\n repeatTimer = null;\n };\n\n const startHold = (amount: () => number, e: PointerEvent): void => {\n if (!isInteractive.value || e.button !== 0) return;\n\n stopHold();\n repeated = false;\n holdTimer = setTimeout(() => {\n repeated = true;\n spinner.incrementBy(amount(), e);\n repeatTimer = setInterval(() => {\n const direction = amount();\n\n if ((direction > 0 && spinner.atMax()) || (direction < 0 && spinner.atMin())) {\n stopHold();\n\n return;\n }\n\n spinner.incrementBy(direction, e);\n }, COUNTER_HOLD_REPEAT_MS);\n }, COUNTER_HOLD_DELAY_MS);\n };\n\n // The click that ends a long-press must not add one more step on top of the repeats.\n const handleClick = (amount: () => number, e: Event): void => {\n if (repeated) {\n repeated = false;\n\n return;\n }\n\n spinner.incrementBy(amount(), e);\n };\n\n const small = (direction: 1 | -1) => (): number => direction * stepSize();\n const large = (direction: 1 | -1) => (): number => direction * largeStepSize();\n const quickLabel = (direction: 1 | -1) => (): string => `${direction > 0 ? '+' : '−'}${largeStepSize()}`;\n\n onCleanup(() => {\n stopHold();\n if (bumpTimer) clearTimeout(bumpTimer);\n });\n\n bind({\n attr: {\n 'data-bump': () => (bumped.value ? '' : null),\n size: props.size,\n },\n });\n\n const describedBy = computed(() => (hasHint.value ? hintId : null));\n\n return html`\n <div class=\"counter\" part=\"counter\" role=\"group\" aria-labelledby=\"${labelId}\">\n <div class=\"header\" part=\"header\">\n <slot name=\"icon\"></slot>\n <span class=\"label\" part=\"label\" id=\"${labelId}\">${() => props.label.value ?? ''}</span>\n <span class=\"header-end\"><slot name=\"header-end\"></slot></span>\n </div>\n <div class=\"controls\" part=\"controls\" @keydown=\"${(e: KeyboardEvent) => spinner.handleKeydown(e)}\">\n <button\n class=\"btn btn-large\"\n part=\"decrement-large-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Decrease ${props.label.value ?? ''} by ${largeStepSize()}`.trim()}\"\n ?hidden=\"${() => isReadonly.value || !hasQuickSteps.value}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMin()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(large(-1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(large(-1), e)}\">\n ${quickLabel(-1)}\n </button>\n <button\n class=\"btn\"\n part=\"decrement-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Decrease ${props.label.value ?? ''}`.trim()}\"\n ?hidden=\"${isReadonly}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMin()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(small(-1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(small(-1), e)}\">\n <ore-icon name=\"minus\" size=\"var(--_icon-size)\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n <output\n class=\"value\"\n part=\"value\"\n role=\"spinbutton\"\n aria-live=\"polite\"\n aria-labelledby=\"${labelId}\"\n aria-describedby=\"${describedBy}\"\n aria-valuenow=\"${() => String(current.value)}\"\n aria-valuemin=\"${() => (props.min.value != null ? String(props.min.value) : null)}\"\n aria-valuemax=\"${() => (props.max.value != null ? String(props.max.value) : null)}\"\n aria-readonly=\"${() => (isReadonly.value ? 'true' : null)}\"\n aria-disabled=\"${() => (isDisabled.value ? 'true' : null)}\"\n tabindex=\"${() => (isDisabled.value ? '-1' : '0')}\">\n ${() => String(current.value)}\n </output>\n <button\n class=\"btn\"\n part=\"increment-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Increase ${props.label.value ?? ''}`.trim()}\"\n ?hidden=\"${isReadonly}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMax()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(small(1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(small(1), e)}\">\n <ore-icon name=\"plus\" size=\"var(--_icon-size)\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n <button\n class=\"btn btn-large\"\n part=\"increment-large-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Increase ${props.label.value ?? ''} by ${largeStepSize()}`.trim()}\"\n ?hidden=\"${() => isReadonly.value || !hasQuickSteps.value}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMax()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(large(1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(large(1), e)}\">\n ${quickLabel(1)}\n </button>\n </div>\n <slot name=\"hint\">\n <span class=\"hint\" part=\"hint\" id=\"${hintId}\" ?hidden=\"${() => !hasHint.value}\">\n ${() => props.hint.value ?? ''}\n </span>\n </slot>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [\n colorThemeMixin,\n disabledStateMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n frostVariantMixin('.counter'),\n componentStyles,\n ],\n});\n"],"mappings":"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"}
|
package/dist/counter.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{coarsePointerMixin as e,reducedMotionMixin as t}from"./styles/mixins/accessibility.css.js";import{disabledStateMixin as n}from"./styles/mixins/states.css.js";import{colorThemeMixin as r}from"./styles/mixins/theme.css.js";import{frostVariantMixin as i}from"./styles/mixins/variants.css.js";import{createSpinnerControl as a}from"./core/spinner.js";import{disablableBundle as o,sizableBundle as s,themableBundle as c}from"./shared/prop-bundles.js";import{ICON_TAG as l}from"./icon.js";import u from"./inputs/counter/counter.css.js";import{computed as d,signal as f,watch as p}from"@vielzeug/ripple";import{bind as m,createId as h,define as g,getHost as _,html as v,onCleanup as y,prop as b,useEmit as x}from"@vielzeug/ore";var S=
|
|
1
|
+
import{coarsePointerMixin as e,reducedMotionMixin as t}from"./styles/mixins/accessibility.css.js";import{disabledStateMixin as n}from"./styles/mixins/states.css.js";import{colorThemeMixin as r}from"./styles/mixins/theme.css.js";import{frostVariantMixin as i}from"./styles/mixins/variants.css.js";import{createSpinnerControl as a}from"./core/spinner.js";import{disablableBundle as o,sizableBundle as s,themableBundle as c}from"./shared/prop-bundles.js";import{ICON_TAG as l}from"./icon.js";import u from"./inputs/counter/counter.css.js";import{computed as d,signal as f,watch as p}from"@vielzeug/ripple";import{bind as m,createId as h,define as g,getHost as _,html as v,onCleanup as y,prop as b,useEmit as x}from"@vielzeug/ore";var S=[`solid`,`flat`,`bordered`,`outline`,`ghost`,`frost`],C=400,w=120,T=`ore-counter`;g(T,{props:{...c,...s,...o,hint:b.string(),label:b.string(),"large-step":b.json(void 0),max:b.json(void 0),min:b.number(0),"quick-steps":b.bool(!1),readonly:b.bool(!1),step:b.number(1),value:b.number(0),variant:b.oneOf(S)},setup(e){let t=_(),n=x(),r=h(`counter-label`),i=h(`counter-hint`),o=f(Number(e.value.value)||0);p(e.value,e=>{let t=Number(e);Number.isFinite(t)&&t!==o.value&&(o.value=t)});let s=d(()=>!!e.disabled.value),c=d(()=>!!e.readonly.value),l=d(()=>!s.value&&!c.value),u=d(()=>!!e.hint.value),g=f(!1),b=null,commit=e=>{if(e==null)return;let r=e-o.value;r!==0&&(o.value=e,t.setAttribute(`value`,String(e)),n(`change`,{delta:r,value:e}),b&&clearTimeout(b),g.value=!1,g.value=!0,b=setTimeout(()=>{g.value=!1,b=null},200))},S=a({commit,disabled:s,largeStep:e[`large-step`],max:e.max,min:e.min,parse:()=>o.value,readonly:c,step:e.step}),stepSize=()=>Number(e.step.value)||1,largeStepSize=()=>Number(e[`large-step`].value)||stepSize()*10,C=d(()=>!!e[`quick-steps`].value),w=null,T=null,E=!1,stopHold=()=>{w&&clearTimeout(w),T&&clearInterval(T),w=null,T=null},startHold=(e,t)=>{l.value&&t.button===0&&(stopHold(),E=!1,w=setTimeout(()=>{E=!0,S.incrementBy(e(),t),T=setInterval(()=>{let n=e();if(n>0&&S.atMax()||n<0&&S.atMin()){stopHold();return}S.incrementBy(n,t)},120)},400))},handleClick=(e,t)=>{if(E){E=!1;return}S.incrementBy(e(),t)},small=e=>()=>e*stepSize(),large=e=>()=>e*largeStepSize(),quickLabel=e=>()=>`${e>0?`+`:`−`}${largeStepSize()}`;y(()=>{stopHold(),b&&clearTimeout(b)}),m({attr:{"data-bump":()=>g.value?``:null,size:e.size}});let D=d(()=>u.value?i:null);return v`
|
|
2
2
|
<div class="counter" part="counter" role="group" aria-labelledby="${r}">
|
|
3
3
|
<div class="header" part="header">
|
|
4
4
|
<slot name="icon"></slot>
|
|
@@ -88,5 +88,5 @@ import{coarsePointerMixin as e,reducedMotionMixin as t}from"./styles/mixins/acce
|
|
|
88
88
|
</span>
|
|
89
89
|
</slot>
|
|
90
90
|
</div>
|
|
91
|
-
`},shadow:{delegatesFocus:!0},styles:[r,n,e,t,i(`.counter`),u]});export{
|
|
91
|
+
`},shadow:{delegatesFocus:!0},styles:[r,n,e,t,i(`.counter`),u]});export{C as COUNTER_HOLD_DELAY_MS,w as COUNTER_HOLD_REPEAT_MS,T as COUNTER_TAG,l as ICON_TAG};
|
|
92
92
|
//# sourceMappingURL=counter.js.map
|
package/dist/counter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"counter.js","names":[],"sources":["../src/inputs/counter/counter.ts"],"sourcesContent":["import { bind, createId, define, getHost, html, onCleanup, prop, useEmit } from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\nimport { createSpinnerControl } from '../../core';\nimport { disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledStateMixin,\n frostVariantMixin,\n reducedMotionMixin,\n} from '../../styles';\nimport type { ComponentSize, ThemeColor, VisualVariant } from '../../types';\nimport componentStyles from './counter.css?inline';\n\nexport { ICON_TAG } from '../../content/icon/icon';\nexport type OreCounterChangeDetail = {\n /** Difference between the new and the previous value */\n delta: number;\n /** New value after clamping */\n value: number;\n};\n\nexport type OreCounterEvents = {\n change: OreCounterChangeDetail;\n};\n\n/** Counter props */\nexport type OreCounterProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** One-line timing or usage hint rendered under the controls */\n hint?: string;\n /** Visible label; also names the control for assistive technology */\n label?: string;\n /** Step for Page Up/Down (default: 10 × step) */\n 'large-step'?: number;\n /** Maximum allowed value */\n max?: number;\n /** Minimum allowed value (default: 0) */\n min?: number;\n /** Render an outer pair of −/+ buttons that move by `large-step` */\n 'quick-steps'?: boolean;\n /** Show the value without controls */\n readonly?: boolean;\n /** Component size */\n size?: ComponentSize;\n /** Increment/decrement step (default: 1) */\n step?: number;\n /** Surface variant; 'frost' turns the panel into frosted glass over artwork */\n variant?: Exclude<VisualVariant, 'text'>;\n /** Current value */\n value?: number;\n};\n\n/** Hold delay before auto-repeat starts, then the repeat interval. */\nexport const COUNTER_HOLD_DELAY_MS = 400;\nexport const COUNTER_HOLD_REPEAT_MS = 120;\n\n/**\n * A large-target tally control for counting things at a glance: `−` / value / `+` with an\n * optional icon, label and hint. Designed for touch and arm's-length use (tabletop trackers,\n * inventories, scoreboards) rather than typed form entry: use `ore-number-input` for that.\n *\n * Holding any button auto-repeats. The value is announced through a live region and is\n * keyboard-operable as a spinbutton (Arrow keys, Home/End, Page Up/Down). Set `quick-steps` to add an\n * outer pair of buttons that move by `large-step`, for values that change in chunks (damage, scores).\n *\n * @element ore-counter\n *\n * @attr {number} value - Current value (default: 0)\n * @attr {number} min - Minimum value (default: 0)\n * @attr {number} max - Maximum value\n * @attr {number} step - Increment/decrement step (default: 1)\n * @attr {number} large-step - Step for Page Up/Down and the quick-step buttons (default: 10 × step)\n * @attr {boolean} quick-steps - Adds an outer −/+ pair that moves by `large-step`\n * @attr {boolean} disabled - Disables the control\n * @attr {boolean} readonly - Shows the value without the −/+ buttons\n * @attr {string} label - Visible label and accessible name\n * @attr {string} hint - One-line hint under the controls\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - 'sm' | 'md' | 'lg'\n * @attr {string} variant - Surface variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost' | 'frost'\n *\n * @fires change - Fired after every accepted change. detail: { value: number, delta: number }\n *\n * @slot icon - Leading icon or token artwork shown before the label\n * @slot header-end - Trailing header action beside the label\n * @slot hint - Custom hint content (replaces the `hint` attribute)\n *\n * @cssprop --counter-value-size - Font size of the value\n * @cssprop --counter-button-size - Width/height of the −/+ buttons\n * @cssprop --counter-gap - Gap between the buttons and the value\n * @cssprop --counter-bg - Background of the control\n * @cssprop --counter-border-color - Border color of the control\n * @cssprop --counter-radius - Border radius of the control\n * @cssprop --counter-icon-size - Size of the slotted icon\n *\n * @part counter - Root container.\n * @part header - Icon and label row.\n * @part label - Label element.\n * @part controls - Buttons and value row.\n * @part decrement-large-btn - Quick-step decrement button (`quick-steps` only).\n * @part decrement-btn - Decrement button.\n * @part value - Value output.\n * @part increment-btn - Increment button.\n * @part increment-large-btn - Quick-step increment button (`quick-steps` only).\n * @part hint - Hint element.\n *\n * @example\n * ```html\n * <ore-counter label=\"Stamina\" value=\"3\" hint=\"Pay for actions\">\n * <ore-icon slot=\"icon\" name=\"zap\"></ore-icon>\n * </ore-counter>\n * <ore-counter label=\"Score\" value=\"10\" min=\"0\" max=\"99\" size=\"lg\" color=\"primary\"></ore-counter>\n * <ore-counter label=\"Damage\" value=\"12\" large-step=\"5\" quick-steps></ore-counter>\n * <ore-counter label=\"Wounds\" value=\"1\" max=\"3\" variant=\"frost\" color=\"error\"></ore-counter>\n * ```\n */\nexport const COUNTER_TAG = 'ore-counter' as const;\ndefine<OreCounterProps>(COUNTER_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n hint: prop.string(),\n label: prop.string(),\n 'large-step': prop.json(undefined as number | undefined),\n max: prop.json(undefined as number | undefined),\n min: prop.number(0),\n 'quick-steps': prop.bool(false),\n readonly: prop.bool(false),\n step: prop.number(1),\n value: prop.number(0),\n variant: prop.string<Exclude<VisualVariant, 'text'>>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreCounterEvents>();\n const labelId = createId('counter-label');\n const hintId = createId('counter-hint');\n\n const current = signal(Number(props.value.value) || 0);\n\n watch(props.value, (next) => {\n const parsed = Number(next);\n\n if (Number.isFinite(parsed) && parsed !== current.value) current.value = parsed;\n });\n\n const isDisabled = computed(() => Boolean(props.disabled.value));\n const isReadonly = computed(() => Boolean(props.readonly.value));\n const isInteractive = computed(() => !isDisabled.value && !isReadonly.value);\n const hasHint = computed(() => Boolean(props.hint.value));\n\n // Toggled briefly on every change so the CSS bump animation restarts.\n const bumped = signal(false);\n let bumpTimer: ReturnType<typeof setTimeout> | null = null;\n\n const commit = (next: number | null): void => {\n if (next == null) return;\n\n const delta = next - current.value;\n\n if (delta === 0) return;\n\n current.value = next;\n el.setAttribute('value', String(next));\n emit('change', { delta, value: next });\n\n if (bumpTimer) clearTimeout(bumpTimer);\n bumped.value = false;\n bumped.value = true;\n bumpTimer = setTimeout(() => {\n bumped.value = false;\n bumpTimer = null;\n }, 200);\n };\n\n const spinner = createSpinnerControl({\n commit,\n disabled: isDisabled,\n largeStep: props['large-step'],\n max: props.max,\n min: props.min,\n parse: () => current.value,\n readonly: isReadonly,\n step: props.step,\n });\n\n const stepSize = (): number => Number(props.step.value) || 1;\n const largeStepSize = (): number => Number(props['large-step'].value) || stepSize() * 10;\n const hasQuickSteps = computed(() => Boolean(props['quick-steps'].value));\n\n // ── Press-and-hold auto-repeat ──────────────────────────────────────────\n let holdTimer: ReturnType<typeof setTimeout> | null = null;\n let repeatTimer: ReturnType<typeof setInterval> | null = null;\n let repeated = false;\n\n const stopHold = (): void => {\n if (holdTimer) clearTimeout(holdTimer);\n if (repeatTimer) clearInterval(repeatTimer);\n holdTimer = null;\n repeatTimer = null;\n };\n\n const startHold = (amount: () => number, e: PointerEvent): void => {\n if (!isInteractive.value || e.button !== 0) return;\n\n stopHold();\n repeated = false;\n holdTimer = setTimeout(() => {\n repeated = true;\n spinner.incrementBy(amount(), e);\n repeatTimer = setInterval(() => {\n const direction = amount();\n\n if ((direction > 0 && spinner.atMax()) || (direction < 0 && spinner.atMin())) {\n stopHold();\n\n return;\n }\n\n spinner.incrementBy(direction, e);\n }, COUNTER_HOLD_REPEAT_MS);\n }, COUNTER_HOLD_DELAY_MS);\n };\n\n // The click that ends a long-press must not add one more step on top of the repeats.\n const handleClick = (amount: () => number, e: Event): void => {\n if (repeated) {\n repeated = false;\n\n return;\n }\n\n spinner.incrementBy(amount(), e);\n };\n\n const small = (direction: 1 | -1) => (): number => direction * stepSize();\n const large = (direction: 1 | -1) => (): number => direction * largeStepSize();\n const quickLabel = (direction: 1 | -1) => (): string => `${direction > 0 ? '+' : '−'}${largeStepSize()}`;\n\n onCleanup(() => {\n stopHold();\n if (bumpTimer) clearTimeout(bumpTimer);\n });\n\n bind({\n attr: {\n 'data-bump': () => (bumped.value ? '' : null),\n size: props.size,\n },\n });\n\n const describedBy = computed(() => (hasHint.value ? hintId : null));\n\n return html`\n <div class=\"counter\" part=\"counter\" role=\"group\" aria-labelledby=\"${labelId}\">\n <div class=\"header\" part=\"header\">\n <slot name=\"icon\"></slot>\n <span class=\"label\" part=\"label\" id=\"${labelId}\">${() => props.label.value ?? ''}</span>\n <span class=\"header-end\"><slot name=\"header-end\"></slot></span>\n </div>\n <div class=\"controls\" part=\"controls\" @keydown=\"${(e: KeyboardEvent) => spinner.handleKeydown(e)}\">\n <button\n class=\"btn btn-large\"\n part=\"decrement-large-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Decrease ${props.label.value ?? ''} by ${largeStepSize()}`.trim()}\"\n ?hidden=\"${() => isReadonly.value || !hasQuickSteps.value}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMin()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(large(-1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(large(-1), e)}\">\n ${quickLabel(-1)}\n </button>\n <button\n class=\"btn\"\n part=\"decrement-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Decrease ${props.label.value ?? ''}`.trim()}\"\n ?hidden=\"${isReadonly}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMin()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(small(-1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(small(-1), e)}\">\n <ore-icon name=\"minus\" size=\"var(--_icon-size)\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n <output\n class=\"value\"\n part=\"value\"\n role=\"spinbutton\"\n aria-live=\"polite\"\n aria-labelledby=\"${labelId}\"\n aria-describedby=\"${describedBy}\"\n aria-valuenow=\"${() => String(current.value)}\"\n aria-valuemin=\"${() => (props.min.value != null ? String(props.min.value) : null)}\"\n aria-valuemax=\"${() => (props.max.value != null ? String(props.max.value) : null)}\"\n aria-readonly=\"${() => (isReadonly.value ? 'true' : null)}\"\n aria-disabled=\"${() => (isDisabled.value ? 'true' : null)}\"\n tabindex=\"${() => (isDisabled.value ? '-1' : '0')}\">\n ${() => String(current.value)}\n </output>\n <button\n class=\"btn\"\n part=\"increment-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Increase ${props.label.value ?? ''}`.trim()}\"\n ?hidden=\"${isReadonly}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMax()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(small(1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(small(1), e)}\">\n <ore-icon name=\"plus\" size=\"var(--_icon-size)\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n <button\n class=\"btn btn-large\"\n part=\"increment-large-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Increase ${props.label.value ?? ''} by ${largeStepSize()}`.trim()}\"\n ?hidden=\"${() => isReadonly.value || !hasQuickSteps.value}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMax()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(large(1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(large(1), e)}\">\n ${quickLabel(1)}\n </button>\n </div>\n <slot name=\"hint\">\n <span class=\"hint\" part=\"hint\" id=\"${hintId}\" ?hidden=\"${() => !hasHint.value}\">\n ${() => props.hint.value ?? ''}\n </span>\n </slot>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [\n colorThemeMixin,\n disabledStateMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n frostVariantMixin('.counter'),\n componentStyles,\n ],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"counter.js","names":[],"sources":["../src/inputs/counter/counter.ts"],"sourcesContent":["import { bind, createId, define, getHost, html, onCleanup, prop, useEmit } from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\nimport { createSpinnerControl } from '../../core';\nimport { disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledStateMixin,\n frostVariantMixin,\n reducedMotionMixin,\n} from '../../styles';\nimport type { ComponentSize, ThemeColor } from '../../types';\nimport componentStyles from './counter.css?inline';\n\nconst COUNTER_VARIANTS = ['solid', 'flat', 'bordered', 'outline', 'ghost', 'frost'] as const;\n\nexport { ICON_TAG } from '../../content/icon/icon';\nexport type OreCounterChangeDetail = {\n /** Difference between the new and the previous value */\n delta: number;\n /** New value after clamping */\n value: number;\n};\n\nexport type OreCounterEvents = {\n change: OreCounterChangeDetail;\n};\n\n/** Counter props */\nexport type OreCounterProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** One-line timing or usage hint rendered under the controls */\n hint?: string;\n /** Visible label; also names the control for assistive technology */\n label?: string;\n /** Step for Page Up/Down (default: 10 × step) */\n 'large-step'?: number;\n /** Maximum allowed value */\n max?: number;\n /** Minimum allowed value (default: 0) */\n min?: number;\n /** Render an outer pair of −/+ buttons that move by `large-step` */\n 'quick-steps'?: boolean;\n /** Show the value without controls */\n readonly?: boolean;\n /** Component size */\n size?: ComponentSize;\n /** Increment/decrement step (default: 1) */\n step?: number;\n /** Surface variant; 'frost' turns the panel into frosted glass over artwork */\n variant?: (typeof COUNTER_VARIANTS)[number];\n /** Current value */\n value?: number;\n};\n\n/** Hold delay before auto-repeat starts, then the repeat interval. */\nexport const COUNTER_HOLD_DELAY_MS = 400;\nexport const COUNTER_HOLD_REPEAT_MS = 120;\n\n/**\n * A large-target tally control for counting things at a glance: `−` / value / `+` with an\n * optional icon, label and hint. Designed for touch and arm's-length use (tabletop trackers,\n * inventories, scoreboards) rather than typed form entry: use `ore-number-input` for that.\n *\n * Holding any button auto-repeats. The value is announced through a live region and is\n * keyboard-operable as a spinbutton (Arrow keys, Home/End, Page Up/Down). Set `quick-steps` to add an\n * outer pair of buttons that move by `large-step`, for values that change in chunks (damage, scores).\n *\n * @element ore-counter\n *\n * @attr {number} value - Current value (default: 0)\n * @attr {number} min - Minimum value (default: 0)\n * @attr {number} max - Maximum value\n * @attr {number} step - Increment/decrement step (default: 1)\n * @attr {number} large-step - Step for Page Up/Down and the quick-step buttons (default: 10 × step)\n * @attr {boolean} quick-steps - Adds an outer −/+ pair that moves by `large-step`\n * @attr {boolean} disabled - Disables the control\n * @attr {boolean} readonly - Shows the value without the −/+ buttons\n * @attr {string} label - Visible label and accessible name\n * @attr {string} hint - One-line hint under the controls\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - 'sm' | 'md' | 'lg'\n * @attr {string} variant - Surface variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost' | 'frost'\n *\n * @fires change - Fired after every accepted change. detail: { value: number, delta: number }\n *\n * @slot icon - Leading icon or token artwork shown before the label\n * @slot header-end - Trailing header action beside the label\n * @slot hint - Custom hint content (replaces the `hint` attribute)\n *\n * @cssprop --counter-value-size - Font size of the value\n * @cssprop --counter-button-size - Width/height of the −/+ buttons\n * @cssprop --counter-gap - Gap between the buttons and the value\n * @cssprop --counter-bg - Background of the control\n * @cssprop --counter-border-color - Border color of the control\n * @cssprop --counter-radius - Border radius of the control\n * @cssprop --counter-icon-size - Size of the slotted icon\n *\n * @part counter - Root container.\n * @part header - Icon and label row.\n * @part label - Label element.\n * @part controls - Buttons and value row.\n * @part decrement-large-btn - Quick-step decrement button (`quick-steps` only).\n * @part decrement-btn - Decrement button.\n * @part value - Value output.\n * @part increment-btn - Increment button.\n * @part increment-large-btn - Quick-step increment button (`quick-steps` only).\n * @part hint - Hint element.\n *\n * @example\n * ```html\n * <ore-counter label=\"Stamina\" value=\"3\" hint=\"Pay for actions\">\n * <ore-icon slot=\"icon\" name=\"zap\"></ore-icon>\n * </ore-counter>\n * <ore-counter label=\"Score\" value=\"10\" min=\"0\" max=\"99\" size=\"lg\" color=\"primary\"></ore-counter>\n * <ore-counter label=\"Damage\" value=\"12\" large-step=\"5\" quick-steps></ore-counter>\n * <ore-counter label=\"Wounds\" value=\"1\" max=\"3\" variant=\"frost\" color=\"error\"></ore-counter>\n * ```\n */\nexport const COUNTER_TAG = 'ore-counter' as const;\ndefine<OreCounterProps>(COUNTER_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n hint: prop.string(),\n label: prop.string(),\n 'large-step': prop.json(undefined as number | undefined),\n max: prop.json(undefined as number | undefined),\n min: prop.number(0),\n 'quick-steps': prop.bool(false),\n readonly: prop.bool(false),\n step: prop.number(1),\n value: prop.number(0),\n variant: prop.oneOf(COUNTER_VARIANTS),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreCounterEvents>();\n const labelId = createId('counter-label');\n const hintId = createId('counter-hint');\n\n const current = signal(Number(props.value.value) || 0);\n\n watch(props.value, (next) => {\n const parsed = Number(next);\n\n if (Number.isFinite(parsed) && parsed !== current.value) current.value = parsed;\n });\n\n const isDisabled = computed(() => Boolean(props.disabled.value));\n const isReadonly = computed(() => Boolean(props.readonly.value));\n const isInteractive = computed(() => !isDisabled.value && !isReadonly.value);\n const hasHint = computed(() => Boolean(props.hint.value));\n\n // Toggled briefly on every change so the CSS bump animation restarts.\n const bumped = signal(false);\n let bumpTimer: ReturnType<typeof setTimeout> | null = null;\n\n const commit = (next: number | null): void => {\n if (next == null) return;\n\n const delta = next - current.value;\n\n if (delta === 0) return;\n\n current.value = next;\n el.setAttribute('value', String(next));\n emit('change', { delta, value: next });\n\n if (bumpTimer) clearTimeout(bumpTimer);\n bumped.value = false;\n bumped.value = true;\n bumpTimer = setTimeout(() => {\n bumped.value = false;\n bumpTimer = null;\n }, 200);\n };\n\n const spinner = createSpinnerControl({\n commit,\n disabled: isDisabled,\n largeStep: props['large-step'],\n max: props.max,\n min: props.min,\n parse: () => current.value,\n readonly: isReadonly,\n step: props.step,\n });\n\n const stepSize = (): number => Number(props.step.value) || 1;\n const largeStepSize = (): number => Number(props['large-step'].value) || stepSize() * 10;\n const hasQuickSteps = computed(() => Boolean(props['quick-steps'].value));\n\n // ── Press-and-hold auto-repeat ──────────────────────────────────────────\n let holdTimer: ReturnType<typeof setTimeout> | null = null;\n let repeatTimer: ReturnType<typeof setInterval> | null = null;\n let repeated = false;\n\n const stopHold = (): void => {\n if (holdTimer) clearTimeout(holdTimer);\n if (repeatTimer) clearInterval(repeatTimer);\n holdTimer = null;\n repeatTimer = null;\n };\n\n const startHold = (amount: () => number, e: PointerEvent): void => {\n if (!isInteractive.value || e.button !== 0) return;\n\n stopHold();\n repeated = false;\n holdTimer = setTimeout(() => {\n repeated = true;\n spinner.incrementBy(amount(), e);\n repeatTimer = setInterval(() => {\n const direction = amount();\n\n if ((direction > 0 && spinner.atMax()) || (direction < 0 && spinner.atMin())) {\n stopHold();\n\n return;\n }\n\n spinner.incrementBy(direction, e);\n }, COUNTER_HOLD_REPEAT_MS);\n }, COUNTER_HOLD_DELAY_MS);\n };\n\n // The click that ends a long-press must not add one more step on top of the repeats.\n const handleClick = (amount: () => number, e: Event): void => {\n if (repeated) {\n repeated = false;\n\n return;\n }\n\n spinner.incrementBy(amount(), e);\n };\n\n const small = (direction: 1 | -1) => (): number => direction * stepSize();\n const large = (direction: 1 | -1) => (): number => direction * largeStepSize();\n const quickLabel = (direction: 1 | -1) => (): string => `${direction > 0 ? '+' : '−'}${largeStepSize()}`;\n\n onCleanup(() => {\n stopHold();\n if (bumpTimer) clearTimeout(bumpTimer);\n });\n\n bind({\n attr: {\n 'data-bump': () => (bumped.value ? '' : null),\n size: props.size,\n },\n });\n\n const describedBy = computed(() => (hasHint.value ? hintId : null));\n\n return html`\n <div class=\"counter\" part=\"counter\" role=\"group\" aria-labelledby=\"${labelId}\">\n <div class=\"header\" part=\"header\">\n <slot name=\"icon\"></slot>\n <span class=\"label\" part=\"label\" id=\"${labelId}\">${() => props.label.value ?? ''}</span>\n <span class=\"header-end\"><slot name=\"header-end\"></slot></span>\n </div>\n <div class=\"controls\" part=\"controls\" @keydown=\"${(e: KeyboardEvent) => spinner.handleKeydown(e)}\">\n <button\n class=\"btn btn-large\"\n part=\"decrement-large-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Decrease ${props.label.value ?? ''} by ${largeStepSize()}`.trim()}\"\n ?hidden=\"${() => isReadonly.value || !hasQuickSteps.value}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMin()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(large(-1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(large(-1), e)}\">\n ${quickLabel(-1)}\n </button>\n <button\n class=\"btn\"\n part=\"decrement-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Decrease ${props.label.value ?? ''}`.trim()}\"\n ?hidden=\"${isReadonly}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMin()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(small(-1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(small(-1), e)}\">\n <ore-icon name=\"minus\" size=\"var(--_icon-size)\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n <output\n class=\"value\"\n part=\"value\"\n role=\"spinbutton\"\n aria-live=\"polite\"\n aria-labelledby=\"${labelId}\"\n aria-describedby=\"${describedBy}\"\n aria-valuenow=\"${() => String(current.value)}\"\n aria-valuemin=\"${() => (props.min.value != null ? String(props.min.value) : null)}\"\n aria-valuemax=\"${() => (props.max.value != null ? String(props.max.value) : null)}\"\n aria-readonly=\"${() => (isReadonly.value ? 'true' : null)}\"\n aria-disabled=\"${() => (isDisabled.value ? 'true' : null)}\"\n tabindex=\"${() => (isDisabled.value ? '-1' : '0')}\">\n ${() => String(current.value)}\n </output>\n <button\n class=\"btn\"\n part=\"increment-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Increase ${props.label.value ?? ''}`.trim()}\"\n ?hidden=\"${isReadonly}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMax()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(small(1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(small(1), e)}\">\n <ore-icon name=\"plus\" size=\"var(--_icon-size)\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n <button\n class=\"btn btn-large\"\n part=\"increment-large-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Increase ${props.label.value ?? ''} by ${largeStepSize()}`.trim()}\"\n ?hidden=\"${() => isReadonly.value || !hasQuickSteps.value}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMax()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(large(1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(large(1), e)}\">\n ${quickLabel(1)}\n </button>\n </div>\n <slot name=\"hint\">\n <span class=\"hint\" part=\"hint\" id=\"${hintId}\" ?hidden=\"${() => !hasHint.value}\">\n ${() => props.hint.value ?? ''}\n </span>\n </slot>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [\n colorThemeMixin,\n disabledStateMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n frostVariantMixin('.counter'),\n componentStyles,\n ],\n});\n"],"mappings":"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"}
|