@vygruppen/spor-react 10.2.0 → 10.3.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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @vygruppen/spor-react@10.2.0 build
2
+ > @vygruppen/spor-react@10.3.0 build
3
3
  > tsup src/index.tsx --dts --treeshake --format cjs,esm
4
4
 
5
5
  CLI Building entry: src/index.tsx
@@ -10,11 +10,11 @@
10
10
  ESM Build start
11
11
  DTS Build start
12
12
  ESM dist/index.mjs 2.15 KB
13
- ESM dist/CountryCodeSelect-UFSVRLDY.mjs 1.19 KB
14
- ESM dist/chunk-BPDFFQ3U.mjs 416.17 KB
15
- ESM ⚡️ Build success in 2287ms
16
- CJS dist/index.js 527.91 KB
17
- CJS ⚡️ Build success in 2288ms
18
- DTS ⚡️ Build success in 15575ms
19
- DTS dist/index.d.ts 315.68 KB
20
- DTS dist/index.d.mts 315.68 KB
13
+ ESM dist/CountryCodeSelect-KCPHU7A7.mjs 1.19 KB
14
+ ESM dist/chunk-5HRYDWQ5.mjs 417.51 KB
15
+ ESM ⚡️ Build success in 2131ms
16
+ CJS dist/index.js 529.34 KB
17
+ CJS ⚡️ Build success in 2132ms
18
+ DTS ⚡️ Build success in 15282ms
19
+ DTS dist/index.d.ts 315.84 KB
20
+ DTS dist/index.d.mts 315.84 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @vygruppen/spor-react
2
2
 
3
+ ## 10.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - d3cd54d: Added input field ariaLabelContext to NummericStepper to improve functionality with screen readers.
8
+
3
9
  ## 10.2.0
4
10
 
5
11
  ### Minor Changes
@@ -1,4 +1,4 @@
1
- import { createTexts, useTranslation, InfoSelect, Item } from './chunk-BPDFFQ3U.mjs';
1
+ import { createTexts, useTranslation, InfoSelect, Item } from './chunk-5HRYDWQ5.mjs';
2
2
  import React from 'react';
3
3
  import { getSupportedCallingCodes } from 'awesome-phonenumber';
4
4
 
@@ -1508,8 +1508,10 @@ function NumericStepper({
1508
1508
  withInput = true,
1509
1509
  stepSize = 1,
1510
1510
  showZero = false,
1511
+ ariaLabelContext = { singular: "", plural: "" },
1511
1512
  ...boxProps
1512
1513
  }) {
1514
+ const addButtonRef = useRef(null);
1513
1515
  const { t: t2 } = useTranslation();
1514
1516
  const styles3 = useMultiStyleConfig("NumericStepper", {});
1515
1517
  const [value, onChange] = useControllableState({
@@ -1519,12 +1521,26 @@ function NumericStepper({
1519
1521
  });
1520
1522
  const formControlProps = useFormControl({ id: idProp, isDisabled });
1521
1523
  const clampedStepSize = Math.max(Math.min(stepSize, 10), 1);
1524
+ const focusOnAddButton = () => {
1525
+ var _a6;
1526
+ (_a6 = addButtonRef.current) == null ? void 0 : _a6.focus();
1527
+ };
1522
1528
  return /* @__PURE__ */ React84__default.createElement(Flex, { __css: styles3.container, ...boxProps }, /* @__PURE__ */ React84__default.createElement(
1523
1529
  VerySmallButton,
1524
1530
  {
1525
1531
  icon: /* @__PURE__ */ React84__default.createElement(SubtractIcon, { stepLabel: clampedStepSize }),
1526
- "aria-label": t2(texts2.decrementButtonAriaLabel(clampedStepSize)),
1527
- onClick: () => onChange(Math.max(value - clampedStepSize, minValue)),
1532
+ "aria-label": t2(
1533
+ texts2.decrementButtonAriaLabel(
1534
+ clampedStepSize,
1535
+ stepSize == 1 ? ariaLabelContext.singular : ariaLabelContext.plural
1536
+ )
1537
+ ),
1538
+ onClick: () => {
1539
+ onChange(Math.max(value - clampedStepSize, minValue));
1540
+ if (Math.max(value - clampedStepSize, minValue) <= minValue) {
1541
+ focusOnAddButton();
1542
+ }
1543
+ },
1528
1544
  visibility: value <= minValue ? "hidden" : "visible",
1529
1545
  isDisabled: formControlProps.disabled,
1530
1546
  id: value <= minValue ? void 0 : formControlProps.id
@@ -1543,13 +1559,16 @@ function NumericStepper({
1543
1559
  width: `${Math.max(value.toString().length + 1, 3)}ch`,
1544
1560
  visibility: !showZero && value === 0 ? "hidden" : "visible",
1545
1561
  "aria-live": "assertive",
1546
- "aria-label": value.toString(),
1562
+ "aria-label": ariaLabelContext.plural !== "" ? t2(texts2.currentNumberAriaLabel(ariaLabelContext.plural)) : "",
1547
1563
  onChange: (e) => {
1548
1564
  const numericInput = Number(e.target.value);
1549
1565
  if (Number.isNaN(numericInput)) {
1550
1566
  return;
1551
1567
  }
1552
1568
  onChange(Math.max(Math.min(numericInput, maxValue), minValue));
1569
+ if (!showZero && Math.max(Math.min(numericInput, maxValue), minValue) === 0) {
1570
+ focusOnAddButton();
1571
+ }
1553
1572
  }
1554
1573
  }
1555
1574
  ) : /* @__PURE__ */ React84__default.createElement(
@@ -1557,14 +1576,21 @@ function NumericStepper({
1557
1576
  {
1558
1577
  sx: styles3.text,
1559
1578
  visibility: !showZero && value === 0 ? "hidden" : "visible",
1560
- "aria-label": value.toString()
1579
+ "aria-live": "assertive",
1580
+ "aria-label": ariaLabelContext.plural !== "" ? t2(texts2.currentNumberAriaLabel(ariaLabelContext.plural)) : ""
1561
1581
  },
1562
1582
  value
1563
1583
  ), /* @__PURE__ */ React84__default.createElement(
1564
1584
  VerySmallButton,
1565
1585
  {
1586
+ ref: addButtonRef,
1566
1587
  icon: /* @__PURE__ */ React84__default.createElement(AddIcon, { stepLabel: clampedStepSize }),
1567
- "aria-label": t2(texts2.incrementButtonAriaLabel(clampedStepSize)),
1588
+ "aria-label": t2(
1589
+ texts2.incrementButtonAriaLabel(
1590
+ clampedStepSize,
1591
+ stepSize == 1 ? ariaLabelContext.singular : ariaLabelContext.plural
1592
+ )
1593
+ ),
1568
1594
  onClick: () => onChange(Math.min(value + clampedStepSize, maxValue)),
1569
1595
  visibility: value >= maxValue ? "hidden" : "visible",
1570
1596
  isDisabled: formControlProps.disabled,
@@ -1572,10 +1598,19 @@ function NumericStepper({
1572
1598
  }
1573
1599
  ));
1574
1600
  }
1575
- var VerySmallButton = (props) => {
1601
+ var VerySmallButton = React84__default.forwardRef((props, ref) => {
1576
1602
  const styles3 = useMultiStyleConfig("NumericStepper", {});
1577
- return /* @__PURE__ */ React84__default.createElement(IconButton, { variant: "primary", size: "xs", sx: styles3.button, ...props });
1578
- };
1603
+ return /* @__PURE__ */ React84__default.createElement(
1604
+ IconButton,
1605
+ {
1606
+ variant: "primary",
1607
+ size: "xs",
1608
+ sx: styles3.button,
1609
+ ref,
1610
+ ...props
1611
+ }
1612
+ );
1613
+ });
1579
1614
  var SubtractIcon = ({ stepLabel, ...props }) => /* @__PURE__ */ React84__default.createElement(React84__default.Fragment, null, /* @__PURE__ */ React84__default.createElement(
1580
1615
  Box,
1581
1616
  {
@@ -1632,20 +1667,28 @@ var AddIcon = ({ stepLabel, ...props }) => /* @__PURE__ */ React84__default.crea
1632
1667
  )
1633
1668
  ), stepLabel > 1 && /* @__PURE__ */ React84__default.createElement(chakra.span, { paddingRight: "1" }, stepLabel.toString()));
1634
1669
  var texts2 = createTexts({
1635
- decrementButtonAriaLabel(stepSize) {
1670
+ currentNumberAriaLabel(ariaContext) {
1671
+ return {
1672
+ nb: `Valgt antall ${ariaContext}`,
1673
+ en: `Chosen number of ${ariaContext}`,
1674
+ nn: `Valgt antall ${ariaContext}`,
1675
+ sv: `Valgt antall ${ariaContext}`
1676
+ };
1677
+ },
1678
+ decrementButtonAriaLabel(stepSize, ariaContext) {
1636
1679
  return {
1637
- nb: `Trekk fra ${stepSize}`,
1638
- en: `Subtract ${stepSize}`,
1639
- nn: `Trekk fr\xE5 ${stepSize}`,
1640
- sv: `Subtrahera ${stepSize}`
1680
+ nb: `Trekk fra ${stepSize} ${ariaContext}`,
1681
+ en: `Subtract ${stepSize} ${ariaContext}`,
1682
+ nn: `Trekk fr\xE5 ${stepSize} ${ariaContext}`,
1683
+ sv: `Subtrahera ${stepSize} ${ariaContext}`
1641
1684
  };
1642
1685
  },
1643
- incrementButtonAriaLabel(stepSize) {
1686
+ incrementButtonAriaLabel(stepSize, ariaContext) {
1644
1687
  return {
1645
- nb: `Legg til ${stepSize}`,
1646
- en: `Add ${stepSize}`,
1647
- nn: `Legg til ${stepSize}`,
1648
- sv: `L\xE4gg till ${stepSize}`
1688
+ nb: `Legg til ${stepSize} ${ariaContext}`,
1689
+ en: `Add ${stepSize} ${ariaContext}`,
1690
+ nn: `Legg til ${stepSize} ${ariaContext}`,
1691
+ sv: `L\xE4gg till ${stepSize} ${ariaContext}`
1649
1692
  };
1650
1693
  }
1651
1694
  });
@@ -1779,7 +1822,7 @@ var texts4 = createTexts({
1779
1822
  sv: "Landskod"
1780
1823
  }
1781
1824
  });
1782
- var LazyCountryCodeSelect = React84__default.lazy(() => import('./CountryCodeSelect-UFSVRLDY.mjs'));
1825
+ var LazyCountryCodeSelect = React84__default.lazy(() => import('./CountryCodeSelect-KCPHU7A7.mjs'));
1783
1826
  var Radio = forwardRef((props, ref) => {
1784
1827
  return /* @__PURE__ */ React84__default.createElement(Radio$1, { ...props, ref });
1785
1828
  });
package/dist/index.d.mts CHANGED
@@ -1270,6 +1270,11 @@ type NumericStepperProps = {
1270
1270
  stepSize?: number;
1271
1271
  /** Whether to show the number input when value is zero */
1272
1272
  showZero?: boolean;
1273
+ /** Name added to the aria-label of subtract and add buttons. */
1274
+ ariaLabelContext?: {
1275
+ singular: string;
1276
+ plural: string;
1277
+ };
1273
1278
  } & Omit<BoxProps, "onChange">;
1274
1279
  /** A simple stepper component for integer values
1275
1280
  *
@@ -1295,7 +1300,7 @@ type NumericStepperProps = {
1295
1300
  * </FormControl>
1296
1301
  * ```
1297
1302
  */
1298
- declare function NumericStepper({ name: nameProp, id: idProp, value: valueProp, defaultValue, onChange: onChangeProp, minValue, maxValue, isDisabled, withInput, stepSize, showZero, ...boxProps }: NumericStepperProps): React.JSX.Element;
1303
+ declare function NumericStepper({ name: nameProp, id: idProp, value: valueProp, defaultValue, onChange: onChangeProp, minValue, maxValue, isDisabled, withInput, stepSize, showZero, ariaLabelContext, ...boxProps }: NumericStepperProps): React.JSX.Element;
1299
1304
 
1300
1305
  type PasswordInputProps = InputProps;
1301
1306
  declare const PasswordInput: _chakra_ui_system_dist_system_types.ComponentWithAs<"input", InputProps>;
package/dist/index.d.ts CHANGED
@@ -1270,6 +1270,11 @@ type NumericStepperProps = {
1270
1270
  stepSize?: number;
1271
1271
  /** Whether to show the number input when value is zero */
1272
1272
  showZero?: boolean;
1273
+ /** Name added to the aria-label of subtract and add buttons. */
1274
+ ariaLabelContext?: {
1275
+ singular: string;
1276
+ plural: string;
1277
+ };
1273
1278
  } & Omit<BoxProps, "onChange">;
1274
1279
  /** A simple stepper component for integer values
1275
1280
  *
@@ -1295,7 +1300,7 @@ type NumericStepperProps = {
1295
1300
  * </FormControl>
1296
1301
  * ```
1297
1302
  */
1298
- declare function NumericStepper({ name: nameProp, id: idProp, value: valueProp, defaultValue, onChange: onChangeProp, minValue, maxValue, isDisabled, withInput, stepSize, showZero, ...boxProps }: NumericStepperProps): React.JSX.Element;
1303
+ declare function NumericStepper({ name: nameProp, id: idProp, value: valueProp, defaultValue, onChange: onChangeProp, minValue, maxValue, isDisabled, withInput, stepSize, showZero, ariaLabelContext, ...boxProps }: NumericStepperProps): React.JSX.Element;
1299
1304
 
1300
1305
  type PasswordInputProps = InputProps;
1301
1306
  declare const PasswordInput: _chakra_ui_system_dist_system_types.ComponentWithAs<"input", InputProps>;
package/dist/index.js CHANGED
@@ -1654,8 +1654,10 @@ function NumericStepper({
1654
1654
  withInput = true,
1655
1655
  stepSize = 1,
1656
1656
  showZero = false,
1657
+ ariaLabelContext = { singular: "", plural: "" },
1657
1658
  ...boxProps
1658
1659
  }) {
1660
+ const addButtonRef = React85.useRef(null);
1659
1661
  const { t: t2 } = useTranslation();
1660
1662
  const styles3 = react.useMultiStyleConfig("NumericStepper", {});
1661
1663
  const [value, onChange] = react.useControllableState({
@@ -1665,12 +1667,26 @@ function NumericStepper({
1665
1667
  });
1666
1668
  const formControlProps = react.useFormControl({ id: idProp, isDisabled });
1667
1669
  const clampedStepSize = Math.max(Math.min(stepSize, 10), 1);
1670
+ const focusOnAddButton = () => {
1671
+ var _a6;
1672
+ (_a6 = addButtonRef.current) == null ? void 0 : _a6.focus();
1673
+ };
1668
1674
  return /* @__PURE__ */ React85__namespace.default.createElement(react.Flex, { __css: styles3.container, ...boxProps }, /* @__PURE__ */ React85__namespace.default.createElement(
1669
1675
  VerySmallButton,
1670
1676
  {
1671
1677
  icon: /* @__PURE__ */ React85__namespace.default.createElement(SubtractIcon, { stepLabel: clampedStepSize }),
1672
- "aria-label": t2(texts2.decrementButtonAriaLabel(clampedStepSize)),
1673
- onClick: () => onChange(Math.max(value - clampedStepSize, minValue)),
1678
+ "aria-label": t2(
1679
+ texts2.decrementButtonAriaLabel(
1680
+ clampedStepSize,
1681
+ stepSize == 1 ? ariaLabelContext.singular : ariaLabelContext.plural
1682
+ )
1683
+ ),
1684
+ onClick: () => {
1685
+ onChange(Math.max(value - clampedStepSize, minValue));
1686
+ if (Math.max(value - clampedStepSize, minValue) <= minValue) {
1687
+ focusOnAddButton();
1688
+ }
1689
+ },
1674
1690
  visibility: value <= minValue ? "hidden" : "visible",
1675
1691
  isDisabled: formControlProps.disabled,
1676
1692
  id: value <= minValue ? void 0 : formControlProps.id
@@ -1689,13 +1705,16 @@ function NumericStepper({
1689
1705
  width: `${Math.max(value.toString().length + 1, 3)}ch`,
1690
1706
  visibility: !showZero && value === 0 ? "hidden" : "visible",
1691
1707
  "aria-live": "assertive",
1692
- "aria-label": value.toString(),
1708
+ "aria-label": ariaLabelContext.plural !== "" ? t2(texts2.currentNumberAriaLabel(ariaLabelContext.plural)) : "",
1693
1709
  onChange: (e) => {
1694
1710
  const numericInput = Number(e.target.value);
1695
1711
  if (Number.isNaN(numericInput)) {
1696
1712
  return;
1697
1713
  }
1698
1714
  onChange(Math.max(Math.min(numericInput, maxValue), minValue));
1715
+ if (!showZero && Math.max(Math.min(numericInput, maxValue), minValue) === 0) {
1716
+ focusOnAddButton();
1717
+ }
1699
1718
  }
1700
1719
  }
1701
1720
  ) : /* @__PURE__ */ React85__namespace.default.createElement(
@@ -1703,14 +1722,21 @@ function NumericStepper({
1703
1722
  {
1704
1723
  sx: styles3.text,
1705
1724
  visibility: !showZero && value === 0 ? "hidden" : "visible",
1706
- "aria-label": value.toString()
1725
+ "aria-live": "assertive",
1726
+ "aria-label": ariaLabelContext.plural !== "" ? t2(texts2.currentNumberAriaLabel(ariaLabelContext.plural)) : ""
1707
1727
  },
1708
1728
  value
1709
1729
  ), /* @__PURE__ */ React85__namespace.default.createElement(
1710
1730
  VerySmallButton,
1711
1731
  {
1732
+ ref: addButtonRef,
1712
1733
  icon: /* @__PURE__ */ React85__namespace.default.createElement(AddIcon, { stepLabel: clampedStepSize }),
1713
- "aria-label": t2(texts2.incrementButtonAriaLabel(clampedStepSize)),
1734
+ "aria-label": t2(
1735
+ texts2.incrementButtonAriaLabel(
1736
+ clampedStepSize,
1737
+ stepSize == 1 ? ariaLabelContext.singular : ariaLabelContext.plural
1738
+ )
1739
+ ),
1714
1740
  onClick: () => onChange(Math.min(value + clampedStepSize, maxValue)),
1715
1741
  visibility: value >= maxValue ? "hidden" : "visible",
1716
1742
  isDisabled: formControlProps.disabled,
@@ -1722,10 +1748,19 @@ var VerySmallButton, SubtractIcon, AddIcon, texts2;
1722
1748
  var init_NumericStepper = __esm({
1723
1749
  "src/input/NumericStepper.tsx"() {
1724
1750
  init_src();
1725
- VerySmallButton = (props) => {
1751
+ VerySmallButton = React85__namespace.default.forwardRef((props, ref) => {
1726
1752
  const styles3 = react.useMultiStyleConfig("NumericStepper", {});
1727
- return /* @__PURE__ */ React85__namespace.default.createElement(exports.IconButton, { variant: "primary", size: "xs", sx: styles3.button, ...props });
1728
- };
1753
+ return /* @__PURE__ */ React85__namespace.default.createElement(
1754
+ exports.IconButton,
1755
+ {
1756
+ variant: "primary",
1757
+ size: "xs",
1758
+ sx: styles3.button,
1759
+ ref,
1760
+ ...props
1761
+ }
1762
+ );
1763
+ });
1729
1764
  SubtractIcon = ({ stepLabel, ...props }) => /* @__PURE__ */ React85__namespace.default.createElement(React85__namespace.default.Fragment, null, /* @__PURE__ */ React85__namespace.default.createElement(
1730
1765
  react.Box,
1731
1766
  {
@@ -1782,20 +1817,28 @@ var init_NumericStepper = __esm({
1782
1817
  )
1783
1818
  ), stepLabel > 1 && /* @__PURE__ */ React85__namespace.default.createElement(react.chakra.span, { paddingRight: "1" }, stepLabel.toString()));
1784
1819
  texts2 = createTexts({
1785
- decrementButtonAriaLabel(stepSize) {
1820
+ currentNumberAriaLabel(ariaContext) {
1821
+ return {
1822
+ nb: `Valgt antall ${ariaContext}`,
1823
+ en: `Chosen number of ${ariaContext}`,
1824
+ nn: `Valgt antall ${ariaContext}`,
1825
+ sv: `Valgt antall ${ariaContext}`
1826
+ };
1827
+ },
1828
+ decrementButtonAriaLabel(stepSize, ariaContext) {
1786
1829
  return {
1787
- nb: `Trekk fra ${stepSize}`,
1788
- en: `Subtract ${stepSize}`,
1789
- nn: `Trekk fr\xE5 ${stepSize}`,
1790
- sv: `Subtrahera ${stepSize}`
1830
+ nb: `Trekk fra ${stepSize} ${ariaContext}`,
1831
+ en: `Subtract ${stepSize} ${ariaContext}`,
1832
+ nn: `Trekk fr\xE5 ${stepSize} ${ariaContext}`,
1833
+ sv: `Subtrahera ${stepSize} ${ariaContext}`
1791
1834
  };
1792
1835
  },
1793
- incrementButtonAriaLabel(stepSize) {
1836
+ incrementButtonAriaLabel(stepSize, ariaContext) {
1794
1837
  return {
1795
- nb: `Legg til ${stepSize}`,
1796
- en: `Add ${stepSize}`,
1797
- nn: `Legg til ${stepSize}`,
1798
- sv: `L\xE4gg till ${stepSize}`
1838
+ nb: `Legg til ${stepSize} ${ariaContext}`,
1839
+ en: `Add ${stepSize} ${ariaContext}`,
1840
+ nn: `Legg til ${stepSize} ${ariaContext}`,
1841
+ sv: `L\xE4gg till ${stepSize} ${ariaContext}`
1799
1842
  };
1800
1843
  }
1801
1844
  });
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- export { Accordion, AccordionButton, AccordionIcon, AccordionItem, AccordionPanel, AttachedInputs, Badge, Box, Brand, Breadcrumb, BreadcrumbItem, BreadcrumbLink, Button, ButtonGroup, CardSelect, CargonetLogo, Center, Checkbox, CheckboxGroup, ChoiceChip, ClosableAlert, CloseButton, Code, Collapse, ColorInlineLoader, ColorSpinner, Combobox, Container, ContentLoader, DarkFullScreenLoader, DarkInlineLoader, DarkMode, DarkSpinner, DatePicker, DateRangePicker, Divider, Drawer, DrawerBody, DrawerCloseButton, DrawerContent, DrawerFooter, ModalHeader as DrawerHeader, DrawerOverlay, Expandable, ExpandableAlert, ExpandableItem, Fade, Flex, FloatingActionButton, FormControl, FormErrorMessage, FormHelperText, FormLabel, FullScreenDrawer, Grid, GridItem, HStack, Heading, IconButton, Image, Img, InfoSelect, InfoTag, Input, InputGroup, InputLeftElement, InputRightElement, Item, ItemDescription, ItemLabel, JumpButton, Language, LanguageProvider, LightFullScreenLoader, LightInlineLoader, LightMode, LightSpinner, LineIcon, ListBox, ListItem, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, NativeSelect, Nudge, NumericStepper, OrderedList, Pagination, PasswordInput, PhoneNumberInput, PlayPauseButton, Portal, PressableCard, ProgressBar, ProgressIndicator, ProgressLoader, Radio, RadioCard, RadioCardGroup, RadioCardGroupContext, RadioGroup, ScaleFade, SearchInput, Section, SimpleDrawer, SimpleGrid, Skeleton, SkeletonCircle, SkeletonText, SkipButton, Slide, SlideFade, Spacer, SporProvider, Stack, StaticAlert, StaticCard, Stepper, StepperStep, Switch, Tab, TabList, TabPanel, TabPanels, Table, TableCaption, Tabs, Tbody, Td, Text, TextLink, Textarea, Tfoot, Th, Thead, Time, TimePicker, Tooltip, Tr, TravelTag, UnorderedList, VStack, VyLogo, VyLogoPride, WizardNudge, Wrap, WrapItem, brandTheme, createTexts, defineStyleConfig, extendTheme, fontFaces, slugify, theme, tokens, useBreakpointValue, useClipboard, useColorMode, useColorModePreference, useColorModeValue, useControllableProp, useDisclosure, useMediaQuery, useMergeRefs, useOutsideClick, usePrefersReducedMotion, useSize, useTheme, useToast, useToken, useTranslation } from './chunk-BPDFFQ3U.mjs';
1
+ export { Accordion, AccordionButton, AccordionIcon, AccordionItem, AccordionPanel, AttachedInputs, Badge, Box, Brand, Breadcrumb, BreadcrumbItem, BreadcrumbLink, Button, ButtonGroup, CardSelect, CargonetLogo, Center, Checkbox, CheckboxGroup, ChoiceChip, ClosableAlert, CloseButton, Code, Collapse, ColorInlineLoader, ColorSpinner, Combobox, Container, ContentLoader, DarkFullScreenLoader, DarkInlineLoader, DarkMode, DarkSpinner, DatePicker, DateRangePicker, Divider, Drawer, DrawerBody, DrawerCloseButton, DrawerContent, DrawerFooter, ModalHeader as DrawerHeader, DrawerOverlay, Expandable, ExpandableAlert, ExpandableItem, Fade, Flex, FloatingActionButton, FormControl, FormErrorMessage, FormHelperText, FormLabel, FullScreenDrawer, Grid, GridItem, HStack, Heading, IconButton, Image, Img, InfoSelect, InfoTag, Input, InputGroup, InputLeftElement, InputRightElement, Item, ItemDescription, ItemLabel, JumpButton, Language, LanguageProvider, LightFullScreenLoader, LightInlineLoader, LightMode, LightSpinner, LineIcon, ListBox, ListItem, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, NativeSelect, Nudge, NumericStepper, OrderedList, Pagination, PasswordInput, PhoneNumberInput, PlayPauseButton, Portal, PressableCard, ProgressBar, ProgressIndicator, ProgressLoader, Radio, RadioCard, RadioCardGroup, RadioCardGroupContext, RadioGroup, ScaleFade, SearchInput, Section, SimpleDrawer, SimpleGrid, Skeleton, SkeletonCircle, SkeletonText, SkipButton, Slide, SlideFade, Spacer, SporProvider, Stack, StaticAlert, StaticCard, Stepper, StepperStep, Switch, Tab, TabList, TabPanel, TabPanels, Table, TableCaption, Tabs, Tbody, Td, Text, TextLink, Textarea, Tfoot, Th, Thead, Time, TimePicker, Tooltip, Tr, TravelTag, UnorderedList, VStack, VyLogo, VyLogoPride, WizardNudge, Wrap, WrapItem, brandTheme, createTexts, defineStyleConfig, extendTheme, fontFaces, slugify, theme, tokens, useBreakpointValue, useClipboard, useColorMode, useColorModePreference, useColorModeValue, useControllableProp, useDisclosure, useMediaQuery, useMergeRefs, useOutsideClick, usePrefersReducedMotion, useSize, useTheme, useToast, useToken, useTranslation } from './chunk-5HRYDWQ5.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vygruppen/spor-react",
3
- "version": "10.2.0",
3
+ "version": "10.3.0",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
@@ -4,7 +4,7 @@ import {
4
4
  useFormControl,
5
5
  useMultiStyleConfig,
6
6
  } from "@chakra-ui/react";
7
- import React from "react";
7
+ import React, { useRef } from "react";
8
8
  import {
9
9
  Box,
10
10
  BoxProps,
@@ -35,6 +35,8 @@ type NumericStepperProps = {
35
35
  stepSize?: number;
36
36
  /** Whether to show the number input when value is zero */
37
37
  showZero?: boolean;
38
+ /** Name added to the aria-label of subtract and add buttons. */
39
+ ariaLabelContext?: { singular: string; plural: string };
38
40
  } & Omit<BoxProps, "onChange">;
39
41
  /** A simple stepper component for integer values
40
42
  *
@@ -72,8 +74,10 @@ export function NumericStepper({
72
74
  withInput = true,
73
75
  stepSize = 1,
74
76
  showZero = false,
77
+ ariaLabelContext = { singular: "", plural: "" },
75
78
  ...boxProps
76
79
  }: NumericStepperProps) {
80
+ const addButtonRef = useRef<HTMLButtonElement>(null);
77
81
  const { t } = useTranslation();
78
82
  const styles = useMultiStyleConfig("NumericStepper", {});
79
83
  const [value, onChange] = useControllableState<number>({
@@ -84,12 +88,26 @@ export function NumericStepper({
84
88
  const formControlProps = useFormControl({ id: idProp, isDisabled });
85
89
  const clampedStepSize = Math.max(Math.min(stepSize, 10), 1);
86
90
 
91
+ const focusOnAddButton = () => {
92
+ addButtonRef.current?.focus();
93
+ };
94
+
87
95
  return (
88
96
  <Flex __css={styles.container} {...boxProps}>
89
97
  <VerySmallButton
90
98
  icon={<SubtractIcon stepLabel={clampedStepSize} />}
91
- aria-label={t(texts.decrementButtonAriaLabel(clampedStepSize))}
92
- onClick={() => onChange(Math.max(value - clampedStepSize, minValue))}
99
+ aria-label={t(
100
+ texts.decrementButtonAriaLabel(
101
+ clampedStepSize,
102
+ stepSize == 1 ? ariaLabelContext.singular : ariaLabelContext.plural,
103
+ ),
104
+ )}
105
+ onClick={() => {
106
+ onChange(Math.max(value - clampedStepSize, minValue));
107
+ if (Math.max(value - clampedStepSize, minValue) <= minValue) {
108
+ focusOnAddButton();
109
+ }
110
+ }}
93
111
  visibility={value <= minValue ? "hidden" : "visible"}
94
112
  isDisabled={formControlProps.disabled}
95
113
  id={value <= minValue ? undefined : formControlProps.id}
@@ -107,27 +125,48 @@ export function NumericStepper({
107
125
  width={`${Math.max(value.toString().length + 1, 3)}ch`}
108
126
  visibility={!showZero && value === 0 ? "hidden" : "visible"}
109
127
  aria-live="assertive"
110
- aria-label={value.toString()}
128
+ aria-label={
129
+ ariaLabelContext.plural !== ""
130
+ ? t(texts.currentNumberAriaLabel(ariaLabelContext.plural))
131
+ : ""
132
+ }
111
133
  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
112
134
  const numericInput = Number(e.target.value);
113
135
  if (Number.isNaN(numericInput)) {
114
136
  return;
115
137
  }
116
138
  onChange(Math.max(Math.min(numericInput, maxValue), minValue));
139
+ if (
140
+ !showZero &&
141
+ Math.max(Math.min(numericInput, maxValue), minValue) === 0
142
+ ) {
143
+ focusOnAddButton();
144
+ }
117
145
  }}
118
146
  />
119
147
  ) : (
120
148
  <chakra.text
121
149
  sx={styles.text}
122
150
  visibility={!showZero && value === 0 ? "hidden" : "visible"}
123
- aria-label={value.toString()}
151
+ aria-live="assertive"
152
+ aria-label={
153
+ ariaLabelContext.plural !== ""
154
+ ? t(texts.currentNumberAriaLabel(ariaLabelContext.plural))
155
+ : ""
156
+ }
124
157
  >
125
158
  {value}
126
159
  </chakra.text>
127
160
  )}
128
161
  <VerySmallButton
162
+ ref={addButtonRef}
129
163
  icon={<AddIcon stepLabel={clampedStepSize} />}
130
- aria-label={t(texts.incrementButtonAriaLabel(clampedStepSize))}
164
+ aria-label={t(
165
+ texts.incrementButtonAriaLabel(
166
+ clampedStepSize,
167
+ stepSize == 1 ? ariaLabelContext.singular : ariaLabelContext.plural,
168
+ ),
169
+ )}
131
170
  onClick={() => onChange(Math.min(value + clampedStepSize, maxValue))}
132
171
  visibility={value >= maxValue ? "hidden" : "visible"}
133
172
  isDisabled={formControlProps.disabled}
@@ -152,12 +191,18 @@ type VerySmallButtonProps = {
152
191
  id?: string;
153
192
  };
154
193
  /** Internal override for extra small icon buttons */
155
- const VerySmallButton = (props: VerySmallButtonProps) => {
194
+ const VerySmallButton = React.forwardRef((props: VerySmallButtonProps, ref) => {
156
195
  const styles = useMultiStyleConfig("NumericStepper", {});
157
196
  return (
158
- <IconButton variant="primary" size="xs" sx={styles.button} {...props} />
197
+ <IconButton
198
+ variant="primary"
199
+ size="xs"
200
+ sx={styles.button}
201
+ ref={ref}
202
+ {...props}
203
+ />
159
204
  );
160
- };
205
+ });
161
206
 
162
207
  type IconPropTypes = BoxProps & { stepLabel: number };
163
208
 
@@ -221,20 +266,28 @@ const AddIcon = ({ stepLabel, ...props }: IconPropTypes) => (
221
266
  );
222
267
 
223
268
  const texts = createTexts({
224
- decrementButtonAriaLabel(stepSize) {
269
+ currentNumberAriaLabel(ariaContext) {
270
+ return {
271
+ nb: `Valgt antall ${ariaContext}`,
272
+ en: `Chosen number of ${ariaContext}`,
273
+ nn: `Valgt antall ${ariaContext}`,
274
+ sv: `Valgt antall ${ariaContext}`,
275
+ };
276
+ },
277
+ decrementButtonAriaLabel(stepSize, ariaContext) {
225
278
  return {
226
- nb: `Trekk fra ${stepSize}`,
227
- en: `Subtract ${stepSize}`,
228
- nn: `Trekk frå ${stepSize}`,
229
- sv: `Subtrahera ${stepSize}`,
279
+ nb: `Trekk fra ${stepSize} ${ariaContext}`,
280
+ en: `Subtract ${stepSize} ${ariaContext}`,
281
+ nn: `Trekk frå ${stepSize} ${ariaContext}`,
282
+ sv: `Subtrahera ${stepSize} ${ariaContext}`,
230
283
  };
231
284
  },
232
- incrementButtonAriaLabel(stepSize) {
285
+ incrementButtonAriaLabel(stepSize, ariaContext) {
233
286
  return {
234
- nb: `Legg til ${stepSize}`,
235
- en: `Add ${stepSize}`,
236
- nn: `Legg til ${stepSize}`,
237
- sv: `Lägg till ${stepSize}`,
287
+ nb: `Legg til ${stepSize} ${ariaContext}`,
288
+ en: `Add ${stepSize} ${ariaContext}`,
289
+ nn: `Legg til ${stepSize} ${ariaContext}`,
290
+ sv: `Lägg till ${stepSize} ${ariaContext}`,
238
291
  };
239
292
  },
240
293
  });