From 7d66670d69c96440dae22b5d48e7687223f1228a Mon Sep 17 00:00:00 2001 From: Antonin Date: Mon, 7 Oct 2024 10:19:05 +0200 Subject: [PATCH] Fixs upload profile picture (#876) --- src/components/Button/Button.tsx | 16 ++- .../ModalOrPageBase/ModalOrPageBase.tsx | 3 +- .../ProfileCard/MyProfileCard.tsx | 5 +- .../ProfilePicture/ProfilePicture.tsx | 26 +++- src/components/base/Dropdown/index.tsx | 24 ++-- .../profil/dashboard/components/CropImg.tsx | 129 ++++++++---------- .../components/ProfilBlock/index.tsx | 27 ++-- tamagui-web.css | 35 +++++ themes.ts | 18 +++ 9 files changed, 179 insertions(+), 104 deletions(-) diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 6a44e8e45..809f3b9db 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -83,6 +83,10 @@ const ContainedFrame = styled(ButtonFrameStyled, { name: 'VoxButtonContained', }) +const InverseContainedFrame = styled(ButtonFrameStyled, { + name: 'VoxButtonInverseContained', +}) + const OutlinedFrame = styled(ButtonFrameStyled, { name: 'VoxButtonOutlined', borderColor: '$borderColor', @@ -100,7 +104,7 @@ const SoftFrame = styled(ButtonFrameStyled, { name: 'VoxButtonSoft', }) -const getFrame = (variant?: 'outlined' | 'text' | 'soft' | 'contained') => { +const getFrame = (variant?: 'outlined' | 'text' | 'soft' | 'contained', inverse?: boolean) => { switch (variant) { case 'outlined': return OutlinedFrame @@ -110,12 +114,16 @@ const getFrame = (variant?: 'outlined' | 'text' | 'soft' | 'contained') => { return SoftFrame case 'contained': default: - return ContainedFrame + return inverse ? InverseContainedFrame : ContainedFrame } } -const ButtonFrame = ({ variant, ...props }: React.ComponentProps & { variant?: 'outlined' | 'text' | 'soft' | 'contained' }) => { - const Frame = getFrame(variant) +const ButtonFrame = ({ + variant, + inverse, + ...props +}: React.ComponentProps & { variant?: 'outlined' | 'text' | 'soft' | 'contained'; inverse?: boolean }) => { + const Frame = getFrame(variant, inverse) return } diff --git a/src/components/ModalOrPageBase/ModalOrPageBase.tsx b/src/components/ModalOrPageBase/ModalOrPageBase.tsx index dd783d08f..9b40a35c0 100644 --- a/src/components/ModalOrPageBase/ModalOrPageBase.tsx +++ b/src/components/ModalOrPageBase/ModalOrPageBase.tsx @@ -98,10 +98,11 @@ const styles = StyleSheet.create({ }, modalView: { backgroundColor: 'white', - borderRadius: 20, + borderRadius: 32, margin: Spacing.largeMargin, alignItems: 'center', cursor: 'auto', + overflow: 'hidden', shadowColor: '#000', shadowOffset: { width: 0, diff --git a/src/components/ProfileCards/ProfileCard/MyProfileCard.tsx b/src/components/ProfileCards/ProfileCard/MyProfileCard.tsx index fc7f4528a..b11d785d5 100644 --- a/src/components/ProfileCards/ProfileCard/MyProfileCard.tsx +++ b/src/components/ProfileCards/ProfileCard/MyProfileCard.tsx @@ -11,7 +11,10 @@ export default function MyProfileCard() { const { session } = useSession() const { user: credentials } = useUserStore() const profile = user?.data - const onNavigateToCadre = useCallback(() => openURL(`${credentials?.isAdmin ? clientEnv.ADMIN_URL : clientEnv.OAUTH_BASE_URL}${profile.cadre_auth_path}`), []) + const onNavigateToCadre = useCallback( + () => openURL(`${credentials?.isAdmin ? clientEnv.ADMIN_URL : clientEnv.OAUTH_BASE_URL}${profile?.cadre_auth_path}`), + [profile], + ) if (!profile) { return null diff --git a/src/components/ProfilePicture/ProfilePicture.tsx b/src/components/ProfilePicture/ProfilePicture.tsx index cb3f27e4c..3486d93c3 100644 --- a/src/components/ProfilePicture/ProfilePicture.tsx +++ b/src/components/ProfilePicture/ProfilePicture.tsx @@ -1,7 +1,9 @@ +import { ComponentPropsWithRef } from 'react' import { Platform } from 'react-native' import { GetThemeValueForKey } from '@tamagui/web' +import { BlurView } from 'expo-blur' import { Image } from 'expo-image' -import { Circle, CircleProps, getTokenValue, Spinner, Square, SquareProps, Token } from 'tamagui' +import { Circle, CircleProps, getTokenValue, Spinner, Square, SquareProps, Token, YStack, ZStack } from 'tamagui' import Text from '../base/Text' type ProfilePictureProps = { @@ -32,29 +34,41 @@ const ProfilePicture = (props: ProfilePictureProps) => { .join('') const Shape = rounded ? Circle : Square - const sizeValue = getTokenValue(size, 'size') const content = src ? ( {alt} ) : ( - + {initials} ) return ( - {props.loading ? : content} + + + {content} + + {props.loading ? ( + + + + ) : null} + ) } -export default ProfilePicture +export default (props: ComponentPropsWithRef) => diff --git a/src/components/base/Dropdown/index.tsx b/src/components/base/Dropdown/index.tsx index d8b559c50..4763150a0 100644 --- a/src/components/base/Dropdown/index.tsx +++ b/src/components/base/Dropdown/index.tsx @@ -1,4 +1,4 @@ -import React, { NamedExoticComponent, useCallback } from 'react' +import React, { NamedExoticComponent, useCallback, useEffect } from 'react' import { FlatList, Modal, TouchableOpacity } from 'react-native' import Text from '@/components/base/Text' import { useLazyRef } from '@/hooks/useLazyRef' @@ -76,8 +76,8 @@ const DropdownFrame = styled(ThemeableStack, { backgroundColor: 'white', borderRadius: 16, overflow: 'hidden', - elevation: 2, - shadowColor: '$gray6', + elevation: 1, + shadowColor: '$gray1', borderWidth: 1, borderColor: '$textOutline', variants: { @@ -119,17 +119,21 @@ function Dropdown({ items, onSelect, value, ...props }: DropdownProps) { ) } -export function DropdownWrapper({ children, onSelect, ...props }: DropdownProps & { children: React.ReactNode }) { - const [open, setOpen] = React.useState(false) +export function DropdownWrapper({ + children, + onSelect, + ...props +}: DropdownProps & { children: React.ReactNode; open?: boolean; onOpenChange?: (x: boolean) => void }) { + const open = props.open ?? false + const setOpen = props.onOpenChange ?? (() => {}) const container = React.useRef(null) const [dropdownTop, setDropdownTop] = React.useState(0) - const handleOpen = useCallback(() => { - if (!container.current) return + useEffect(() => { + if (!container.current || !props.open) return container.current.measure((_fx, _fy, _w, h, _px, py) => { setDropdownTop(py + h) }) - setOpen(true) - }, []) + }, [props.open]) const handleClose = useCallback(() => { setOpen(false) @@ -141,7 +145,7 @@ export function DropdownWrapper({ children, onSelect, ...props }: DropdownProps }, []) return ( - + diff --git a/src/screens/profil/dashboard/components/CropImg.tsx b/src/screens/profil/dashboard/components/CropImg.tsx index db4dadf4d..7fcc64005 100644 --- a/src/screens/profil/dashboard/components/CropImg.tsx +++ b/src/screens/profil/dashboard/components/CropImg.tsx @@ -1,10 +1,11 @@ import React, { ComponentProps, useEffect, useRef, useState } from 'react' -import { Dimensions, StyleSheet, View } from 'react-native' +import { Dimensions, SafeAreaView, StyleSheet, View } from 'react-native' import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler' import Animated, { useAnimatedStyle, useSharedValue } from 'react-native-reanimated' import { VoxButton } from '@/components/Button' import ModalOrPageBase from '@/components/ModalOrPageBase/ModalOrPageBase' import VoxCard from '@/components/VoxCard/VoxCard' +import { useMutation } from '@tanstack/react-query' import { ImageResult, manipulateAsync, SaveFormat } from 'expo-image-manipulator' import { isWeb, styled, ThemeableStack, useMedia, XStack, YStack } from 'tamagui' @@ -12,7 +13,7 @@ const styles = StyleSheet.create({ container: { position: 'relative', overflow: 'hidden', - backgroundColor: 'black', + backgroundColor: '$gray5', }, flex: { flex: 1, @@ -37,36 +38,19 @@ function clamp(val: number, min: number, max: number) { } const CROP_SIZE = 300 -const getMinImgWidthForCrop = (img: ImageResult) => { - const imageMetaData = img - const sizes = { width: imageMetaData.width, height: imageMetaData.height } - const smallerSideLabel = sizes.width > sizes.height ? 'height' : 'width' - const smallerSide = sizes[smallerSideLabel] - if (smallerSide < CROP_SIZE) { - const calcNewWidth = smallerSideLabel === 'width' ? CROP_SIZE : (CROP_SIZE * sizes.width) / sizes.height - const calcNewHeight = smallerSideLabel === 'height' ? CROP_SIZE : (CROP_SIZE * sizes.height) / sizes.width - return { width: calcNewWidth, height: calcNewHeight } - } - return false -} - type Size = { width: number height: number } -const getMaxImgWidth = (img: Size, { width }: Size) => { - let newSize = img - if (img.width > width) { - newSize = { width: width, height: (width * img.height!) / img.width! } - } - if (newSize.height < CROP_SIZE) { - newSize = { width: (CROP_SIZE * newSize.width) / newSize.height, height: CROP_SIZE } - } - return newSize +const getMaxImgWidth = (img: Size) => { + const smallerSideLabel = img.width > img.height ? 'height' : 'width' + const smallerSide = img[smallerSideLabel] + const result = CROP_SIZE / smallerSide + return result } -function ImageCroper(props: { windowSize: { width: number; height: number }; image: ImageResult; onChange: (image: string) => void }) { +function ImageCroper(props: { windowSize: { width: number; height: number }; image: ImageResult; onChange: (image?: string) => void }) { const scale = useSharedValue(1) const startScale = useSharedValue(0) const translationX = useSharedValue(0) @@ -104,24 +88,10 @@ function ImageCroper(props: { windowSize: { width: number; height: number }; ima useEffect(() => { ;(async () => { const image = props.image - const maybeNewSize = getMinImgWidthForCrop(image) - if (maybeNewSize) { - const newImg = await manipulateAsync(image.uri, [ - { - resize: { - width: maybeNewSize.width, - height: maybeNewSize.height, - }, - }, - ]) - originalImage.current = newImg - } else { - originalImage.current = image as ImageResult - } + originalImage.current = image as ImageResult const orignalSize = { width: originalImage.current.width, height: originalImage.current.height } setOriginalSizes(orignalSize) - const showSize = getMaxImgWidth(orignalSize, props.windowSize) - const displaySizeRatio = showSize.width / orignalSize.width + const displaySizeRatio = getMaxImgWidth(orignalSize) scale.value = displaySizeRatio setReady(true) })() @@ -171,29 +141,35 @@ function ImageCroper(props: { windowSize: { width: number; height: number }; ima return { x: x > 0 ? x : 0, y: y > 0 ? y : 0, width: CROP_SIZE / scale.value, height: CROP_SIZE / scale.value } } - const cropImg = () => { - const cropSize = calcCropSize() - const crop = { - originX: cropSize.x, - originY: cropSize.y, - width: cropSize.width, - height: cropSize.height, - } - manipulateAsync( - originalImage.current?.uri || '', - [ - { crop }, - { - resize: { - width: 360, - height: 360, + const { mutate, isPending } = useMutation({ + mutationFn: (cropSize: { x: number; y: number; width: number; height: number }) => { + const crop = { + originX: cropSize.x, + originY: cropSize.y, + width: cropSize.width, + height: cropSize.height, + } + return manipulateAsync( + originalImage.current?.uri || '', + [ + { crop }, + { + resize: { + width: 360, + height: 360, + }, }, - }, - ], - { compress: 0.5, format: SaveFormat.JPEG, base64: true }, - ).then((result) => { + ], + { compress: 0.5, format: SaveFormat.JPEG, base64: true }, + ) + }, + onSuccess: (result) => { props.onChange('data:image/jpeg;base64,' + result.base64!) - }) + }, + }) + + const cropImg = () => { + mutate(calcCropSize()) } const dimentionStyle = media.gtMd ? props.windowSize : {} @@ -214,15 +190,26 @@ function ImageCroper(props: { windowSize: { width: number; height: number }; ima - + - - - Terminé - - + + + props.onChange()} disabled={isPending}> + Annuler + + + Enregistrer + + + @@ -232,15 +219,15 @@ function ImageCroper(props: { windowSize: { width: number; height: number }; ima } const windowSize = Dimensions.get(isWeb ? 'window' : 'screen') -export default function ModalImageCroper(props: { image: ImageResult | null; onClose: (img: string) => void; open: boolean }) { +export default function ModalImageCroper(props: { image: ImageResult | null; onClose: (img?: string) => void; open: boolean }) { const media = useMedia() return ( - } scrollable={false} open={props.open}> + } scrollable={false} open={props.open} onClose={props.onClose}> {props.image ? ( media.md ? ( ) : ( - + { const { mutate: del, isPending: deletePending } = useDeleteProfilPicture({ uuid: props.profil.uuid }) const isPending = postPending || deletePending - const handleCloseCrop = (img: string) => { - mutate(img) + const handleCloseCrop = (img?: string) => { + if (img) mutate(img) setOpenCrop(false) } const pickImage = async () => { @@ -55,11 +55,17 @@ const UploadPP = (props: { profil: RestProfilResponse }) => { } } + const [openDropdown, setOpenDropdown] = useState(false) + + const handleOpenDropdown = () => { + props.profil.image_url ? setOpenDropdown(!openDropdown) : pickImage() + } + return ( - + { fullName={props.profil.first_name + ' ' + props.profil.last_name} src={props.profil.image_url ?? undefined} alt="profile" + onPress={handleOpenDropdown} /> - - {props.profil.image_url ? : } - + onPress={handleOpenDropdown} + iconLeft={props.profil.image_url ? Settings2 : Plus} + /> diff --git a/tamagui-web.css b/tamagui-web.css index 16e2bc7d6..8b8f55fd5 100644 --- a/tamagui-web.css +++ b/tamagui-web.css @@ -206,6 +206,11 @@ body{background:var(--background);color:var(--color)} .t_active_VoxButtonContained, .t_dark .t_light .t_active_VoxButtonContained, .t_dark .t_light .t_pink_active_VoxButtonContained, .t_dark .t_light .t_pink_VoxButtonContained, .t_dark .t_light .t_red_active_VoxButtonContained, .t_dark .t_light .t_red_VoxButtonContained, .t_dark .t_light .t_VoxButtonContained, .t_pink_active_VoxButtonContained, .t_pink_VoxButtonContained, .t_red_active_VoxButtonContained, .t_red_VoxButtonContained, .t_VoxButtonContained {--background:var(--color-12);--backgroundHover:var(--color-13);--backgroundPress:var(--color-14);--color:hsl(0, 0%, 100%);--colorPress:hsl(0, 0%, 100%);--colorHover:hsl(0, 0%, 100%);} } +:root.t_dark .t_light .t_active_VoxButtonInverseContained, :root.t_dark .t_light .t_dark .t_light .t_active_VoxButtonInverseContained, :root.t_dark .t_light .t_dark .t_light .t_pink_active_VoxButtonInverseContained, :root.t_dark .t_light .t_dark .t_light .t_pink_VoxButtonInverseContained, :root.t_dark .t_light .t_dark .t_light .t_red_active_VoxButtonInverseContained, :root.t_dark .t_light .t_dark .t_light .t_red_VoxButtonInverseContained, :root.t_dark .t_light .t_dark .t_light .t_VoxButtonInverseContained, :root.t_dark .t_light .t_pink_active_VoxButtonInverseContained, :root.t_dark .t_light .t_pink_VoxButtonInverseContained, :root.t_dark .t_light .t_red_active_VoxButtonInverseContained, :root.t_dark .t_light .t_red_VoxButtonInverseContained, :root.t_dark .t_light .t_VoxButtonInverseContained, :root.t_light .t_active_VoxButtonInverseContained, :root.t_light .t_dark .t_light .t_active_VoxButtonInverseContained, :root.t_light .t_dark .t_light .t_pink_active_VoxButtonInverseContained, :root.t_light .t_dark .t_light .t_pink_VoxButtonInverseContained, :root.t_light .t_dark .t_light .t_red_active_VoxButtonInverseContained, :root.t_light .t_dark .t_light .t_red_VoxButtonInverseContained, :root.t_light .t_dark .t_light .t_VoxButtonInverseContained, :root.t_light .t_pink_active_VoxButtonInverseContained, :root.t_light .t_pink_VoxButtonInverseContained, :root.t_light .t_red_active_VoxButtonInverseContained, :root.t_light .t_red_VoxButtonInverseContained, :root.t_light .t_VoxButtonInverseContained {--background:hsl(0, 0%, 100%);--backgroundHover:hsl(0, 0%, 100%);--backgroundPress:hsl(0, 0%, 97%);--color:hsl(211,24%, 17%);--colorPress:hsl(211,24%, 17%);--colorHover:hsl(211,24%, 17%);} +@media(prefers-color-scheme:light){ + body{background:var(--background);color:var(--color)} + .t_active_VoxButtonInverseContained, .t_dark .t_light .t_active_VoxButtonInverseContained, .t_dark .t_light .t_pink_active_VoxButtonInverseContained, .t_dark .t_light .t_pink_VoxButtonInverseContained, .t_dark .t_light .t_red_active_VoxButtonInverseContained, .t_dark .t_light .t_red_VoxButtonInverseContained, .t_dark .t_light .t_VoxButtonInverseContained, .t_pink_active_VoxButtonInverseContained, .t_pink_VoxButtonInverseContained, .t_red_active_VoxButtonInverseContained, .t_red_VoxButtonInverseContained, .t_VoxButtonInverseContained {--background:hsl(0, 0%, 100%);--backgroundHover:hsl(0, 0%, 100%);--backgroundPress:hsl(0, 0%, 97%);--color:hsl(211,24%, 17%);--colorPress:hsl(211,24%, 17%);--colorHover:hsl(211,24%, 17%);} + } :root.t_dark .t_light .t_active_VoxButtonSoft, :root.t_dark .t_light .t_dark .t_light .t_active_VoxButtonSoft, :root.t_dark .t_light .t_dark .t_light .t_pink_active_VoxButtonSoft, :root.t_dark .t_light .t_dark .t_light .t_pink_VoxButtonSoft, :root.t_dark .t_light .t_dark .t_light .t_red_active_VoxButtonSoft, :root.t_dark .t_light .t_dark .t_light .t_red_VoxButtonSoft, :root.t_dark .t_light .t_dark .t_light .t_VoxButtonSoft, :root.t_dark .t_light .t_pink_active_VoxButtonSoft, :root.t_dark .t_light .t_pink_VoxButtonSoft, :root.t_dark .t_light .t_red_active_VoxButtonSoft, :root.t_dark .t_light .t_red_VoxButtonSoft, :root.t_dark .t_light .t_VoxButtonSoft, :root.t_light .t_active_VoxButtonSoft, :root.t_light .t_dark .t_light .t_active_VoxButtonSoft, :root.t_light .t_dark .t_light .t_pink_active_VoxButtonSoft, :root.t_light .t_dark .t_light .t_pink_VoxButtonSoft, :root.t_light .t_dark .t_light .t_red_active_VoxButtonSoft, :root.t_light .t_dark .t_light .t_red_VoxButtonSoft, :root.t_light .t_dark .t_light .t_VoxButtonSoft, :root.t_light .t_pink_active_VoxButtonSoft, :root.t_light .t_pink_VoxButtonSoft, :root.t_light .t_red_active_VoxButtonSoft, :root.t_light .t_red_VoxButtonSoft, :root.t_light .t_VoxButtonSoft {--background:hsl(0, 0%, 100%);--backgroundHover:hsl(0, 0%, 100%);--backgroundPress:hsl(0, 0%, 97%);--color:var(--color-15);--colorPress:var(--color-15);--colorHover:var(--color-15);--colorPop:var(--color-16);} @media(prefers-color-scheme:light){ body{background:var(--background);color:var(--color)} @@ -271,6 +276,11 @@ body{background:var(--background);color:var(--color)} .t_dark .t_light .t_orange_active_VoxButtonContained, .t_dark .t_light .t_orange_VoxButtonContained, .t_orange_active_VoxButtonContained, .t_orange_VoxButtonContained {--background:hsl(14, 60%, 56%);--backgroundHover:hsl(14, 56%, 51%);--backgroundPress:hsl(14, 55%, 41%);--color:hsl(0, 0%, 100%);--colorPress:hsl(0, 0%, 100%);--colorHover:hsl(0, 0%, 100%);} } +:root.t_dark .t_light .t_dark .t_light .t_orange_active_VoxButtonInverseContained, :root.t_dark .t_light .t_dark .t_light .t_orange_VoxButtonInverseContained, :root.t_dark .t_light .t_orange_active_VoxButtonInverseContained, :root.t_dark .t_light .t_orange_VoxButtonInverseContained, :root.t_light .t_dark .t_light .t_orange_active_VoxButtonInverseContained, :root.t_light .t_dark .t_light .t_orange_VoxButtonInverseContained, :root.t_light .t_orange_active_VoxButtonInverseContained, :root.t_light .t_orange_VoxButtonInverseContained {--background:hsl(0, 0%, 100%);--backgroundHover:hsl(354, 78%, 95%);--backgroundPress:hsl(355, 86%, 92%);--color:hsl(211,24%, 17%);--colorPress:hsl(211,24%, 17%);--colorHover:hsl(211,24%, 17%);} +@media(prefers-color-scheme:light){ + body{background:var(--background);color:var(--color)} + .t_dark .t_light .t_orange_active_VoxButtonInverseContained, .t_dark .t_light .t_orange_VoxButtonInverseContained, .t_orange_active_VoxButtonInverseContained, .t_orange_VoxButtonInverseContained {--background:hsl(0, 0%, 100%);--backgroundHover:hsl(354, 78%, 95%);--backgroundPress:hsl(355, 86%, 92%);--color:hsl(211,24%, 17%);--colorPress:hsl(211,24%, 17%);--colorHover:hsl(211,24%, 17%);} + } :root.t_dark .t_light .t_dark .t_light .t_orange_active_VoxButtonSoft, :root.t_dark .t_light .t_dark .t_light .t_orange_VoxButtonSoft, :root.t_dark .t_light .t_orange_active_VoxButtonSoft, :root.t_dark .t_light .t_orange_VoxButtonSoft, :root.t_light .t_dark .t_light .t_orange_active_VoxButtonSoft, :root.t_light .t_dark .t_light .t_orange_VoxButtonSoft, :root.t_light .t_orange_active_VoxButtonSoft, :root.t_light .t_orange_VoxButtonSoft {--background:hsl(356, 100%, 97%);--backgroundHover:hsl(354, 78%, 95%);--backgroundPress:hsl(355, 86%, 92%);--color:hsl(14, 68%, 34%);--colorPress:hsl(14, 68%, 34%);--colorHover:hsl(14, 68%, 34%);--colorPop:hsl(14, 93%, 61%);} @media(prefers-color-scheme:light){ body{background:var(--background);color:var(--color)} @@ -336,6 +346,11 @@ body{background:var(--background);color:var(--color)} .t_dark .t_light .t_yellow_active_VoxButtonContained, .t_dark .t_light .t_yellow_VoxButtonContained, .t_yellow_active_VoxButtonContained, .t_yellow_VoxButtonContained {--background:hsl(39, 59%, 49%);--backgroundHover:hsl(39, 64%, 46%);--backgroundPress:hsl(40, 65%, 34%);--color:hsl(0, 0%, 100%);--colorPress:hsl(0, 0%, 100%);--colorHover:hsl(0, 0%, 100%);} } +:root.t_dark .t_light .t_dark .t_light .t_yellow_active_VoxButtonInverseContained, :root.t_dark .t_light .t_dark .t_light .t_yellow_VoxButtonInverseContained, :root.t_dark .t_light .t_yellow_active_VoxButtonInverseContained, :root.t_dark .t_light .t_yellow_VoxButtonInverseContained, :root.t_light .t_dark .t_light .t_yellow_active_VoxButtonInverseContained, :root.t_light .t_dark .t_light .t_yellow_VoxButtonInverseContained, :root.t_light .t_yellow_active_VoxButtonInverseContained, :root.t_light .t_yellow_VoxButtonInverseContained {--background:hsl(0, 0%, 100%);--backgroundHover:hsl(37, 66%, 88%);--backgroundPress:hsl(36, 60%, 82%);--color:hsl(211,24%, 17%);--colorPress:hsl(211,24%, 17%);--colorHover:hsl(211,24%, 17%);} +@media(prefers-color-scheme:light){ + body{background:var(--background);color:var(--color)} + .t_dark .t_light .t_yellow_active_VoxButtonInverseContained, .t_dark .t_light .t_yellow_VoxButtonInverseContained, .t_yellow_active_VoxButtonInverseContained, .t_yellow_VoxButtonInverseContained {--background:hsl(0, 0%, 100%);--backgroundHover:hsl(37, 66%, 88%);--backgroundPress:hsl(36, 60%, 82%);--color:hsl(211,24%, 17%);--colorPress:hsl(211,24%, 17%);--colorHover:hsl(211,24%, 17%);} + } :root.t_dark .t_light .t_dark .t_light .t_yellow_active_VoxButtonSoft, :root.t_dark .t_light .t_dark .t_light .t_yellow_VoxButtonSoft, :root.t_dark .t_light .t_yellow_active_VoxButtonSoft, :root.t_dark .t_light .t_yellow_VoxButtonSoft, :root.t_light .t_dark .t_light .t_yellow_active_VoxButtonSoft, :root.t_light .t_dark .t_light .t_yellow_VoxButtonSoft, :root.t_light .t_yellow_active_VoxButtonSoft, :root.t_light .t_yellow_VoxButtonSoft {--background:hsl(36, 100%, 96%);--backgroundHover:hsl(37, 66%, 88%);--backgroundPress:hsl(36, 60%, 82%);--color:hsl(40, 76%, 28%);--colorPress:hsl(40, 76%, 28%);--colorHover:hsl(40, 76%, 28%);--colorPop:hsl(43, 95%, 57%);} @media(prefers-color-scheme:light){ body{background:var(--background);color:var(--color)} @@ -401,6 +416,11 @@ body{background:var(--background);color:var(--color)} .t_dark .t_light .t_green_active_VoxButtonContained, .t_dark .t_light .t_green_VoxButtonContained, .t_green_active_VoxButtonContained, .t_green_VoxButtonContained {--background:hsl(146, 27%, 45%);--backgroundHover:hsl(146, 33%, 39%);--backgroundPress:hsl(146, 31%, 30%);--color:hsl(0, 0%, 100%);--colorPress:hsl(0, 0%, 100%);--colorHover:hsl(0, 0%, 100%);} } +:root.t_dark .t_light .t_dark .t_light .t_green_active_VoxButtonInverseContained, :root.t_dark .t_light .t_dark .t_light .t_green_VoxButtonInverseContained, :root.t_dark .t_light .t_green_active_VoxButtonInverseContained, :root.t_dark .t_light .t_green_VoxButtonInverseContained, :root.t_light .t_dark .t_light .t_green_active_VoxButtonInverseContained, :root.t_light .t_dark .t_light .t_green_VoxButtonInverseContained, :root.t_light .t_green_active_VoxButtonInverseContained, :root.t_light .t_green_VoxButtonInverseContained {--background:hsl(0, 0%, 100%);--backgroundHover:hsl(120, 50%, 91%);--backgroundPress:hsl(120, 46%, 85%);--color:hsl(211,24%, 17%);--colorPress:hsl(211,24%, 17%);--colorHover:hsl(211,24%, 17%);} +@media(prefers-color-scheme:light){ + body{background:var(--background);color:var(--color)} + .t_dark .t_light .t_green_active_VoxButtonInverseContained, .t_dark .t_light .t_green_VoxButtonInverseContained, .t_green_active_VoxButtonInverseContained, .t_green_VoxButtonInverseContained {--background:hsl(0, 0%, 100%);--backgroundHover:hsl(120, 50%, 91%);--backgroundPress:hsl(120, 46%, 85%);--color:hsl(211,24%, 17%);--colorPress:hsl(211,24%, 17%);--colorHover:hsl(211,24%, 17%);} + } :root.t_dark .t_light .t_dark .t_light .t_green_active_VoxButtonSoft, :root.t_dark .t_light .t_dark .t_light .t_green_VoxButtonSoft, :root.t_dark .t_light .t_green_active_VoxButtonSoft, :root.t_dark .t_light .t_green_VoxButtonSoft, :root.t_light .t_dark .t_light .t_green_active_VoxButtonSoft, :root.t_light .t_dark .t_light .t_green_VoxButtonSoft, :root.t_light .t_green_active_VoxButtonSoft, :root.t_light .t_green_VoxButtonSoft {--background:hsl(120, 48%, 95%);--backgroundHover:hsl(120, 50%, 91%);--backgroundPress:hsl(120, 46%, 85%);--color:hsl(146, 40%, 24%);--colorPress:hsl(146, 40%, 24%);--colorHover:hsl(146, 40%, 24%);--colorPop:hsl(127, 55%, 55%);} @media(prefers-color-scheme:light){ body{background:var(--background);color:var(--color)} @@ -466,6 +486,11 @@ body{background:var(--background);color:var(--color)} .t_blue_active_VoxButtonContained, .t_blue_VoxButtonContained, .t_dark .t_light .t_blue_active_VoxButtonContained, .t_dark .t_light .t_blue_VoxButtonContained {--background:hsl(210, 47%, 52%);--backgroundHover:hsl(210, 49%, 45%);--backgroundPress:hsl(210, 47%, 34%);--color:hsl(0, 0%, 100%);--colorPress:hsl(0, 0%, 100%);--colorHover:hsl(0, 0%, 100%);} } +:root.t_dark .t_light .t_blue_active_VoxButtonInverseContained, :root.t_dark .t_light .t_blue_VoxButtonInverseContained, :root.t_dark .t_light .t_dark .t_light .t_blue_active_VoxButtonInverseContained, :root.t_dark .t_light .t_dark .t_light .t_blue_VoxButtonInverseContained, :root.t_light .t_blue_active_VoxButtonInverseContained, :root.t_light .t_blue_VoxButtonInverseContained, :root.t_light .t_dark .t_light .t_blue_active_VoxButtonInverseContained, :root.t_light .t_dark .t_light .t_blue_VoxButtonInverseContained {--background:hsl(0, 0%, 100%);--backgroundHover:hsl(215, 94%, 93%);--backgroundPress:hsl(214, 72%, 87%);--color:hsl(211,24%, 17%);--colorPress:hsl(211,24%, 17%);--colorHover:hsl(211,24%, 17%);} +@media(prefers-color-scheme:light){ + body{background:var(--background);color:var(--color)} + .t_blue_active_VoxButtonInverseContained, .t_blue_VoxButtonInverseContained, .t_dark .t_light .t_blue_active_VoxButtonInverseContained, .t_dark .t_light .t_blue_VoxButtonInverseContained {--background:hsl(0, 0%, 100%);--backgroundHover:hsl(215, 94%, 93%);--backgroundPress:hsl(214, 72%, 87%);--color:hsl(211,24%, 17%);--colorPress:hsl(211,24%, 17%);--colorHover:hsl(211,24%, 17%);} + } :root.t_dark .t_light .t_blue_active_VoxButtonSoft, :root.t_dark .t_light .t_blue_VoxButtonSoft, :root.t_dark .t_light .t_dark .t_light .t_blue_active_VoxButtonSoft, :root.t_dark .t_light .t_dark .t_light .t_blue_VoxButtonSoft, :root.t_light .t_blue_active_VoxButtonSoft, :root.t_light .t_blue_VoxButtonSoft, :root.t_light .t_dark .t_light .t_blue_active_VoxButtonSoft, :root.t_light .t_dark .t_light .t_blue_VoxButtonSoft {--background:hsl(216, 100%, 96%);--backgroundHover:hsl(215, 94%, 93%);--backgroundPress:hsl(214, 72%, 87%);--color:hsl(210, 62%, 25%);--colorPress:hsl(210, 62%, 25%);--colorHover:hsl(210, 62%, 25%);--colorPop:hsl(210, 73%, 57%);} @media(prefers-color-scheme:light){ body{background:var(--background);color:var(--color)} @@ -531,6 +556,11 @@ body{background:var(--background);color:var(--color)} .t_dark .t_light .t_purple_active_VoxButtonContained, .t_dark .t_light .t_purple_VoxButtonContained, .t_purple_active_VoxButtonContained, .t_purple_VoxButtonContained {--background:hsl(272, 38%, 57%);--backgroundHover:hsl(273, 33%, 43%);--backgroundPress:hsl(273, 37%, 35%);--color:hsl(0, 0%, 100%);--colorPress:hsl(0, 0%, 100%);--colorHover:hsl(0, 0%, 100%);} } +:root.t_dark .t_light .t_dark .t_light .t_purple_active_VoxButtonInverseContained, :root.t_dark .t_light .t_dark .t_light .t_purple_VoxButtonInverseContained, :root.t_dark .t_light .t_purple_active_VoxButtonInverseContained, :root.t_dark .t_light .t_purple_VoxButtonInverseContained, :root.t_light .t_dark .t_light .t_purple_active_VoxButtonInverseContained, :root.t_light .t_dark .t_light .t_purple_VoxButtonInverseContained, :root.t_light .t_purple_active_VoxButtonInverseContained, :root.t_light .t_purple_VoxButtonInverseContained {--background:hsl(0, 0%, 100%);--backgroundHover:hsl(261, 100%, 94%);--backgroundPress:hsl(259, 87%, 91%);--color:hsl(211,24%, 17%);--colorPress:hsl(211,24%, 17%);--colorHover:hsl(211,24%, 17%);} +@media(prefers-color-scheme:light){ + body{background:var(--background);color:var(--color)} + .t_dark .t_light .t_purple_active_VoxButtonInverseContained, .t_dark .t_light .t_purple_VoxButtonInverseContained, .t_purple_active_VoxButtonInverseContained, .t_purple_VoxButtonInverseContained {--background:hsl(0, 0%, 100%);--backgroundHover:hsl(261, 100%, 94%);--backgroundPress:hsl(259, 87%, 91%);--color:hsl(211,24%, 17%);--colorPress:hsl(211,24%, 17%);--colorHover:hsl(211,24%, 17%);} + } :root.t_dark .t_light .t_dark .t_light .t_purple_active_VoxButtonSoft, :root.t_dark .t_light .t_dark .t_light .t_purple_VoxButtonSoft, :root.t_dark .t_light .t_purple_active_VoxButtonSoft, :root.t_dark .t_light .t_purple_VoxButtonSoft, :root.t_light .t_dark .t_light .t_purple_active_VoxButtonSoft, :root.t_light .t_dark .t_light .t_purple_VoxButtonSoft, :root.t_light .t_purple_active_VoxButtonSoft, :root.t_light .t_purple_VoxButtonSoft {--background:hsl(272, 100%, 97%);--backgroundHover:hsl(261, 100%, 94%);--backgroundPress:hsl(259, 87%, 91%);--color:hsl(274, 45%, 29%);--colorPress:hsl(274, 45%, 29%);--colorHover:hsl(274, 45%, 29%);--colorPop:hsl(266, 83%, 66%);} @media(prefers-color-scheme:light){ body{background:var(--background);color:var(--color)} @@ -596,6 +626,11 @@ body{background:var(--background);color:var(--color)} .t_dark .t_light .t_gray_active_VoxButtonContained, .t_dark .t_light .t_gray_VoxButtonContained, .t_gray_active_VoxButtonContained, .t_gray_VoxButtonContained {--background:hsl(210, 9%, 43%);--backgroundHover:hsl(210, 13%, 37%);--backgroundPress:hsl(210, 15%, 28%);--color:hsl(0, 0%, 100%);--colorPress:hsl(0, 0%, 100%);--colorHover:hsl(0, 0%, 100%);} } +:root.t_dark .t_light .t_dark .t_light .t_gray_active_VoxButtonInverseContained, :root.t_dark .t_light .t_dark .t_light .t_gray_VoxButtonInverseContained, :root.t_dark .t_light .t_gray_active_VoxButtonInverseContained, :root.t_dark .t_light .t_gray_VoxButtonInverseContained, :root.t_light .t_dark .t_light .t_gray_active_VoxButtonInverseContained, :root.t_light .t_dark .t_light .t_gray_VoxButtonInverseContained, :root.t_light .t_gray_active_VoxButtonInverseContained, :root.t_light .t_gray_VoxButtonInverseContained {--background:hsl(0, 0%, 100%);--backgroundHover:hsl(210, 26%, 89%);--backgroundPress:hsl(208, 27%, 86%);--color:hsl(211,24%, 17%);--colorPress:hsl(211,24%, 17%);--colorHover:hsl(211,24%, 17%);} +@media(prefers-color-scheme:light){ + body{background:var(--background);color:var(--color)} + .t_dark .t_light .t_gray_active_VoxButtonInverseContained, .t_dark .t_light .t_gray_VoxButtonInverseContained, .t_gray_active_VoxButtonInverseContained, .t_gray_VoxButtonInverseContained {--background:hsl(0, 0%, 100%);--backgroundHover:hsl(210, 26%, 89%);--backgroundPress:hsl(208, 27%, 86%);--color:hsl(211,24%, 17%);--colorPress:hsl(211,24%, 17%);--colorHover:hsl(211,24%, 17%);} + } :root.t_dark .t_light .t_dark .t_light .t_gray_active_VoxButtonSoft, :root.t_dark .t_light .t_dark .t_light .t_gray_VoxButtonSoft, :root.t_dark .t_light .t_gray_active_VoxButtonSoft, :root.t_dark .t_light .t_gray_VoxButtonSoft, :root.t_light .t_dark .t_light .t_gray_active_VoxButtonSoft, :root.t_light .t_dark .t_light .t_gray_VoxButtonSoft, :root.t_light .t_gray_active_VoxButtonSoft, :root.t_light .t_gray_VoxButtonSoft {--background:hsl(210, 22%, 96%);--backgroundHover:hsl(210, 26%, 89%);--backgroundPress:hsl(208, 27%, 86%);--color:hsl(209, 25%, 20%);--colorPress:hsl(209, 25%, 20%);--colorHover:hsl(209, 25%, 20%);--colorPop:hsl(209, 25%, 43%);} @media(prefers-color-scheme:light){ body{background:var(--background);color:var(--color)} diff --git a/themes.ts b/themes.ts index a4b476f16..cabb3e305 100644 --- a/themes.ts +++ b/themes.ts @@ -188,6 +188,15 @@ export const templates = (() => { colorHover: white.white1, } + const inverseButtonContainedSurface = { + background: white.white1, + backgroundHover: transparencies + 2, + backgroundPress: transparencies + 3, + color: text.light.textPrimary, + colorPress: text.light.textPrimary, + colorHover: text.light.textPrimary, + } + const buttonSoftSurface = { background: transparencies + 1, backgroundHover: transparencies + 2, @@ -353,6 +362,7 @@ export const templates = (() => { buttonOutlinedSurface, buttonTextSurface, buttonContainedSurface, + inverseButtonContainedSurface, buttonSoftSurface, } })() @@ -484,6 +494,13 @@ const buttonContainedSurface = [ }, ] as any +const inverseButtonContainedSurface = [ + { + parent: '', + template: 'inverseButtonContainedSurface', + }, +] as any + const buttonSoftSurface = [ { parent: '', @@ -601,6 +618,7 @@ const themeBuilder = createThemeBuilder() VoxInput: surfaceInput1, VoxBadge: badgeSurface, VoxButtonContained: buttonContainedSurface, + VoxButtonInverseContained: inverseButtonContainedSurface, VoxButtonSoft: buttonSoftSurface, VoxButtonOutlined: buttonOutlinedSurface, VoxButtonText: buttonTextSurface,