@telus-uds/components-web 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +2 -0
- package/.vscode/settings.json +7 -0
- package/CHANGELOG.md +39 -2
- package/lib/Autocomplete/Autocomplete.js +393 -0
- package/lib/Autocomplete/Loading.js +51 -0
- package/lib/Autocomplete/Suggestions.js +81 -0
- package/lib/Autocomplete/constants.js +19 -0
- package/lib/Autocomplete/dictionary.js +19 -0
- package/lib/Autocomplete/index.js +13 -0
- package/lib/BlockQuote/BlockQuote.js +173 -0
- package/lib/BlockQuote/index.js +13 -0
- package/lib/Callout/Callout.js +3 -0
- package/lib/Card/Card.js +180 -0
- package/lib/Card/CardContent.js +110 -0
- package/lib/Card/CardFooter.js +98 -0
- package/lib/Card/index.js +13 -0
- package/lib/Countdown/Countdown.js +189 -0
- package/lib/Countdown/Segment.js +111 -0
- package/lib/Countdown/constants.js +14 -0
- package/lib/Countdown/dictionary.js +29 -0
- package/lib/Countdown/index.js +13 -0
- package/lib/Countdown/types.js +39 -0
- package/lib/Countdown/useCountdown.js +40 -0
- package/lib/IconButton/IconButton.js +70 -0
- package/lib/IconButton/index.js +13 -0
- package/lib/Listbox/GroupControl.js +94 -0
- package/lib/Listbox/Listbox.js +164 -0
- package/lib/Listbox/ListboxGroup.js +129 -0
- package/lib/Listbox/ListboxItem.js +137 -0
- package/lib/Listbox/ListboxOverlay.js +89 -0
- package/lib/Listbox/PressableItem.js +149 -0
- package/lib/Listbox/index.js +13 -0
- package/lib/Modal/ModalContent.js +11 -4
- package/lib/NavigationBar/resolveItemSelection.js +24 -0
- package/lib/OptimizeImage/OptimizeImage.js +127 -0
- package/lib/OptimizeImage/index.js +13 -0
- package/lib/OptimizeImage/utils/getFallbackUrl.js +18 -0
- package/lib/OptimizeImage/utils/getOptimizedUrl.js +32 -0
- package/lib/OptimizeImage/utils/hasWebpSupport.js +38 -0
- package/lib/OptimizeImage/utils/index.js +31 -0
- package/lib/OptimizeImage/utils/isSvgUrl.js +10 -0
- package/lib/QuantitySelector/QuantitySelector.js +253 -0
- package/lib/QuantitySelector/dictionary.js +33 -0
- package/lib/QuantitySelector/index.js +13 -0
- package/lib/QuantitySelector/styles.js +40 -0
- package/lib/StoryCard/StoryCard.js +244 -0
- package/lib/StoryCard/index.js +13 -0
- package/lib/TermsAndConditions/ExpandCollapse.js +141 -0
- package/lib/TermsAndConditions/TermsAndConditions.js +221 -0
- package/lib/TermsAndConditions/dictionary.js +19 -0
- package/lib/TermsAndConditions/index.js +15 -0
- package/lib/Testimonial/Testimonial.js +226 -0
- package/lib/Testimonial/index.js +13 -0
- package/lib/Video/ControlBar/ControlBar.js +315 -0
- package/lib/Video/ControlBar/Controls/VideoButton/VideoButton.js +91 -0
- package/lib/Video/ControlBar/Controls/VideoMenu/VideoMenu.js +186 -0
- package/lib/Video/ControlBar/Controls/VideoProgressBar/VideoProgressBar.js +221 -0
- package/lib/Video/ControlBar/Controls/VolumeSlider/VolumeSlider.js +213 -0
- package/lib/Video/MiddleControlButton/MiddleControlButton.js +89 -0
- package/lib/Video/Video.js +1072 -0
- package/lib/Video/index.js +13 -0
- package/lib/Video/videoText.js +62 -0
- package/lib/WebVideo/WebVideo.js +170 -0
- package/lib/WebVideo/index.js +13 -0
- package/lib/baseExports.js +0 -12
- package/lib/index.js +118 -1
- package/lib/shared/VideoSplash/SplashButton/SplashButton.js +102 -0
- package/lib/shared/VideoSplash/SplashButtonWithDetails/SplashButtonWithDetails.js +234 -0
- package/lib/shared/VideoSplash/VideoSplash.js +86 -0
- package/lib/shared/VideoSplash/helpers.js +38 -0
- package/lib/utils/index.js +8 -0
- package/lib/utils/useOverlaidPosition.js +246 -0
- package/lib-module/Autocomplete/Autocomplete.js +369 -0
- package/lib-module/Autocomplete/Loading.js +38 -0
- package/lib-module/Autocomplete/Suggestions.js +64 -0
- package/lib-module/Autocomplete/constants.js +5 -0
- package/lib-module/Autocomplete/dictionary.js +12 -0
- package/lib-module/Autocomplete/index.js +2 -0
- package/lib-module/BlockQuote/BlockQuote.js +156 -0
- package/lib-module/BlockQuote/index.js +2 -0
- package/lib-module/Callout/Callout.js +3 -0
- package/lib-module/Card/Card.js +158 -0
- package/lib-module/Card/CardContent.js +92 -0
- package/lib-module/Card/CardFooter.js +80 -0
- package/lib-module/Card/index.js +2 -0
- package/lib-module/Countdown/Countdown.js +165 -0
- package/lib-module/Countdown/Segment.js +94 -0
- package/lib-module/Countdown/constants.js +4 -0
- package/lib-module/Countdown/dictionary.js +22 -0
- package/lib-module/Countdown/index.js +2 -0
- package/lib-module/Countdown/types.js +23 -0
- package/lib-module/Countdown/useCountdown.js +32 -0
- package/lib-module/IconButton/IconButton.js +52 -0
- package/lib-module/IconButton/index.js +2 -0
- package/lib-module/Listbox/GroupControl.js +80 -0
- package/lib-module/Listbox/Listbox.js +142 -0
- package/lib-module/Listbox/ListboxGroup.js +106 -0
- package/lib-module/Listbox/ListboxItem.js +112 -0
- package/lib-module/Listbox/ListboxOverlay.js +68 -0
- package/lib-module/Listbox/PressableItem.js +128 -0
- package/lib-module/Listbox/index.js +2 -0
- package/lib-module/Modal/ModalContent.js +10 -4
- package/lib-module/NavigationBar/resolveItemSelection.js +16 -0
- package/lib-module/OptimizeImage/OptimizeImage.js +106 -0
- package/lib-module/OptimizeImage/index.js +2 -0
- package/lib-module/OptimizeImage/utils/getFallbackUrl.js +8 -0
- package/lib-module/OptimizeImage/utils/getOptimizedUrl.js +22 -0
- package/lib-module/OptimizeImage/utils/hasWebpSupport.js +32 -0
- package/lib-module/OptimizeImage/utils/index.js +4 -0
- package/lib-module/OptimizeImage/utils/isSvgUrl.js +3 -0
- package/lib-module/QuantitySelector/QuantitySelector.js +232 -0
- package/lib-module/QuantitySelector/dictionary.js +26 -0
- package/lib-module/QuantitySelector/index.js +2 -0
- package/lib-module/QuantitySelector/styles.js +21 -0
- package/lib-module/StoryCard/StoryCard.js +220 -0
- package/lib-module/StoryCard/index.js +2 -0
- package/lib-module/TermsAndConditions/ExpandCollapse.js +120 -0
- package/lib-module/TermsAndConditions/TermsAndConditions.js +193 -0
- package/lib-module/TermsAndConditions/dictionary.js +12 -0
- package/lib-module/TermsAndConditions/index.js +1 -0
- package/lib-module/Testimonial/Testimonial.js +204 -0
- package/lib-module/Testimonial/index.js +2 -0
- package/lib-module/Video/ControlBar/ControlBar.js +292 -0
- package/lib-module/Video/ControlBar/Controls/VideoButton/VideoButton.js +74 -0
- package/lib-module/Video/ControlBar/Controls/VideoMenu/VideoMenu.js +167 -0
- package/lib-module/Video/ControlBar/Controls/VideoProgressBar/VideoProgressBar.js +201 -0
- package/lib-module/Video/ControlBar/Controls/VolumeSlider/VolumeSlider.js +193 -0
- package/lib-module/Video/MiddleControlButton/MiddleControlButton.js +72 -0
- package/lib-module/Video/Video.js +1042 -0
- package/lib-module/Video/index.js +2 -0
- package/lib-module/Video/videoText.js +55 -0
- package/lib-module/WebVideo/WebVideo.js +144 -0
- package/lib-module/WebVideo/index.js +2 -0
- package/lib-module/baseExports.js +1 -1
- package/lib-module/index.js +13 -0
- package/lib-module/shared/VideoSplash/SplashButton/SplashButton.js +85 -0
- package/lib-module/shared/VideoSplash/SplashButtonWithDetails/SplashButtonWithDetails.js +216 -0
- package/lib-module/shared/VideoSplash/VideoSplash.js +65 -0
- package/lib-module/shared/VideoSplash/helpers.js +23 -0
- package/lib-module/utils/index.js +2 -1
- package/lib-module/utils/useOverlaidPosition.js +235 -0
- package/package.json +7 -5
- package/src/Autocomplete/Autocomplete.jsx +354 -0
- package/src/Autocomplete/Loading.jsx +18 -0
- package/src/Autocomplete/Suggestions.jsx +52 -0
- package/src/Autocomplete/constants.js +6 -0
- package/src/Autocomplete/dictionary.js +12 -0
- package/src/Autocomplete/index.js +3 -0
- package/src/BlockQuote/BlockQuote.jsx +130 -0
- package/src/BlockQuote/index.js +3 -0
- package/src/Callout/Callout.jsx +1 -1
- package/src/Card/Card.jsx +170 -0
- package/src/Card/CardContent.jsx +88 -0
- package/src/Card/CardFooter.jsx +70 -0
- package/src/Card/index.js +3 -0
- package/src/Countdown/Countdown.jsx +144 -0
- package/src/Countdown/Segment.jsx +69 -0
- package/src/Countdown/constants.js +4 -0
- package/src/Countdown/dictionary.js +22 -0
- package/src/Countdown/index.js +3 -0
- package/src/Countdown/types.js +23 -0
- package/src/Countdown/useCountdown.js +34 -0
- package/src/IconButton/IconButton.jsx +46 -0
- package/src/IconButton/index.js +3 -0
- package/src/Listbox/GroupControl.jsx +65 -0
- package/src/Listbox/Listbox.jsx +148 -0
- package/src/Listbox/ListboxGroup.jsx +110 -0
- package/src/Listbox/ListboxItem.jsx +101 -0
- package/src/Listbox/ListboxOverlay.jsx +71 -0
- package/src/Listbox/PressableItem.jsx +121 -0
- package/src/Listbox/index.js +3 -0
- package/src/Modal/ModalContent.jsx +8 -4
- package/src/NavigationBar/resolveItemSelection.js +11 -0
- package/src/OptimizeImage/OptimizeImage.jsx +131 -0
- package/src/OptimizeImage/index.js +3 -0
- package/src/OptimizeImage/utils/getFallbackUrl.js +9 -0
- package/src/OptimizeImage/utils/getOptimizedUrl.js +30 -0
- package/src/OptimizeImage/utils/hasWebpSupport.js +33 -0
- package/src/OptimizeImage/utils/index.js +5 -0
- package/src/OptimizeImage/utils/isSvgUrl.js +3 -0
- package/src/QuantitySelector/QuantitySelector.jsx +245 -0
- package/src/QuantitySelector/dictionary.js +27 -0
- package/src/QuantitySelector/index.js +3 -0
- package/src/QuantitySelector/styles.js +83 -0
- package/src/StoryCard/StoryCard.jsx +198 -0
- package/src/StoryCard/index.js +3 -0
- package/src/TermsAndConditions/ExpandCollapse.jsx +106 -0
- package/src/TermsAndConditions/TermsAndConditions.jsx +161 -0
- package/src/TermsAndConditions/dictionary.js +12 -0
- package/src/TermsAndConditions/index.js +1 -0
- package/src/Testimonial/Testimonial.jsx +169 -0
- package/src/Testimonial/index.js +3 -0
- package/src/Video/ControlBar/ControlBar.jsx +261 -0
- package/src/Video/ControlBar/Controls/VideoButton/VideoButton.jsx +61 -0
- package/src/Video/ControlBar/Controls/VideoMenu/VideoMenu.jsx +159 -0
- package/src/Video/ControlBar/Controls/VideoProgressBar/VideoProgressBar.jsx +185 -0
- package/src/Video/ControlBar/Controls/VolumeSlider/VolumeSlider.jsx +184 -0
- package/src/Video/MiddleControlButton/MiddleControlButton.jsx +64 -0
- package/src/Video/Video.jsx +988 -0
- package/src/Video/index.js +3 -0
- package/src/Video/videoText.js +58 -0
- package/src/WebVideo/WebVideo.jsx +131 -0
- package/src/WebVideo/index.js +3 -0
- package/src/baseExports.js +0 -2
- package/src/index.js +13 -0
- package/src/shared/VideoSplash/SplashButton/SplashButton.jsx +64 -0
- package/src/shared/VideoSplash/SplashButtonWithDetails/SplashButtonWithDetails.jsx +128 -0
- package/src/shared/VideoSplash/VideoSplash.jsx +50 -0
- package/src/shared/VideoSplash/helpers.js +27 -0
- package/src/utils/index.js +10 -1
- package/src/utils/useOverlaidPosition.js +226 -0
- package/types/Autocomplete.d.ts +32 -0
- package/types/Card.d.ts +45 -0
- package/types/ControlBar.d.ts +59 -0
- package/types/MiddleControlButton.d.ts +15 -0
- package/types/Video.d.ts +39 -0
- package/types/VideoButton.d.ts +14 -0
- package/types/VideoMenu.d.ts +16 -0
- package/types/VideoProgressBar.d.ts +17 -0
- package/types/VolumeSlider.d.ts +20 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/* eslint-disable react/require-default-props */
|
|
2
|
+
import React, { forwardRef } from 'react';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import { Card as CardBase, getTokensPropType, paddingProp, responsiveProps, selectSystemProps, StackView, useThemeTokens, variantProp, a11yProps, viewProps } from '@telus-uds/components-base';
|
|
5
|
+
import CardContent from './CardContent';
|
|
6
|
+
import CardFooter from './CardFooter';
|
|
7
|
+
import FullBleedContent, { getFullBleedBorderRadius, useFullBleedContentProps } from '../shared/FullBleedContent'; // Passes React Native-oriented system props through UDS Card
|
|
8
|
+
|
|
9
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
|
+
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
|
|
12
|
+
/**
|
|
13
|
+
* A basic card component, unstyled by default.
|
|
14
|
+
*
|
|
15
|
+
* ## Component API
|
|
16
|
+
*
|
|
17
|
+
* ### With Footer
|
|
18
|
+
*
|
|
19
|
+
* Pass any component or simply a string in the `footer` prop in order to render
|
|
20
|
+
* a card with a footer (which uses the `alternative` background).
|
|
21
|
+
*
|
|
22
|
+
* ### With Full Bleed Content
|
|
23
|
+
*
|
|
24
|
+
* Use `fullBleedContent` prop to add a full bleed style image, video or other content to the
|
|
25
|
+
* card. This prop accepts an object with the following properties:
|
|
26
|
+
* - `alt`: alt tag for an image,
|
|
27
|
+
* - `src`: default image source,
|
|
28
|
+
* - `position`: `none`, `bottom`, `left`, `right` or `top`, depending on where you would like your full bleed image to be placed,
|
|
29
|
+
* - all the props from the `ResponsiveImage` component in case you want that full bleed image to be responsive,
|
|
30
|
+
* - `content`: pass a custom JSX to be used for rendering of the full bleed content (defaults to `ResponsiveImage`
|
|
31
|
+
* receiving the other props).
|
|
32
|
+
*
|
|
33
|
+
* Note that `position` can be responsive, i.e. different for different viewports. A full bleed content with position
|
|
34
|
+
* {xs: 'none', md: 'left'} for example, will have a full bleed content to the left of card content when viewed on desktops
|
|
35
|
+
* viewports, and no content when viewed on mobile viewports.
|
|
36
|
+
*
|
|
37
|
+
* ## Accessibility
|
|
38
|
+
* `Card` component accepts all the standard accessibility props.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
const Card = /*#__PURE__*/forwardRef(function () {
|
|
42
|
+
let {
|
|
43
|
+
children,
|
|
44
|
+
footer,
|
|
45
|
+
footerPadding,
|
|
46
|
+
fullBleedImage,
|
|
47
|
+
fullBleedContent = fullBleedImage,
|
|
48
|
+
tokens,
|
|
49
|
+
variant,
|
|
50
|
+
...rest
|
|
51
|
+
} = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
|
|
52
|
+
fullBleedContent: {
|
|
53
|
+
position: null
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
let ref = arguments.length > 1 ? arguments[1] : undefined;
|
|
57
|
+
const {
|
|
58
|
+
contentStackAlign,
|
|
59
|
+
contentStackDirection,
|
|
60
|
+
fullBleedContentPosition,
|
|
61
|
+
fullBleedContentProps
|
|
62
|
+
} = useFullBleedContentProps(fullBleedContent); // If the card has rounded corners and a full bleed image, we need to apply
|
|
63
|
+
// those corners on the image as well, but partially
|
|
64
|
+
|
|
65
|
+
const {
|
|
66
|
+
borderRadius
|
|
67
|
+
} = useThemeTokens('Card', tokens, variant);
|
|
68
|
+
const hasFooter = Boolean(footer);
|
|
69
|
+
const fullBleedBorderRadius = getFullBleedBorderRadius(borderRadius, fullBleedContentPosition, hasFooter); // Make multiple cards in a row have equal heights with even space between content items
|
|
70
|
+
|
|
71
|
+
const columnFlex = {
|
|
72
|
+
flexGrow: 1,
|
|
73
|
+
flexShrink: 1,
|
|
74
|
+
justifyContent: 'space-between'
|
|
75
|
+
};
|
|
76
|
+
return /*#__PURE__*/_jsx(CardBase, {
|
|
77
|
+
ref: ref,
|
|
78
|
+
variant: { ...variant,
|
|
79
|
+
padding: 'custom'
|
|
80
|
+
},
|
|
81
|
+
tokens: tokens,
|
|
82
|
+
...selectProps(rest),
|
|
83
|
+
children: /*#__PURE__*/_jsxs(StackView, {
|
|
84
|
+
space: 0,
|
|
85
|
+
tokens: columnFlex,
|
|
86
|
+
children: [/*#__PURE__*/_jsxs(StackView, {
|
|
87
|
+
direction: contentStackDirection,
|
|
88
|
+
tokens: { ...columnFlex,
|
|
89
|
+
alignItems: contentStackAlign
|
|
90
|
+
},
|
|
91
|
+
space: 0,
|
|
92
|
+
children: [/*#__PURE__*/_jsx(CardContent, {
|
|
93
|
+
tokens: tokens,
|
|
94
|
+
variant: variant,
|
|
95
|
+
withFooter: hasFooter,
|
|
96
|
+
children: children
|
|
97
|
+
}), fullBleedContentPosition !== 'none' && /*#__PURE__*/_jsx(FullBleedContent, {
|
|
98
|
+
borderRadius: fullBleedBorderRadius,
|
|
99
|
+
...fullBleedContentProps
|
|
100
|
+
})]
|
|
101
|
+
}), footer && /*#__PURE__*/_jsx(CardFooter, {
|
|
102
|
+
padding: footerPadding,
|
|
103
|
+
tokens: tokens,
|
|
104
|
+
variant: variant,
|
|
105
|
+
children: footer
|
|
106
|
+
})]
|
|
107
|
+
})
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
const positionValues = ['none', 'bottom', 'left', 'right', 'top'];
|
|
111
|
+
const alignValues = ['start', 'end', 'center', 'stretch'];
|
|
112
|
+
const PositionedFullBleedContentPropType = PropTypes.shape({
|
|
113
|
+
position: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(positionValues)).isRequired,
|
|
114
|
+
align: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(alignValues)),
|
|
115
|
+
// eslint-disable-next-line react/forbid-foreign-prop-types
|
|
116
|
+
...FullBleedContent.propTypes
|
|
117
|
+
});
|
|
118
|
+
Card.displayName = 'Card';
|
|
119
|
+
Card.propTypes = { ...selectedSystemPropTypes,
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Card content.
|
|
123
|
+
*/
|
|
124
|
+
children: PropTypes.node,
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Card footer.
|
|
128
|
+
*/
|
|
129
|
+
footer: PropTypes.node,
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Custom card footer padding.
|
|
133
|
+
*/
|
|
134
|
+
footerPadding: paddingProp.propType,
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Full bleed image to be placed on the card, deprecated in favor of `fullBleedContent`.
|
|
138
|
+
*
|
|
139
|
+
* @deprecated
|
|
140
|
+
*/
|
|
141
|
+
fullBleedImage: PositionedFullBleedContentPropType,
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Full bleed content to be placed on the card.
|
|
145
|
+
*/
|
|
146
|
+
fullBleedContent: PositionedFullBleedContentPropType,
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Card tokens.
|
|
150
|
+
*/
|
|
151
|
+
tokens: getTokensPropType('Card'),
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Card variant.
|
|
155
|
+
*/
|
|
156
|
+
variant: variantProp.propType
|
|
157
|
+
};
|
|
158
|
+
export default Card;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { getTokensPropType, selectSystemProps, useThemeTokens, useViewport, variantProp } from '@telus-uds/components-base';
|
|
4
|
+
import styled from 'styled-components';
|
|
5
|
+
import { htmlAttrs } from '../utils';
|
|
6
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
7
|
+
const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs]);
|
|
8
|
+
const CardContentContainer = /*#__PURE__*/styled.div.withConfig({
|
|
9
|
+
displayName: "CardContent__CardContentContainer",
|
|
10
|
+
componentId: "components-web__sc-1k2501q-0"
|
|
11
|
+
})(_ref => {
|
|
12
|
+
let {
|
|
13
|
+
backgroundColor,
|
|
14
|
+
borderRadius,
|
|
15
|
+
paddingBottom,
|
|
16
|
+
paddingLeft,
|
|
17
|
+
paddingRight,
|
|
18
|
+
paddingTop,
|
|
19
|
+
withFooter,
|
|
20
|
+
contentAlignItem: alignItem,
|
|
21
|
+
contentFlexGrow: flexGrow,
|
|
22
|
+
contentFlexShrink: flexShrink,
|
|
23
|
+
contentJustifyContent: justifyContent
|
|
24
|
+
} = _ref;
|
|
25
|
+
return {
|
|
26
|
+
backgroundColor,
|
|
27
|
+
// We need to make sure to have sharp corners on the bottom
|
|
28
|
+
// if the card has a footer
|
|
29
|
+
borderBottomLeftRadius: withFooter ? 0 : borderRadius,
|
|
30
|
+
borderBottomRightRadius: withFooter ? 0 : borderRadius,
|
|
31
|
+
borderTopLeftRadius: borderRadius,
|
|
32
|
+
borderTopRightRadius: borderRadius,
|
|
33
|
+
paddingBottom,
|
|
34
|
+
paddingLeft,
|
|
35
|
+
paddingRight,
|
|
36
|
+
paddingTop,
|
|
37
|
+
display: 'flex',
|
|
38
|
+
flexDirection: 'column',
|
|
39
|
+
alignItem,
|
|
40
|
+
flexGrow,
|
|
41
|
+
flexShrink,
|
|
42
|
+
justifyContent
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
/**
|
|
46
|
+
* Card content, applying the card tokens as per the theme used.
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
const CardContent = _ref2 => {
|
|
50
|
+
let {
|
|
51
|
+
children,
|
|
52
|
+
flexContent,
|
|
53
|
+
tokens,
|
|
54
|
+
variant,
|
|
55
|
+
withFooter = false,
|
|
56
|
+
...rest
|
|
57
|
+
} = _ref2;
|
|
58
|
+
const viewport = useViewport();
|
|
59
|
+
const themeTokens = useThemeTokens('Card', tokens, variant, {
|
|
60
|
+
viewport
|
|
61
|
+
});
|
|
62
|
+
return /*#__PURE__*/_jsx(CardContentContainer, { ...themeTokens,
|
|
63
|
+
flexContent: flexContent,
|
|
64
|
+
withFooter: withFooter,
|
|
65
|
+
...selectProps(rest),
|
|
66
|
+
children: children
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
CardContent.propTypes = { ...selectedSystemPropTypes,
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Card section content.
|
|
74
|
+
*/
|
|
75
|
+
children: PropTypes.node,
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Card tokens.
|
|
79
|
+
*/
|
|
80
|
+
tokens: getTokensPropType('Card'),
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Card variant.
|
|
84
|
+
*/
|
|
85
|
+
variant: variantProp.propType,
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Whether the card has a footer.
|
|
89
|
+
*/
|
|
90
|
+
withFooter: PropTypes.bool
|
|
91
|
+
};
|
|
92
|
+
export default CardContent;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { getTokensPropType, paddingProp, selectSystemProps, useThemeTokens, useViewport, variantProp } from '@telus-uds/components-base';
|
|
4
|
+
import styled from 'styled-components';
|
|
5
|
+
import { htmlAttrs } from '../utils';
|
|
6
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
7
|
+
const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs]);
|
|
8
|
+
const CardFooterContainer = /*#__PURE__*/styled.div.withConfig({
|
|
9
|
+
displayName: "CardFooter__CardFooterContainer",
|
|
10
|
+
componentId: "components-web__sc-pm6vns-0"
|
|
11
|
+
})(_ref => {
|
|
12
|
+
let {
|
|
13
|
+
backgroundColor,
|
|
14
|
+
borderRadius,
|
|
15
|
+
paddingBottom,
|
|
16
|
+
paddingLeft,
|
|
17
|
+
paddingRight,
|
|
18
|
+
paddingTop
|
|
19
|
+
} = _ref;
|
|
20
|
+
return {
|
|
21
|
+
backgroundColor,
|
|
22
|
+
borderBottomLeftRadius: borderRadius,
|
|
23
|
+
borderBottomRightRadius: borderRadius,
|
|
24
|
+
// @todo circle back to the following non-standard value to
|
|
25
|
+
// see if it can be integrated into the palette
|
|
26
|
+
boxShadow: 'inset 0px 1px 3px rgba(0, 0, 0, 0.05)',
|
|
27
|
+
paddingBottom,
|
|
28
|
+
paddingLeft,
|
|
29
|
+
paddingRight,
|
|
30
|
+
paddingTop
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* Card footer, applying the tokens as per the theme used.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
const CardFooter = _ref2 => {
|
|
38
|
+
let {
|
|
39
|
+
children,
|
|
40
|
+
padding,
|
|
41
|
+
tokens,
|
|
42
|
+
variant,
|
|
43
|
+
...rest
|
|
44
|
+
} = _ref2;
|
|
45
|
+
const viewport = useViewport();
|
|
46
|
+
const themeTokens = useThemeTokens('Card', tokens, { ...variant,
|
|
47
|
+
background: 'alternative'
|
|
48
|
+
}, {
|
|
49
|
+
viewport
|
|
50
|
+
});
|
|
51
|
+
return /*#__PURE__*/_jsx(CardFooterContainer, { ...themeTokens,
|
|
52
|
+
...padding,
|
|
53
|
+
...selectProps(rest),
|
|
54
|
+
children: children
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
CardFooter.propTypes = { ...selectedSystemPropTypes,
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Card footer content.
|
|
62
|
+
*/
|
|
63
|
+
children: PropTypes.node,
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Card footer padding.
|
|
67
|
+
*/
|
|
68
|
+
padding: paddingProp.propType,
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Card tokens.
|
|
72
|
+
*/
|
|
73
|
+
tokens: getTokensPropType('Card'),
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Card variant.
|
|
77
|
+
*/
|
|
78
|
+
variant: variantProp.propType
|
|
79
|
+
};
|
|
80
|
+
export default CardFooter;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/* eslint-disable react/require-default-props */
|
|
2
|
+
import React, { forwardRef } from 'react';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import { Spacer, StackView, Typography, selectSystemProps, useViewport, useThemeTokens, applyTextStyles } from '@telus-uds/components-base';
|
|
5
|
+
import { viewports } from '@telus-uds/system-constants';
|
|
6
|
+
import styled from 'styled-components'; // Reading these from the RN palette since they will be used to generate
|
|
7
|
+
// the `Typography` tokens
|
|
8
|
+
|
|
9
|
+
import { htmlAttrs, transformGradient } from '../utils';
|
|
10
|
+
import Segment from './Segment';
|
|
11
|
+
import useCountdown from './useCountdown';
|
|
12
|
+
import { countdownVariantPropType, dictionaryContentShape } from './types';
|
|
13
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
14
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
15
|
+
const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs]);
|
|
16
|
+
const Container = /*#__PURE__*/styled.div.withConfig({
|
|
17
|
+
displayName: "Countdown__Container",
|
|
18
|
+
componentId: "components-web__sc-18tqcb2-0"
|
|
19
|
+
})(_ref => {
|
|
20
|
+
let {
|
|
21
|
+
variant: {
|
|
22
|
+
feature,
|
|
23
|
+
inverse,
|
|
24
|
+
large
|
|
25
|
+
},
|
|
26
|
+
themeTokens,
|
|
27
|
+
gradient
|
|
28
|
+
} = _ref;
|
|
29
|
+
return { ...(large || feature && {
|
|
30
|
+
display: 'flex',
|
|
31
|
+
flex: 0
|
|
32
|
+
}),
|
|
33
|
+
...(feature && {
|
|
34
|
+
borderRadius: themeTokens.containerBorderRadius,
|
|
35
|
+
justifyContent: 'center',
|
|
36
|
+
paddingBottom: `${themeTokens.containerPaddingBottomTop}px`,
|
|
37
|
+
paddingLeft: `${themeTokens.containerPaddingLeftRight}px`,
|
|
38
|
+
paddingRight: `${themeTokens.containerPaddingLeftRight}px`,
|
|
39
|
+
paddingTop: `${themeTokens.containerPaddingBottomTop}px`,
|
|
40
|
+
width: 'fit-content'
|
|
41
|
+
}),
|
|
42
|
+
...(feature && !inverse && {
|
|
43
|
+
background: `linear-gradient(#fff 0 0) padding-box, ${gradient} border-box`,
|
|
44
|
+
border: `${themeTokens.containerInverseBorder}px solid transparent`
|
|
45
|
+
}),
|
|
46
|
+
...(feature && inverse && {
|
|
47
|
+
border: `${themeTokens.containerInverseBorder}px solid ${themeTokens.inverseBorderColor}`
|
|
48
|
+
})
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const getLabelTokens = themeTokens => ({
|
|
53
|
+
color: themeTokens.labelBorderColor,
|
|
54
|
+
fontWeight: themeTokens.textTimerFontWeight,
|
|
55
|
+
fontSize: `${themeTokens.labelFontSize}`,
|
|
56
|
+
lineHeight: `${themeTokens.labelLineHeight}`
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const getMainTextTokens = themeTokens => ({
|
|
60
|
+
color: themeTokens.labelBorderColor,
|
|
61
|
+
fontWeight: themeTokens.textTimerFontWeight,
|
|
62
|
+
fontSize: themeTokens.textFontSize,
|
|
63
|
+
lineHeight: themeTokens.textLineHeight
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const Countdown = /*#__PURE__*/forwardRef((_ref2, ref) => {
|
|
67
|
+
let {
|
|
68
|
+
copy = 'en',
|
|
69
|
+
targetTime,
|
|
70
|
+
tokens,
|
|
71
|
+
variant = {},
|
|
72
|
+
...rest
|
|
73
|
+
} = _ref2;
|
|
74
|
+
const [days, hours, minutes, seconds] = useCountdown(targetTime);
|
|
75
|
+
const viewport = useViewport();
|
|
76
|
+
const {
|
|
77
|
+
feature,
|
|
78
|
+
large,
|
|
79
|
+
label,
|
|
80
|
+
noDivider
|
|
81
|
+
} = variant;
|
|
82
|
+
|
|
83
|
+
if (noDivider && !label) {
|
|
84
|
+
throw new Error('`noDivider` variant can only be used with `label` enabled!');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const themeTokens = useThemeTokens('Countdown', tokens, variant, {
|
|
88
|
+
viewport
|
|
89
|
+
});
|
|
90
|
+
const segmentFontSize = themeTokens.textFontSize;
|
|
91
|
+
const semanticGradient = themeTokens.containerGradient && transformGradient(themeTokens.containerGradient);
|
|
92
|
+
const mainTextTokens = getMainTextTokens(themeTokens);
|
|
93
|
+
const divider = noDivider === true ?
|
|
94
|
+
/*#__PURE__*/
|
|
95
|
+
// StackView-based subcontainer adds a 1-step space on the each side of the divider
|
|
96
|
+
_jsx(Spacer, {
|
|
97
|
+
direction: "row",
|
|
98
|
+
space: (feature || large) && viewport !== viewports.xs ? 7 : 2
|
|
99
|
+
}) : /*#__PURE__*/_jsx(Typography, {
|
|
100
|
+
tokens: mainTextTokens,
|
|
101
|
+
children: ":"
|
|
102
|
+
});
|
|
103
|
+
const labelTokens = getLabelTokens(themeTokens);
|
|
104
|
+
const commonProps = {
|
|
105
|
+
copy,
|
|
106
|
+
labelTokens,
|
|
107
|
+
numberTokens: mainTextTokens,
|
|
108
|
+
segmentFontSize,
|
|
109
|
+
variant
|
|
110
|
+
};
|
|
111
|
+
return (
|
|
112
|
+
/*#__PURE__*/
|
|
113
|
+
// Making it focusable for accessibility purposes
|
|
114
|
+
_jsx(Container, {
|
|
115
|
+
ref: ref,
|
|
116
|
+
variant: variant,
|
|
117
|
+
...selectProps(rest),
|
|
118
|
+
tabIndex: 0,
|
|
119
|
+
themeTokens: themeTokens,
|
|
120
|
+
gradient: semanticGradient,
|
|
121
|
+
children: /*#__PURE__*/_jsxs(StackView, {
|
|
122
|
+
direction: "row",
|
|
123
|
+
space: 1,
|
|
124
|
+
children: [/*#__PURE__*/_jsx(Segment, {
|
|
125
|
+
labelKey: "day",
|
|
126
|
+
number: days,
|
|
127
|
+
segmentWidth: String(days).length,
|
|
128
|
+
...commonProps
|
|
129
|
+
}), divider, /*#__PURE__*/_jsx(Segment, {
|
|
130
|
+
labelKey: "hour",
|
|
131
|
+
number: hours,
|
|
132
|
+
...commonProps,
|
|
133
|
+
...applyTextStyles(themeTokens)
|
|
134
|
+
}), divider, /*#__PURE__*/_jsx(Segment, {
|
|
135
|
+
labelKey: "minute",
|
|
136
|
+
number: minutes,
|
|
137
|
+
...commonProps,
|
|
138
|
+
...applyTextStyles(themeTokens)
|
|
139
|
+
}), divider, /*#__PURE__*/_jsx(Segment, {
|
|
140
|
+
labelKey: "second",
|
|
141
|
+
number: seconds,
|
|
142
|
+
...commonProps,
|
|
143
|
+
...applyTextStyles(themeTokens)
|
|
144
|
+
})]
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
Countdown.displayName = 'Countdown';
|
|
150
|
+
Countdown.propTypes = { ...selectedSystemPropTypes,
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Copy language identifier (`'en'` or `'fr'`) or a dictionary instance (an object with
|
|
154
|
+
* the following keys: days, day, hours, hour, minutes, minute, seconds, second)
|
|
155
|
+
*/
|
|
156
|
+
copy: PropTypes.oneOfType([PropTypes.oneOf(['en', 'fr']), dictionaryContentShape]),
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* An instance of JavaScript `Date` object or a string parseable via `Date.parse()`
|
|
160
|
+
* representing a point in the future to count down to.
|
|
161
|
+
*/
|
|
162
|
+
targetTime: PropTypes.instanceOf(Date),
|
|
163
|
+
variant: countdownVariantPropType
|
|
164
|
+
};
|
|
165
|
+
export default Countdown;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { StackView, Typography, useCopy } from '@telus-uds/components-base';
|
|
4
|
+
import styled from 'styled-components';
|
|
5
|
+
import dictionary from './dictionary';
|
|
6
|
+
import { countdownVariantPropType, dictionaryContentShape } from './types';
|
|
7
|
+
import { SEGMENT_WIDTH_TO_FONT_SIZE_RATIO } from './constants'; // Pads with zeros on the left if it's a single digit number
|
|
8
|
+
|
|
9
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
|
+
|
|
12
|
+
const pad = function (number) {
|
|
13
|
+
let segmentWidth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2;
|
|
14
|
+
return String(number).padStart(segmentWidth, '0');
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const Container = /*#__PURE__*/styled.div.withConfig({
|
|
18
|
+
displayName: "Segment__Container",
|
|
19
|
+
componentId: "components-web__sc-yrh35y-0"
|
|
20
|
+
})(_ref => {
|
|
21
|
+
let {
|
|
22
|
+
segmentFontSize,
|
|
23
|
+
segmentWidth = 2,
|
|
24
|
+
variant: {
|
|
25
|
+
feature
|
|
26
|
+
}
|
|
27
|
+
} = _ref;
|
|
28
|
+
return {
|
|
29
|
+
justifyContent: 'space-evenly',
|
|
30
|
+
display: 'inline-flex',
|
|
31
|
+
...(feature && {
|
|
32
|
+
width: `${segmentFontSize * SEGMENT_WIDTH_TO_FONT_SIZE_RATIO * segmentWidth}px`,
|
|
33
|
+
display: 'flex'
|
|
34
|
+
})
|
|
35
|
+
};
|
|
36
|
+
}); // A segment of the countdown string: we need to make sure it
|
|
37
|
+
// keeps its width constant to prevent the whole component from
|
|
38
|
+
// being automatically resized while using variable size fonts
|
|
39
|
+
|
|
40
|
+
const Segment = _ref2 => {
|
|
41
|
+
let {
|
|
42
|
+
copy = 'en',
|
|
43
|
+
segmentFontSize,
|
|
44
|
+
labelKey,
|
|
45
|
+
labelTokens,
|
|
46
|
+
number,
|
|
47
|
+
numberTokens,
|
|
48
|
+
segmentWidth = 2,
|
|
49
|
+
variant = {}
|
|
50
|
+
} = _ref2;
|
|
51
|
+
const getCopy = useCopy({
|
|
52
|
+
dictionary,
|
|
53
|
+
copy
|
|
54
|
+
});
|
|
55
|
+
const {
|
|
56
|
+
label,
|
|
57
|
+
large,
|
|
58
|
+
feature
|
|
59
|
+
} = variant;
|
|
60
|
+
return /*#__PURE__*/_jsx(Container, {
|
|
61
|
+
segmentFontSize: segmentFontSize,
|
|
62
|
+
segmentWidth: segmentWidth,
|
|
63
|
+
variant: variant,
|
|
64
|
+
children: /*#__PURE__*/_jsxs(StackView, {
|
|
65
|
+
direction: large || feature ? 'column' : 'row',
|
|
66
|
+
space: large || feature ? 0 : 1,
|
|
67
|
+
tokens: {
|
|
68
|
+
alignItems: 'center'
|
|
69
|
+
},
|
|
70
|
+
children: [/*#__PURE__*/_jsx(Typography, {
|
|
71
|
+
tokens: numberTokens,
|
|
72
|
+
children: pad(number, segmentWidth)
|
|
73
|
+
}), label && /*#__PURE__*/_jsx(Typography, {
|
|
74
|
+
tokens: labelTokens,
|
|
75
|
+
children: getCopy(number === 1 ? labelKey : `${labelKey}s`)
|
|
76
|
+
})]
|
|
77
|
+
})
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
Segment.propTypes = {
|
|
82
|
+
/**
|
|
83
|
+
* Copy language identifier or a dictionary instance.
|
|
84
|
+
*/
|
|
85
|
+
copy: PropTypes.oneOfType([PropTypes.oneOf(['en', 'fr']), dictionaryContentShape]),
|
|
86
|
+
segmentFontSize: PropTypes.number,
|
|
87
|
+
labelKey: PropTypes.oneOf(['day', 'hour', 'minute', 'second']),
|
|
88
|
+
labelTokens: PropTypes.object,
|
|
89
|
+
number: PropTypes.number,
|
|
90
|
+
numberTokens: PropTypes.object,
|
|
91
|
+
segmentWidth: PropTypes.number,
|
|
92
|
+
variant: countdownVariantPropType
|
|
93
|
+
};
|
|
94
|
+
export default Segment;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
en: {
|
|
3
|
+
days: 'Days',
|
|
4
|
+
day: 'Day',
|
|
5
|
+
hours: 'Hours',
|
|
6
|
+
hour: 'Hour',
|
|
7
|
+
minutes: 'Minutes',
|
|
8
|
+
minute: 'Minute',
|
|
9
|
+
seconds: 'Seconds',
|
|
10
|
+
second: 'Second'
|
|
11
|
+
},
|
|
12
|
+
fr: {
|
|
13
|
+
days: 'Jours',
|
|
14
|
+
day: 'Jour',
|
|
15
|
+
hours: 'Heures',
|
|
16
|
+
hour: 'Heure',
|
|
17
|
+
minutes: 'Minutes',
|
|
18
|
+
minute: 'Minute',
|
|
19
|
+
seconds: 'Secondes',
|
|
20
|
+
second: 'Seconde'
|
|
21
|
+
}
|
|
22
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
export const countdownVariantPropType = PropTypes.shape({
|
|
3
|
+
feature: PropTypes.bool,
|
|
4
|
+
inverse: PropTypes.bool,
|
|
5
|
+
label: PropTypes.bool,
|
|
6
|
+
large: PropTypes.bool,
|
|
7
|
+
noDivider: PropTypes.bool
|
|
8
|
+
}); // If a language dictionary entry is provided, it must contain every key
|
|
9
|
+
|
|
10
|
+
export const dictionaryContentShape = PropTypes.shape({
|
|
11
|
+
days: PropTypes.string.isRequired,
|
|
12
|
+
day: PropTypes.string.isRequired,
|
|
13
|
+
hours: PropTypes.string.isRequired,
|
|
14
|
+
hour: PropTypes.string.isRequired,
|
|
15
|
+
minutes: PropTypes.string.isRequired,
|
|
16
|
+
minute: PropTypes.string.isRequired,
|
|
17
|
+
seconds: PropTypes.string.isRequired,
|
|
18
|
+
second: PropTypes.string.isRequired
|
|
19
|
+
});
|
|
20
|
+
export default {
|
|
21
|
+
countdownVariantPropType,
|
|
22
|
+
dictionaryContentShape
|
|
23
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
const getTimeCounts = countdown => {
|
|
4
|
+
if (countdown <= 0) {
|
|
5
|
+
return [0, 0, 0, 0];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const days = Math.floor(countdown / (1000 * 60 * 60 * 24));
|
|
9
|
+
const hours = Math.floor(countdown % (1000 * 60 * 60 * 24) / (1000 * 60 * 60));
|
|
10
|
+
const minutes = Math.floor(countdown % (1000 * 60 * 60) / (1000 * 60));
|
|
11
|
+
const seconds = Math.floor(countdown % (1000 * 60) / 1000);
|
|
12
|
+
return [days, hours, minutes, seconds];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const useCountdown = targetTime => {
|
|
16
|
+
const countdownTime = new Date(targetTime).getTime();
|
|
17
|
+
|
|
18
|
+
if (!countdownTime) {
|
|
19
|
+
throw new Error('Invalid target time is provided!');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const [countdown, setCountdown] = useState(countdownTime - new Date().getTime());
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const interval = setInterval(() => {
|
|
25
|
+
setCountdown(countdownTime - new Date().getTime());
|
|
26
|
+
}, 1000);
|
|
27
|
+
return () => clearInterval(interval);
|
|
28
|
+
}, [countdownTime]);
|
|
29
|
+
return getTimeCounts(countdown);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default useCountdown;
|