@telefonica/mistica 17.5.0 → 17.6.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/css/mistica.css +1 -1
- package/dist/accordion.css-mistica.js +6 -6
- package/dist/align.css-mistica.js +1 -1
- package/dist/autocomplete.css-mistica.js +1 -1
- package/dist/avatar.css-mistica.js +1 -1
- package/dist/badge.css-mistica.js +1 -1
- package/dist/box.css-mistica.js +13 -13
- package/dist/boxed.css-mistica.js +24 -24
- package/dist/button-group.css-mistica.js +1 -1
- package/dist/button-layout.css-mistica.js +14 -14
- package/dist/button.css-mistica.js +34 -34
- package/dist/callout.css-mistica.js +11 -11
- package/dist/card-cover.d.ts +2 -2
- package/dist/card-data.d.ts +2 -2
- package/dist/card-data.js +6 -8
- package/dist/card-internal.css-mistica.js +47 -29
- package/dist/card-internal.css.d.ts +4 -0
- package/dist/card-internal.d.ts +38 -3
- package/dist/card-internal.js +394 -301
- package/dist/card-media.d.ts +2 -2
- package/dist/card-media.js +7 -9
- package/dist/card-naked.d.ts +2 -2
- package/dist/card-naked.js +9 -11
- package/dist/carousel.css-mistica.js +8 -8
- package/dist/checkbox.css-mistica.js +11 -11
- package/dist/checkbox.d.ts +2 -0
- package/dist/checkbox.js +10 -10
- package/dist/chip.css-mistica.js +15 -15
- package/dist/circle.css-mistica.js +1 -1
- package/dist/community/advanced-data-card.css-mistica.js +20 -11
- package/dist/community/advanced-data-card.css.d.ts +2 -0
- package/dist/community/advanced-data-card.d.ts +7 -3
- package/dist/community/advanced-data-card.js +164 -142
- package/dist/community/ai-card.css-mistica.js +13 -13
- package/dist/community/ai-card.js +1 -1
- package/dist/community/blocks.css-mistica.js +1 -1
- package/dist/community/cyber/example-component.css-mistica.js +1 -1
- package/dist/community/example-component.css-mistica.js +1 -1
- package/dist/counter.css-mistica.js +1 -1
- package/dist/cover-hero.css-mistica.js +2 -2
- package/dist/credit-card-number-field.css-mistica.js +3 -3
- package/dist/date-field.css-mistica.js +1 -1
- package/dist/date-time-picker.css-mistica.js +1 -1
- package/dist/dialog.css-mistica.js +4 -4
- package/dist/divider.css-mistica.js +5 -5
- package/dist/double-field.css-mistica.js +2 -2
- package/dist/drawer.css-mistica.js +1 -1
- package/dist/empty-state-card.css-mistica.js +1 -1
- package/dist/empty-state.css-mistica.js +5 -5
- package/dist/fade-in.css-mistica.js +1 -1
- package/dist/fade-out.css-mistica.js +1 -1
- package/dist/feedback.css-mistica.js +1 -1
- package/dist/file-upload.css-mistica.js +7 -7
- package/dist/fixed-footer-layout.css-mistica.js +2 -2
- package/dist/form.css-mistica.js +1 -1
- package/dist/generated/mistica-icons/icon-document-information-filled.js +165 -0
- package/dist/generated/mistica-icons/icon-document-information-light.js +165 -0
- package/dist/generated/mistica-icons/icon-document-information-regular.js +165 -0
- package/dist/generated/mistica-icons/icon-internet-roaming-filled.js +159 -0
- package/dist/generated/mistica-icons/icon-internet-roaming-light.js +159 -0
- package/dist/generated/mistica-icons/icon-internet-roaming-regular.js +159 -0
- package/dist/generated/mistica-icons/icon-mobile-information-filled.js +165 -0
- package/dist/generated/mistica-icons/icon-mobile-information-light.js +173 -0
- package/dist/generated/mistica-icons/icon-mobile-information-regular.js +165 -0
- package/dist/generated/mistica-icons/icon-search-ai-filled.js +165 -0
- package/dist/generated/mistica-icons/icon-search-ai-light.js +165 -0
- package/dist/generated/mistica-icons/icon-search-ai-regular.js +165 -0
- package/dist/grid-layout.css-mistica.js +3 -3
- package/dist/grid.css-mistica.js +120 -120
- package/dist/header.css-mistica.js +1 -1
- package/dist/hero.css-mistica.js +2 -2
- package/dist/horizontal-scroll.css-mistica.js +1 -1
- package/dist/icon-button.css-mistica.js +53 -53
- package/dist/icons/icon-chevron.css-mistica.js +2 -2
- package/dist/icons/icon-error.css-mistica.js +1 -1
- package/dist/image.css-mistica.js +11 -11
- package/dist/index.d.ts +12 -0
- package/dist/index.js +48 -0
- package/dist/inline.css-mistica.js +11 -11
- package/dist/list.css-mistica.js +7 -7
- package/dist/loading-bar.css-mistica.js +1 -1
- package/dist/loading-screen.css-mistica.js +4 -4
- package/dist/logo.css-mistica.js +5 -5
- package/dist/menu.css-mistica.js +13 -13
- package/dist/mosaic.css-mistica.js +1 -1
- package/dist/navigation-bar.css-mistica.js +9 -9
- package/dist/navigation-breadcrumbs.css-mistica.js +1 -1
- package/dist/navigation-shared.css-mistica.js +9 -9
- package/dist/package-version.js +1 -1
- package/dist/pagination.css-mistica.js +18 -18
- package/dist/pin-field.css-mistica.js +1 -1
- package/dist/popover.css-mistica.js +1 -1
- package/dist/progress-bar.css-mistica.js +6 -6
- package/dist/radio-button.css-mistica.js +19 -19
- package/dist/radio-button.d.ts +3 -1
- package/dist/radio-button.js +73 -72
- package/dist/rating.css-mistica.js +2 -2
- package/dist/responsive-layout.css-mistica.js +6 -6
- package/dist/screen-reader-only.css-mistica.js +1 -1
- package/dist/select.css-mistica.js +15 -15
- package/dist/sheet-action-row.css-mistica.js +1 -1
- package/dist/sheet-common.css-mistica.js +1 -1
- package/dist/sheet-info.css-mistica.js +1 -1
- package/dist/sidenav-bar-layout.css-mistica.js +1 -1
- package/dist/sidenav-bar.css-mistica.js +71 -71
- package/dist/skeletons.css-mistica.js +6 -6
- package/dist/skins/skin-contract.css-mistica.js +738 -738
- package/dist/skip-link.css-mistica.js +1 -1
- package/dist/slider.css-mistica.js +18 -18
- package/dist/snackbar.css-mistica.js +4 -4
- package/dist/spinner.css-mistica.js +1 -1
- package/dist/square.css-mistica.js +1 -1
- package/dist/stack.css-mistica.js +5 -5
- package/dist/stacking-group.css-mistica.js +1 -1
- package/dist/stepper.css-mistica.js +2 -2
- package/dist/switch-component.css-mistica.js +35 -35
- package/dist/switch-component.d.ts +2 -0
- package/dist/switch-component.js +19 -18
- package/dist/table.css-mistica.js +9 -9
- package/dist/tabs.css-mistica.js +17 -17
- package/dist/tag.css-mistica.js +1 -1
- package/dist/text-field-base.css-mistica.js +16 -16
- package/dist/text-field-components.css-mistica.js +3 -3
- package/dist/text-link.css-mistica.js +6 -6
- package/dist/text.css-mistica.js +6 -6
- package/dist/timeline.css-mistica.js +9 -9
- package/dist/timer.css-mistica.js +6 -6
- package/dist/tooltip.css-mistica.js +1 -1
- package/dist/touchable.css-mistica.js +1 -1
- package/dist/utils/aspect-ratio-support.css-mistica.js +2 -2
- package/dist/video.css-mistica.js +1 -1
- package/dist/vivinho-loading-animation/vivinho-loading-animation.css-mistica.js +1 -1
- package/dist-es/accordion.css-mistica.js +6 -6
- package/dist-es/align.css-mistica.js +1 -1
- package/dist-es/autocomplete.css-mistica.js +1 -1
- package/dist-es/avatar.css-mistica.js +1 -1
- package/dist-es/badge.css-mistica.js +1 -1
- package/dist-es/box.css-mistica.js +13 -13
- package/dist-es/boxed.css-mistica.js +23 -23
- package/dist-es/button-group.css-mistica.js +1 -1
- package/dist-es/button-layout.css-mistica.js +14 -14
- package/dist-es/button.css-mistica.js +34 -34
- package/dist-es/callout.css-mistica.js +11 -11
- package/dist-es/card-data.js +10 -12
- package/dist-es/card-internal.css-mistica.js +25 -19
- package/dist-es/card-internal.js +482 -395
- package/dist-es/card-media.js +11 -13
- package/dist-es/card-naked.js +14 -16
- package/dist-es/carousel.css-mistica.js +8 -8
- package/dist-es/checkbox.css-mistica.js +11 -11
- package/dist-es/checkbox.js +19 -19
- package/dist-es/chip.css-mistica.js +15 -15
- package/dist-es/circle.css-mistica.js +1 -1
- package/dist-es/community/advanced-data-card.css-mistica.js +10 -7
- package/dist-es/community/advanced-data-card.js +214 -192
- package/dist-es/community/ai-card.css-mistica.js +4 -4
- package/dist-es/community/ai-card.js +1 -1
- package/dist-es/community/blocks.css-mistica.js +1 -1
- package/dist-es/community/cyber/example-component.css-mistica.js +1 -1
- package/dist-es/community/example-component.css-mistica.js +1 -1
- package/dist-es/counter.css-mistica.js +1 -1
- package/dist-es/cover-hero.css-mistica.js +2 -2
- package/dist-es/credit-card-number-field.css-mistica.js +3 -3
- package/dist-es/date-field.css-mistica.js +1 -1
- package/dist-es/date-time-picker.css-mistica.js +1 -1
- package/dist-es/dialog.css-mistica.js +4 -4
- package/dist-es/divider.css-mistica.js +5 -5
- package/dist-es/double-field.css-mistica.js +2 -2
- package/dist-es/drawer.css-mistica.js +1 -1
- package/dist-es/empty-state-card.css-mistica.js +1 -1
- package/dist-es/empty-state.css-mistica.js +5 -5
- package/dist-es/fade-in.css-mistica.js +1 -1
- package/dist-es/fade-out.css-mistica.js +1 -1
- package/dist-es/feedback.css-mistica.js +1 -1
- package/dist-es/file-upload.css-mistica.js +7 -7
- package/dist-es/fixed-footer-layout.css-mistica.js +2 -2
- package/dist-es/form.css-mistica.js +1 -1
- package/dist-es/generated/mistica-icons/icon-document-information-filled.js +115 -0
- package/dist-es/generated/mistica-icons/icon-document-information-light.js +115 -0
- package/dist-es/generated/mistica-icons/icon-document-information-regular.js +115 -0
- package/dist-es/generated/mistica-icons/icon-internet-roaming-filled.js +109 -0
- package/dist-es/generated/mistica-icons/icon-internet-roaming-light.js +109 -0
- package/dist-es/generated/mistica-icons/icon-internet-roaming-regular.js +109 -0
- package/dist-es/generated/mistica-icons/icon-mobile-information-filled.js +115 -0
- package/dist-es/generated/mistica-icons/icon-mobile-information-light.js +123 -0
- package/dist-es/generated/mistica-icons/icon-mobile-information-regular.js +115 -0
- package/dist-es/generated/mistica-icons/icon-search-ai-filled.js +115 -0
- package/dist-es/generated/mistica-icons/icon-search-ai-light.js +115 -0
- package/dist-es/generated/mistica-icons/icon-search-ai-regular.js +115 -0
- package/dist-es/grid-layout.css-mistica.js +3 -3
- package/dist-es/grid.css-mistica.js +120 -120
- package/dist-es/header.css-mistica.js +1 -1
- package/dist-es/hero.css-mistica.js +2 -2
- package/dist-es/horizontal-scroll.css-mistica.js +1 -1
- package/dist-es/icon-button.css-mistica.js +53 -53
- package/dist-es/icons/icon-chevron.css-mistica.js +2 -2
- package/dist-es/icons/icon-error.css-mistica.js +1 -1
- package/dist-es/image.css-mistica.js +4 -4
- package/dist-es/index.js +1501 -1489
- package/dist-es/inline.css-mistica.js +11 -11
- package/dist-es/list.css-mistica.js +7 -7
- package/dist-es/loading-bar.css-mistica.js +1 -1
- package/dist-es/loading-screen.css-mistica.js +4 -4
- package/dist-es/logo.css-mistica.js +5 -5
- package/dist-es/menu.css-mistica.js +13 -13
- package/dist-es/mosaic.css-mistica.js +1 -1
- package/dist-es/navigation-bar.css-mistica.js +9 -9
- package/dist-es/navigation-breadcrumbs.css-mistica.js +1 -1
- package/dist-es/navigation-shared.css-mistica.js +9 -9
- package/dist-es/package-version.js +1 -1
- package/dist-es/pagination.css-mistica.js +18 -18
- package/dist-es/pin-field.css-mistica.js +1 -1
- package/dist-es/popover.css-mistica.js +1 -1
- package/dist-es/progress-bar.css-mistica.js +6 -6
- package/dist-es/radio-button.css-mistica.js +19 -19
- package/dist-es/radio-button.js +91 -90
- package/dist-es/rating.css-mistica.js +2 -2
- package/dist-es/responsive-layout.css-mistica.js +6 -6
- package/dist-es/screen-reader-only.css-mistica.js +1 -1
- package/dist-es/select.css-mistica.js +15 -15
- package/dist-es/sheet-action-row.css-mistica.js +1 -1
- package/dist-es/sheet-common.css-mistica.js +1 -1
- package/dist-es/sheet-info.css-mistica.js +1 -1
- package/dist-es/sidenav-bar-layout.css-mistica.js +1 -1
- package/dist-es/sidenav-bar.css-mistica.js +71 -71
- package/dist-es/skeletons.css-mistica.js +6 -6
- package/dist-es/skins/skin-contract.css-mistica.js +738 -738
- package/dist-es/skip-link.css-mistica.js +1 -1
- package/dist-es/slider.css-mistica.js +18 -18
- package/dist-es/snackbar.css-mistica.js +4 -4
- package/dist-es/spinner.css-mistica.js +1 -1
- package/dist-es/square.css-mistica.js +1 -1
- package/dist-es/stack.css-mistica.js +5 -5
- package/dist-es/stacking-group.css-mistica.js +1 -1
- package/dist-es/stepper.css-mistica.js +2 -2
- package/dist-es/style.css +1 -1
- package/dist-es/switch-component.css-mistica.js +35 -35
- package/dist-es/switch-component.js +34 -33
- package/dist-es/table.css-mistica.js +9 -9
- package/dist-es/tabs.css-mistica.js +17 -17
- package/dist-es/tag.css-mistica.js +1 -1
- package/dist-es/text-field-base.css-mistica.js +16 -16
- package/dist-es/text-field-components.css-mistica.js +3 -3
- package/dist-es/text-link.css-mistica.js +6 -6
- package/dist-es/text.css-mistica.js +6 -6
- package/dist-es/timeline.css-mistica.js +9 -9
- package/dist-es/timer.css-mistica.js +6 -6
- package/dist-es/tooltip.css-mistica.js +1 -1
- package/dist-es/touchable.css-mistica.js +1 -1
- package/dist-es/utils/aspect-ratio-support.css-mistica.js +2 -2
- package/dist-es/video.css-mistica.js +1 -1
- package/dist-es/vivinho-loading-animation/vivinho-loading-animation.css-mistica.js +1 -1
- package/doc/components.md +35 -0
- package/package.json +4 -3
- package/src/card-cover.tsx +2 -2
- package/src/card-data.tsx +3 -4
- package/src/card-internal.css.ts +36 -0
- package/src/card-internal.tsx +162 -9
- package/src/card-media.tsx +3 -4
- package/src/card-naked.tsx +3 -4
- package/src/checkbox.tsx +6 -1
- package/src/community/__screenshot_tests__/__image_snapshots__/ai-card-screenshot-test-tsx-ai-card-line-break-at-chars-1-snap.png +0 -0
- package/src/community/__screenshot_tests__/__image_snapshots__/ai-card-screenshot-test-tsx-ai-card-static-text-1-snap.png +0 -0
- package/src/community/__screenshot_tests__/ai-card-screenshot-test.tsx +28 -0
- package/src/community/__stories__/advanced-data-card-story.tsx +5 -0
- package/src/community/__type_tests__/advanced-data-card-type-test.tsx +3 -0
- package/src/community/advanced-data-card.css.ts +26 -1
- package/src/community/advanced-data-card.tsx +123 -73
- package/src/community/ai-card.tsx +3 -3
- package/src/index.tsx +12 -0
- package/src/package-version.tsx +1 -1
- package/src/radio-button.tsx +7 -1
- package/src/switch-component.tsx +7 -3
package/src/card-internal.css.ts
CHANGED
|
@@ -269,3 +269,39 @@ export const stretchedLink = style({
|
|
|
269
269
|
cursor: 'pointer',
|
|
270
270
|
},
|
|
271
271
|
});
|
|
272
|
+
|
|
273
|
+
const selectionOutlineWidth = 2;
|
|
274
|
+
const selectionOutlineOffset = 2;
|
|
275
|
+
|
|
276
|
+
export const selectionOutline = style({
|
|
277
|
+
selectors: {
|
|
278
|
+
'&::after': {
|
|
279
|
+
content: '',
|
|
280
|
+
position: 'absolute',
|
|
281
|
+
inset: -(selectionOutlineWidth + selectionOutlineOffset),
|
|
282
|
+
border: `${selectionOutlineWidth}px solid transparent`,
|
|
283
|
+
borderRadius: `calc(${skinVars.borderRadii.container} + 3px)`,
|
|
284
|
+
pointerEvents: 'none',
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
export const selectionOutlineColor = styleVariants({
|
|
290
|
+
default: {selectors: {'&::after': {borderColor: skinVars.colors.controlActivated}}},
|
|
291
|
+
alternative: {selectors: {'&::after': {borderColor: skinVars.colors.controlActivated}}},
|
|
292
|
+
brand: {selectors: {'&::after': {borderColor: skinVars.colors.controlActivatedBrand}}},
|
|
293
|
+
media: {selectors: {'&::after': {borderColor: skinVars.colors.controlActivated}}},
|
|
294
|
+
negative: {selectors: {'&::after': {borderColor: skinVars.colors.controlActivatedNegative}}},
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
export const selectionControl = style({borderRadius: skinVars.borderRadii.container});
|
|
298
|
+
|
|
299
|
+
export const selectionControlContent = style({
|
|
300
|
+
display: 'flex',
|
|
301
|
+
flexDirection: 'column',
|
|
302
|
+
width: '100%',
|
|
303
|
+
height: '100%',
|
|
304
|
+
flexGrow: 1,
|
|
305
|
+
borderRadius: 'inherit',
|
|
306
|
+
outlineOffset: 0,
|
|
307
|
+
});
|
package/src/card-internal.tsx
CHANGED
|
@@ -20,6 +20,9 @@ import {getPrefixedDataAttributes} from './utils/dom';
|
|
|
20
20
|
import {applyCssVars} from './utils/css';
|
|
21
21
|
import {InternalBoxed} from './boxed';
|
|
22
22
|
import {BaseTouchable} from './touchable';
|
|
23
|
+
import Checkbox from './checkbox';
|
|
24
|
+
import Switch from './switch-component';
|
|
25
|
+
import RadioButton, {useRadioContext} from './radio-button';
|
|
23
26
|
import {AspectRatioContainer, aspectRatioToNumber} from './utils/aspect-ratio-support';
|
|
24
27
|
import {vars as skinVars} from './skins/skin-contract.css';
|
|
25
28
|
import {IconButton, ToggleIconButton} from './icon-button';
|
|
@@ -42,7 +45,98 @@ import type {ButtonLink, ButtonPrimary, ButtonSecondary} from './button';
|
|
|
42
45
|
import type {NonDeprecatedVariant, Variant} from './theme-variant-context';
|
|
43
46
|
import type {VideoElement, VideoSource, AspectRatio as VideoAspectRatio} from './video';
|
|
44
47
|
import type {AspectRatio as ImageAspectRatio} from './image';
|
|
45
|
-
import type {PressHandler, TouchableElement} from './touchable';
|
|
48
|
+
import type {PressHandler, TouchableElement, TouchableProps as BaseTouchableProps} from './touchable';
|
|
49
|
+
|
|
50
|
+
type ControlProps = {
|
|
51
|
+
name?: string;
|
|
52
|
+
value?: boolean;
|
|
53
|
+
defaultValue?: boolean;
|
|
54
|
+
onChange?: (checked: boolean) => void;
|
|
55
|
+
disabled?: boolean;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export type CardSelectionProps = ExclusifyUnion<
|
|
59
|
+
{checkbox: ControlProps} | {switch: ControlProps} | {radioValue: string}
|
|
60
|
+
>;
|
|
61
|
+
|
|
62
|
+
export const useControlState = ({
|
|
63
|
+
value,
|
|
64
|
+
defaultValue,
|
|
65
|
+
onChange,
|
|
66
|
+
}: {
|
|
67
|
+
value?: boolean;
|
|
68
|
+
defaultValue?: boolean;
|
|
69
|
+
onChange?: (isChecked: boolean) => void;
|
|
70
|
+
} = {}): {isChecked: boolean; toggleChecked: () => void} => {
|
|
71
|
+
const isControlledByParent = value !== undefined;
|
|
72
|
+
const [isChecked, setIsChecked] = React.useState<boolean>(!!defaultValue);
|
|
73
|
+
|
|
74
|
+
const toggleChecked = () => {
|
|
75
|
+
if (!isControlledByParent) {
|
|
76
|
+
setIsChecked(!isChecked);
|
|
77
|
+
}
|
|
78
|
+
onChange?.(isControlledByParent ? !value : !isChecked);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
return {isChecked: isControlledByParent ? !!value : isChecked, toggleChecked};
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
type CardControlProps = {
|
|
85
|
+
checkbox?: ControlProps;
|
|
86
|
+
switch?: ControlProps;
|
|
87
|
+
radioValue?: string;
|
|
88
|
+
checked: boolean;
|
|
89
|
+
onChange: () => void;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const CardWithControl = ({
|
|
93
|
+
checkbox,
|
|
94
|
+
switch: switchProps,
|
|
95
|
+
radioValue,
|
|
96
|
+
checked,
|
|
97
|
+
onChange,
|
|
98
|
+
...props
|
|
99
|
+
}: BaseTouchableProps & CardSelectionProps & Pick<CardControlProps, 'checked' | 'onChange'>): JSX.Element => {
|
|
100
|
+
const id = React.useId();
|
|
101
|
+
const render = ({controlElement}: {controlElement: React.ReactElement}) => (
|
|
102
|
+
<>
|
|
103
|
+
{props.children}
|
|
104
|
+
<div className={styles.topActionsContainer} data-testid="cardSelector" aria-hidden>
|
|
105
|
+
{controlElement}
|
|
106
|
+
</div>
|
|
107
|
+
</>
|
|
108
|
+
);
|
|
109
|
+
const renderWithControl = (control: React.ReactNode) => (
|
|
110
|
+
<div className={classnames(styles.selectionControl, props.className)} style={props.style}>
|
|
111
|
+
{control}
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
if (switchProps || checkbox) {
|
|
116
|
+
const Control = switchProps ? Switch : Checkbox;
|
|
117
|
+
return renderWithControl(
|
|
118
|
+
<Control
|
|
119
|
+
className={styles.selectionControlContent}
|
|
120
|
+
name={switchProps?.name ?? checkbox?.name ?? id}
|
|
121
|
+
checked={checked}
|
|
122
|
+
onChange={onChange}
|
|
123
|
+
disabled={switchProps?.disabled ?? checkbox?.disabled}
|
|
124
|
+
aria-label={props['aria-label']}
|
|
125
|
+
aria-labelledby={props['aria-labelledby']}
|
|
126
|
+
render={render}
|
|
127
|
+
/>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
return renderWithControl(
|
|
131
|
+
<RadioButton
|
|
132
|
+
className={styles.selectionControlContent}
|
|
133
|
+
value={radioValue}
|
|
134
|
+
aria-label={props['aria-label']}
|
|
135
|
+
aria-labelledby={props['aria-labelledby']}
|
|
136
|
+
render={render}
|
|
137
|
+
/>
|
|
138
|
+
);
|
|
139
|
+
};
|
|
46
140
|
|
|
47
141
|
export type CardAspectRatio = '1:1' | '16:9' | '7:10' | '9:10' | 'auto' | number;
|
|
48
142
|
export type MediaAspectRatio = ImageAspectRatio | VideoAspectRatio | 'auto' | number;
|
|
@@ -67,6 +161,7 @@ type ContainerProps = {
|
|
|
67
161
|
type: CardType;
|
|
68
162
|
size: CardSize;
|
|
69
163
|
variant?: Variant;
|
|
164
|
+
selected?: boolean;
|
|
70
165
|
width?: string | number;
|
|
71
166
|
height?: string | number;
|
|
72
167
|
/** Gradient overlay color for cover cards. If not set it uses the theme color */
|
|
@@ -198,7 +293,11 @@ type TouchableProps = {
|
|
|
198
293
|
>;
|
|
199
294
|
|
|
200
295
|
type TouchableCard<T> = T & TouchableProps;
|
|
201
|
-
export type
|
|
296
|
+
export type CardInteractionProps<T> = ExclusifyUnion<
|
|
297
|
+
| (TouchableCard<T> & {selected?: boolean})
|
|
298
|
+
| (T & {selected?: boolean})
|
|
299
|
+
| (Omit<T, 'topActions' | 'onClose' | 'selected'> & CardSelectionProps)
|
|
300
|
+
>;
|
|
202
301
|
|
|
203
302
|
type PrivateContainerProps = {
|
|
204
303
|
children?: React.ReactNode;
|
|
@@ -219,6 +318,7 @@ const Container = React.forwardRef<HTMLDivElement, ContainerProps & MediaProps &
|
|
|
219
318
|
dataAttributes,
|
|
220
319
|
backgroundColor,
|
|
221
320
|
variant,
|
|
321
|
+
selected,
|
|
222
322
|
},
|
|
223
323
|
ref
|
|
224
324
|
): JSX.Element => {
|
|
@@ -249,6 +349,10 @@ const Container = React.forwardRef<HTMLDivElement, ContainerProps & MediaProps &
|
|
|
249
349
|
}}
|
|
250
350
|
>
|
|
251
351
|
<div
|
|
352
|
+
className={classnames({
|
|
353
|
+
[styles.selectionOutline]: !!selected,
|
|
354
|
+
[styles.selectionOutlineColor[normalizeVariant(variant || 'default')]]: !!selected,
|
|
355
|
+
})}
|
|
252
356
|
style={{
|
|
253
357
|
display: 'flex',
|
|
254
358
|
flexDirection: 'column',
|
|
@@ -1189,7 +1293,7 @@ const TextContent = ({
|
|
|
1189
1293
|
);
|
|
1190
1294
|
};
|
|
1191
1295
|
|
|
1192
|
-
type CardTouchableProps = {
|
|
1296
|
+
type CardTouchableProps = CardControlProps & {
|
|
1193
1297
|
children: React.ReactNode;
|
|
1194
1298
|
isTouchable: boolean;
|
|
1195
1299
|
touchableAriaLabel?: string;
|
|
@@ -1205,6 +1309,11 @@ type CardTouchableProps = {
|
|
|
1205
1309
|
};
|
|
1206
1310
|
|
|
1207
1311
|
const CardTouchable = ({
|
|
1312
|
+
checkbox,
|
|
1313
|
+
switch: switchProps,
|
|
1314
|
+
radioValue,
|
|
1315
|
+
checked,
|
|
1316
|
+
onChange,
|
|
1208
1317
|
children,
|
|
1209
1318
|
isTouchable,
|
|
1210
1319
|
touchableAriaLabel,
|
|
@@ -1239,6 +1348,29 @@ const CardTouchable = ({
|
|
|
1239
1348
|
</div>
|
|
1240
1349
|
);
|
|
1241
1350
|
|
|
1351
|
+
if (checkbox || switchProps || radioValue !== undefined) {
|
|
1352
|
+
return (
|
|
1353
|
+
<CardWithControl
|
|
1354
|
+
maybe
|
|
1355
|
+
{...(checkbox
|
|
1356
|
+
? {checkbox}
|
|
1357
|
+
: switchProps
|
|
1358
|
+
? {switch: switchProps}
|
|
1359
|
+
: {radioValue: radioValue ?? ''})}
|
|
1360
|
+
checked={checked}
|
|
1361
|
+
onChange={onChange}
|
|
1362
|
+
aria-label={touchableAriaLabel}
|
|
1363
|
+
aria-labelledby={ariaLabeledByProp}
|
|
1364
|
+
aria-description={ariaDescriptionProp}
|
|
1365
|
+
aria-describedby={ariaDescribedByProp}
|
|
1366
|
+
className={classnames(styles.touchable, styles.touchableContainer)}
|
|
1367
|
+
style={contentRadiusStyle}
|
|
1368
|
+
>
|
|
1369
|
+
{content}
|
|
1370
|
+
</CardWithControl>
|
|
1371
|
+
);
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1242
1374
|
if (isTouchable && segregateTouchableContent) {
|
|
1243
1375
|
return hasTouchableInContent ? (
|
|
1244
1376
|
<div className={classnames(styles.touchable, styles.touchableContainer)}>{content}</div>
|
|
@@ -1287,7 +1419,7 @@ const replaceRgbaWithColor = (stringWithRgbaColors: string, newColor: string): s
|
|
|
1287
1419
|
return stringWithRgbaColors.replace(RGBA_REGEX, (_, alpha) => applyAlpha(newColor, parseFloat(alpha)));
|
|
1288
1420
|
};
|
|
1289
1421
|
|
|
1290
|
-
export const InternalCard = React.forwardRef<HTMLDivElement,
|
|
1422
|
+
export const InternalCard = React.forwardRef<HTMLDivElement, CardInteractionProps<CardProps>>(
|
|
1291
1423
|
(
|
|
1292
1424
|
{
|
|
1293
1425
|
type,
|
|
@@ -1341,17 +1473,32 @@ export const InternalCard = React.forwardRef<HTMLDivElement, MaybeTouchableCard<
|
|
|
1341
1473
|
videoLoop,
|
|
1342
1474
|
videoAutoPlay,
|
|
1343
1475
|
videoDataAttributes,
|
|
1476
|
+
selected,
|
|
1477
|
+
checkbox,
|
|
1478
|
+
switch: switchProps,
|
|
1479
|
+
radioValue,
|
|
1344
1480
|
segregateTouchableContent = false,
|
|
1345
1481
|
touchableAriaLabel: touchableAriaLabelProp,
|
|
1346
1482
|
...touchableProps
|
|
1347
1483
|
},
|
|
1348
1484
|
ref
|
|
1349
1485
|
): JSX.Element => {
|
|
1486
|
+
const {isChecked, toggleChecked} = useControlState(switchProps || checkbox);
|
|
1487
|
+
const radioContext = useRadioContext();
|
|
1488
|
+
const isSelectable = !!switchProps || !!checkbox || radioValue !== undefined;
|
|
1489
|
+
const isSelected =
|
|
1490
|
+
switchProps || checkbox
|
|
1491
|
+
? isChecked
|
|
1492
|
+
: radioValue !== undefined
|
|
1493
|
+
? radioContext.selectedValue === radioValue
|
|
1494
|
+
: selected;
|
|
1350
1495
|
const {text: slotText, ref: slotRef} = useInnerText();
|
|
1351
1496
|
const {text: headlineText, ref: headlineRef} = useInnerText();
|
|
1352
1497
|
const touchableContentRef = React.useRef<TouchableElement>(null);
|
|
1353
|
-
const isTouchable =
|
|
1498
|
+
const isTouchable =
|
|
1499
|
+
isSelectable || !!(touchableProps.href || touchableProps.to || touchableProps.onPress);
|
|
1354
1500
|
const hasTouchableInContent = !!(
|
|
1501
|
+
!isSelectable &&
|
|
1355
1502
|
segregateTouchableContent &&
|
|
1356
1503
|
!touchableAriaLabelProp &&
|
|
1357
1504
|
(title || pretitle || headline || subtitle || description)
|
|
@@ -1379,7 +1526,7 @@ export const InternalCard = React.forwardRef<HTMLDivElement, MaybeTouchableCard<
|
|
|
1379
1526
|
|
|
1380
1527
|
const shouldShowVideo = hasMediaVideo || hasBackgroundVideo;
|
|
1381
1528
|
const {video, videoAction} = useVideoWithControls({
|
|
1382
|
-
src: shouldShowVideo ? videoSrc : undefined,
|
|
1529
|
+
src: shouldShowVideo && !isSelectable ? videoSrc : undefined,
|
|
1383
1530
|
poster: imageSrc,
|
|
1384
1531
|
ref: videoRef,
|
|
1385
1532
|
autoHeight:
|
|
@@ -1407,7 +1554,7 @@ export const InternalCard = React.forwardRef<HTMLDivElement, MaybeTouchableCard<
|
|
|
1407
1554
|
|
|
1408
1555
|
const showButtonsInBody = !shouldShowFooter && hasButtons;
|
|
1409
1556
|
|
|
1410
|
-
const topActionsLengthWithoutVideo = (topActions?.length || 0) + (onClose ? 1 : 0);
|
|
1557
|
+
const topActionsLengthWithoutVideo = isSelectable ? 1 : (topActions?.length || 0) + (onClose ? 1 : 0);
|
|
1411
1558
|
const topActionsLength = videoAction
|
|
1412
1559
|
? topActionsLengthWithoutVideo + 1
|
|
1413
1560
|
: topActionsLengthWithoutVideo;
|
|
@@ -1474,6 +1621,7 @@ export const InternalCard = React.forwardRef<HTMLDivElement, MaybeTouchableCard<
|
|
|
1474
1621
|
dataAttributes={dataAttributes}
|
|
1475
1622
|
ref={ref}
|
|
1476
1623
|
variant={variant}
|
|
1624
|
+
selected={isSelected}
|
|
1477
1625
|
width={width}
|
|
1478
1626
|
height={height}
|
|
1479
1627
|
aspectRatio={aspectRatio}
|
|
@@ -1515,13 +1663,18 @@ export const InternalCard = React.forwardRef<HTMLDivElement, MaybeTouchableCard<
|
|
|
1515
1663
|
)}
|
|
1516
1664
|
|
|
1517
1665
|
<CardTouchable
|
|
1666
|
+
checkbox={checkbox}
|
|
1667
|
+
switch={switchProps}
|
|
1668
|
+
radioValue={radioValue}
|
|
1669
|
+
checked={isChecked}
|
|
1670
|
+
onChange={toggleChecked}
|
|
1518
1671
|
isTouchable={isTouchable}
|
|
1519
1672
|
touchableAriaLabel={touchableAriaLabel}
|
|
1520
1673
|
ariaLabeledByProp={ariaLabeledByProp}
|
|
1521
1674
|
ariaDescriptionProp={ariaDescriptionProp}
|
|
1522
1675
|
ariaDescribedByProp={ariaDescribedByProp}
|
|
1523
1676
|
touchableProps={touchableProps}
|
|
1524
|
-
segregateTouchableContent={segregateTouchableContent}
|
|
1677
|
+
segregateTouchableContent={isSelectable ? false : segregateTouchableContent}
|
|
1525
1678
|
hasTouchableInContent={hasTouchableInContent}
|
|
1526
1679
|
overlayClassname={overlayStyle}
|
|
1527
1680
|
contentStyle={{
|
|
@@ -1558,7 +1711,7 @@ export const InternalCard = React.forwardRef<HTMLDivElement, MaybeTouchableCard<
|
|
|
1558
1711
|
/>
|
|
1559
1712
|
)}
|
|
1560
1713
|
<div
|
|
1561
|
-
aria-hidden={isTouchable && !segregateTouchableContent}
|
|
1714
|
+
aria-hidden={isTouchable && !isSelectable && !segregateTouchableContent}
|
|
1562
1715
|
data-testid="body"
|
|
1563
1716
|
className={classnames(styles.touchable, {
|
|
1564
1717
|
[styles.containerPaddingTopVariants[size]]:
|
package/src/card-media.tsx
CHANGED
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
CardActionButtonSecondary,
|
|
11
11
|
CardAspectRatio,
|
|
12
12
|
CardSize,
|
|
13
|
-
|
|
13
|
+
CardInteractionProps,
|
|
14
14
|
MediaAspectRatio,
|
|
15
15
|
SlotAlignment,
|
|
16
16
|
TopActionsArray,
|
|
@@ -73,8 +73,8 @@ export type MediaCardProps = {
|
|
|
73
73
|
footerDivider?: boolean;
|
|
74
74
|
};
|
|
75
75
|
|
|
76
|
-
export const MediaCard = React.forwardRef<HTMLDivElement,
|
|
77
|
-
({size = 'default', slot,
|
|
76
|
+
export const MediaCard = React.forwardRef<HTMLDivElement, CardInteractionProps<MediaCardProps>>(
|
|
77
|
+
({size = 'default', slot, buttonPrimary, dataAttributes, ...rest}, ref) => {
|
|
78
78
|
return (
|
|
79
79
|
<InternalCard
|
|
80
80
|
type="media"
|
|
@@ -85,7 +85,6 @@ export const MediaCard = React.forwardRef<HTMLDivElement, MaybeTouchableCard<Med
|
|
|
85
85
|
...dataAttributes,
|
|
86
86
|
}}
|
|
87
87
|
slot={slot}
|
|
88
|
-
topActions={topActions}
|
|
89
88
|
buttonPrimary={buttonPrimary}
|
|
90
89
|
ref={ref}
|
|
91
90
|
{...rest}
|
package/src/card-naked.tsx
CHANGED
|
@@ -4,12 +4,12 @@ import {InternalCard} from './card-internal';
|
|
|
4
4
|
import {useThemeVariant} from './theme-variant-context';
|
|
5
5
|
|
|
6
6
|
import type {MediaCardProps} from './card-media';
|
|
7
|
-
import type {
|
|
7
|
+
import type {CardInteractionProps} from './card-internal';
|
|
8
8
|
|
|
9
9
|
type NakedCardProps = Omit<MediaCardProps, 'footerBackgroundColor' | 'variant' | 'footerVariant'>;
|
|
10
10
|
|
|
11
|
-
export const NakedCard = React.forwardRef<HTMLDivElement,
|
|
12
|
-
({size = 'default', slot,
|
|
11
|
+
export const NakedCard = React.forwardRef<HTMLDivElement, CardInteractionProps<NakedCardProps>>(
|
|
12
|
+
({size = 'default', slot, buttonPrimary, dataAttributes, ...rest}, ref) => {
|
|
13
13
|
const variant = useThemeVariant();
|
|
14
14
|
return (
|
|
15
15
|
<InternalCard
|
|
@@ -21,7 +21,6 @@ export const NakedCard = React.forwardRef<HTMLDivElement, MaybeTouchableCard<Nak
|
|
|
21
21
|
type="naked"
|
|
22
22
|
size={size}
|
|
23
23
|
slot={slot}
|
|
24
|
-
topActions={topActions}
|
|
25
24
|
buttonPrimary={buttonPrimary}
|
|
26
25
|
ref={ref}
|
|
27
26
|
{...rest}
|
package/src/checkbox.tsx
CHANGED
|
@@ -86,6 +86,7 @@ type RenderProps = {
|
|
|
86
86
|
'aria-label'?: string;
|
|
87
87
|
role?: 'checkbox' | 'menuitemcheckbox';
|
|
88
88
|
dataAttributes?: DataAttributes;
|
|
89
|
+
className?: string;
|
|
89
90
|
};
|
|
90
91
|
|
|
91
92
|
type ChildrenProps = {
|
|
@@ -101,6 +102,7 @@ type ChildrenProps = {
|
|
|
101
102
|
'aria-labelledby'?: string;
|
|
102
103
|
role?: 'checkbox' | 'menuitemcheckbox';
|
|
103
104
|
dataAttributes?: DataAttributes;
|
|
105
|
+
className?: string;
|
|
104
106
|
};
|
|
105
107
|
|
|
106
108
|
const Checkbox = React.forwardRef<HTMLDivElement, RenderProps | ChildrenProps>((props, ref) => {
|
|
@@ -154,7 +156,10 @@ const Checkbox = React.forwardRef<HTMLDivElement, RenderProps | ChildrenProps>((
|
|
|
154
156
|
}}
|
|
155
157
|
tabIndex={disabled ? undefined : 0}
|
|
156
158
|
ref={combineRefs(ref, focusableRef)}
|
|
157
|
-
className={
|
|
159
|
+
className={classnames(
|
|
160
|
+
disabled ? styles.checkboxContainerDisabled : styles.checkboxContainer,
|
|
161
|
+
props.className
|
|
162
|
+
)}
|
|
158
163
|
aria-label={ariaLabel}
|
|
159
164
|
aria-labelledby={ariaLabel ? undefined : labelId}
|
|
160
165
|
aria-disabled={disabled}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {openStoryPage, screen} from '../../test-utils';
|
|
2
|
+
|
|
3
|
+
const expectAiCardSnapshot = async () => {
|
|
4
|
+
const element = await screen.findByTestId('ai-card');
|
|
5
|
+
const image = await element.screenshot();
|
|
6
|
+
|
|
7
|
+
expect(image).toMatchImageSnapshot();
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
test('AiCard static text', async () => {
|
|
11
|
+
await openStoryPage({
|
|
12
|
+
id: 'community-vivo-aicard--default',
|
|
13
|
+
device: 'MOBILE_IOS',
|
|
14
|
+
args: {text: 'Lorem ipsum '},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
await expectAiCardSnapshot();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('AiCard line break at chars', async () => {
|
|
21
|
+
await openStoryPage({
|
|
22
|
+
id: 'community-vivo-aicard--default',
|
|
23
|
+
device: 'MOBILE_IOS',
|
|
24
|
+
args: {text: 'Lorem ipsum ', lineBreakAtChars: 8},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
await expectAiCardSnapshot();
|
|
28
|
+
});
|
|
@@ -15,6 +15,7 @@ export default {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
type Args = {
|
|
18
|
+
selected?: boolean;
|
|
18
19
|
headlineType: TagType;
|
|
19
20
|
headline: string;
|
|
20
21
|
pretitle: string;
|
|
@@ -33,6 +34,7 @@ type Args = {
|
|
|
33
34
|
};
|
|
34
35
|
|
|
35
36
|
export const Default: StoryComponent<Args> = ({
|
|
37
|
+
selected,
|
|
36
38
|
headlineType,
|
|
37
39
|
headline,
|
|
38
40
|
pretitle,
|
|
@@ -77,6 +79,7 @@ export const Default: StoryComponent<Args> = ({
|
|
|
77
79
|
|
|
78
80
|
return (
|
|
79
81
|
<AdvancedDataCard
|
|
82
|
+
selected={selected}
|
|
80
83
|
dataAttributes={{testid: 'advanced-data-card'}}
|
|
81
84
|
stackingGroup={
|
|
82
85
|
stackingGroup ? (
|
|
@@ -113,6 +116,7 @@ export const Default: StoryComponent<Args> = ({
|
|
|
113
116
|
|
|
114
117
|
Default.storyName = 'Advanced Data Card';
|
|
115
118
|
Default.args = {
|
|
119
|
+
selected: undefined,
|
|
116
120
|
headlineType: 'promo',
|
|
117
121
|
headline: 'headline',
|
|
118
122
|
pretitle: 'pretitle',
|
|
@@ -130,6 +134,7 @@ Default.args = {
|
|
|
130
134
|
noSlotDivider: false,
|
|
131
135
|
};
|
|
132
136
|
Default.argTypes = {
|
|
137
|
+
selected: {type: 'boolean', options: [undefined, true, false], control: {type: 'select'}},
|
|
133
138
|
headlineType: {
|
|
134
139
|
options: ['promo', 'active', 'inactive', 'success', 'warning', 'error', 'info'],
|
|
135
140
|
control: {type: 'select'},
|
|
@@ -38,3 +38,6 @@ import {AdvancedDataCard} from '../advanced-data-card';
|
|
|
38
38
|
<AdvancedDataCard title="title" onPress={() => {}} to="/" />;
|
|
39
39
|
// @ts-expect-error href and to can't be used together
|
|
40
40
|
<AdvancedDataCard title="title" href="/" to="/" />;
|
|
41
|
+
|
|
42
|
+
<AdvancedDataCard title="Selected" selected />;
|
|
43
|
+
<AdvancedDataCard title="Unselected" selected={false} />;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {createVar, style, styleVariants} from '@vanilla-extract/css';
|
|
1
|
+
import {createVar, style, styleVariants, globalStyle} from '@vanilla-extract/css';
|
|
2
2
|
import {sprinkles} from '../sprinkles.css';
|
|
3
3
|
import * as mq from '../media-queries.css';
|
|
4
4
|
import {vars as skinVars} from '../skins/skin-contract.css';
|
|
@@ -269,3 +269,28 @@ export const flexColumn = style({
|
|
|
269
269
|
display: 'flex',
|
|
270
270
|
flexDirection: 'column',
|
|
271
271
|
});
|
|
272
|
+
|
|
273
|
+
export const selectionTouchable = style({
|
|
274
|
+
borderRadius: `calc(${skinVars.borderRadii.container} - 1px)`,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Draw the focus ring inside Boxed to prevent its overflow from clipping the native outline.
|
|
278
|
+
globalStyle(`${selectionTouchable} > div:focus-visible`, {outline: 'none'});
|
|
279
|
+
globalStyle(`${selectionTouchable} > div:focus-visible::before`, {
|
|
280
|
+
content: '""',
|
|
281
|
+
position: 'absolute',
|
|
282
|
+
inset: 0,
|
|
283
|
+
border: '1.5px solid',
|
|
284
|
+
borderColor: skinVars.colors.controlActivated,
|
|
285
|
+
borderRadius: 'inherit',
|
|
286
|
+
pointerEvents: 'none',
|
|
287
|
+
zIndex: 2,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
const switchWidth = {default: 42, ios: 51};
|
|
291
|
+
const controlSpacing = 16;
|
|
292
|
+
|
|
293
|
+
export const switchSpace = styleVariants({
|
|
294
|
+
default: {width: switchWidth.default + controlSpacing, flexShrink: 0},
|
|
295
|
+
ios: {width: switchWidth.ios + controlSpacing, flexShrink: 0},
|
|
296
|
+
});
|