diff --git a/.gitignore b/.gitignore index 908fdba313a..bac9d688222 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,5 @@ Temporary Items **/node_modules **/build .vercel -.env \ No newline at end of file +.env +.codegpt \ No newline at end of file diff --git a/package.json b/package.json index 7f3eed7825a..a5ec8b366aa 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@telefonica/mistica": "^16.4.0", + "@telefonica/mistica": "^16.8.0", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", diff --git a/src/App.js b/src/App.js index 02301292aec..b1de95eca6d 100644 --- a/src/App.js +++ b/src/App.js @@ -12,6 +12,9 @@ import { useState } from "react"; import { createContext } from "react"; import SkinGenerator from "./pages/skinGenerator"; import PaletteGenerator from "./pages/paletteGenerator"; +import CreateColor from "./pages/skin-tool/onboarding/create-color"; +import CreateTypo from "./pages/skin-tool/onboarding/create-typo"; +import CreateBorder from "./pages/skin-tool/onboarding/create-border"; import Wrapped2023 from "./pages/wrapped2023/index"; import AdventCalendar2024 from "./pages/advent-calendar-2024/index"; import WrappedFinale from "./pages/wrapped2023/finale"; @@ -21,6 +24,7 @@ import ComingSoon from "./pages/advent-calendar-2024/pages/coming-soon"; import ProgressView from "./pages/advent-calendar-2024/pages/progress-view"; import ProductStatus from "./pages/mistica-product-status/index"; import ClaimYourGift from "./pages/advent-calendar-2024/pages/claim-your-gift"; +import OnboardingComplete from "./pages/skin-tool/onboarding/onboarding-complete"; export const SchemeContext = createContext(); @@ -55,8 +59,20 @@ const App = () => { element: , }, { - path: `/palette-generator`, - element: , + path: `/create-skin`, + element: + }, + { + path: `/create-typo`, + element: + }, + { + path: `/create-border`, + element: + }, + { + path: `/onboarding-complete`, + element: }, { path: `/palette-generator`, diff --git a/src/pages/skin-tool/Instructions.md b/src/pages/skin-tool/Instructions.md new file mode 100644 index 00000000000..ac47dea95b9 --- /dev/null +++ b/src/pages/skin-tool/Instructions.md @@ -0,0 +1,99 @@ +# Mística SkinTool + +## Project Goal +To provide an intuitive tool for creating and previewing custom brand skins efficiently using Mística. + +## Project Overview +Mística SkinTool enables users to customize colors, typography, and borders for brand skins. It features a multi-step flow, real-time previews, localStorage persistence, and JSON export capabilities. + +## File Descriptions and Next Steps + +### `Theme.js` +- **Purpose**: Provides a theme context for Mística components, merging custom `themeColors` with defaults to define a comprehensive palette, border radii, and text presets. +- **Specific To-Dos/Fixes**: + - Address doubts: palette variable names can be changed? (e.g., `movistarBlue` ), for colors that vary (e.g., `selectedColor`), what name should be used? +- **Next Steps**: + - Extend to accept `typography` and `borderRadius` props for full customization. + - Map user colors to `darkModeColors` for dark mode support. + - Optimize palette generation for performance. + +### `storageUtils.js` +- **Purpose**: Manages localStorage with keys, defaults, and utilities to persist and retrieve skin configurations, generating a final JSON config. +- **Specific To-Dos/Fixes**: + - Decide if `DEFAULT_VALUES` should include all variables (recommended for completeness) or only user-editable ones (current approach). + - In `generateSkinConfig`, clarify if all color variables should be returned (full palette) or just user-selected ones (current subset). +- **Next Steps**: + - Add error handling for incomplete configs in `generateSkinConfig`. + - Support versioning in exported JSON (e.g., `"version": "1.0"`). + - Optimize storage calls (e.g., batch updates). + +### `ThemePreviewWithTools.js` +- **Purpose**: Allows previewing and customizing a theme’s colors, typography, and borders, persisting colors in localStorage and exporting as JSON. +- **Specific To-Dos/Fixes**: + - Convert custom dialog and components (e.g., `ColorDialog`, `ColorBox`) to Mística’s `Dialog` and existing components where possible. +- **Next Steps**: + - Add “Typography” and “Border” tabs for full customization preview. + - Integrate undo/redo buttons in the navigation bar. + - Add a dark mode toggle in the “Preview” tab. + +### `getColorScale.js` +- **Purpose**: Generates a color scale from a base HEX color by converting to HSL, adjusting lightness, and returning HEX values for UI use. +- **Specific To-Dos/Fixes**: + - Simplify shading: Adapt scale based on color brightness (e.g., more light shades for dark bases, more dark shades for light bases). +- **Next Steps**: + - Add unit tests for edge cases (e.g., `#000000`, invalid HEX). + - Optimize algorithm for faster execution. + - Support custom scale steps (e.g., adjustable `darkCount`/`lightCount`). + +### `ColorDialog.js` +- **Purpose**: A modal for editing color names and values within `ThemePreviewWithTools`, with save/cancel options. +- **Specific To-Dos/Fixes**: + - Adapt to Mística’s `Dialog` or `Drawer` component for consistency and built-in features. +- **Next Steps**: + - Add HEX validation with error feedback. + - Enhance accessibility with ARIA labels (e.g., “Edit color name”). + - Improve responsiveness for smaller screens. + - Improve UI when selecting color input. + +### `SkinTool.js` +- **Purpose**: The entry point, offering options to start a new skin or remix an existing one. +- **Specific To-Dos/Fixes**: + - Improve UI: Add a decorative or interactive mockup to illustrate platform functionality. +- **Next Steps**: + - Add an intro modal with a tutorial and “Get Started” CTA. + - Implement “Import Skin” with file input. + - Create "Remix with existing skin" behaviour. + - Enhance mobile responsiveness. + +### `CreateColor.js` +- **Purpose**: The first step, enabling color palette definition with pickers, persisting selections in localStorage, and navigation. +- **Next Steps**: + - Add section headers (e.g., “Primary”, “Feedback”) and tooltips. + - Validate HEX codes and debounce color updates. + +### `CreateTypo.js` +- **Purpose**: The second step in the flow, allowing font and weight selection with a preview, persisting choices in localStorage. +- **Specific To-Dos/Fixes**: + - Convert custom components (e.g., weight buttons) to Mística equivalents (e.g., `Button` with custom styling). +- **Next Steps**: + - Add a preview toggle for all weights of the selected font. + - Include font info and use case labels (e.g., “Bold: Headlines”). + - Enhance accessibility with keyboard navigation and focus styles. + +### `CreateBorder.js` +- **Purpose**: The third step, allowing border radius customization and rounded button toggling with a preview grid. +- **Specific To-Dos/Fixes**: + - Fix rounded borders not appearing (ensure `borderRadius` applies correctly in `CardWrapper`). + - Fix button roundness when checkbox is unselected (correct `button` radius logic in `customTheme`). + - Finalize `borderRadiusValues` for “Ultra Soft”, “Soft”, “Square” (adjust from placeholders `[32, 24, 0]`). +- **Next Steps**: + - Integrate with `ThemePreviewWithTools` for consistent preview. + +### `OnboardingComplete.js` +- **Purpose**: The final screen, showing a celebratory message with confetti, marking flow completion, and transitioning to advanced tools. + +## Next Steps for SkinTool +1. **Beta Release**: Implement core UI/UX improvements (e.g., onboarding, navigation) and fixes (e.g., border issues, component conversions). +2. **Iteration**: Test with users, refine based on feedback (e.g., copy, bugs). +3. **Feature Expansion**: Add dark mode, enhanced import/export, and collaboration features. +4. **Production Release**: Finalize accessibility, performance, and deploy with testing/docs. \ No newline at end of file diff --git a/src/pages/skin-tool/advanced-tools.css b/src/pages/skin-tool/advanced-tools.css new file mode 100644 index 00000000000..6c393c310ad --- /dev/null +++ b/src/pages/skin-tool/advanced-tools.css @@ -0,0 +1,29 @@ +/* Estilo del mensaje y confeti que se mueve hacia arriba */ +.animated-message { + transition: opacity 1s ease-out, transform 4s ease-out; + } + + .animated-message.fade-out { + opacity: 0; + transform: translateY(-100%); /* Mueve el contenido hacia arriba */ + } + + /* Herramientas avanzadas */ + .advanced-tools-container { + position: relative; + top: 0; + animation: slideUp 3s forwards; + } + + /* Animación para que las herramientas avanzadas suban */ + @keyframes slideUp { + 0% { + transform: translateY(100%); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } + } + \ No newline at end of file diff --git a/src/pages/skin-tool/advanced-tools.jsx b/src/pages/skin-tool/advanced-tools.jsx new file mode 100644 index 00000000000..4fdd8af3526 --- /dev/null +++ b/src/pages/skin-tool/advanced-tools.jsx @@ -0,0 +1,373 @@ +// This file defines a React component `ThemePreviewWithTools` that allows users to preview and customize a theme's color palette, typography, and borders. +// It integrates with localStorage to persist color selections, provides a navigation bar to switch between sections, and includes export functionality +// for the customized theme as JSON. + +// Next steps: +// 1. change some code to Mística components (dialog, drawer) (maybe is it possible to mistify the colorbox component?) +// 2. + +import React, { useState } from "react"; +import { + ResponsiveLayout, + MainNavigationBar, + ButtonPrimary, + Text2, + skinVars, + Header, + HeaderLayout, + Inline, + Row, + IconChevronLeftRegular, + IconShareRegular, + IconTimeRegular, + IconWifiRegular, + IconCheckRegular, + ButtonLayout, + ButtonSecondary, + Title2, + Text5, + Text1, + IconButton, + IconEditPencilRegular, + Title1, +} from "@telefonica/mistica"; +import getColorScale from './utils/getColorScale'; +import Theme from "./utils/theme"; +import ColorDialog from './utils/color-input'; + +// Component definition for ThemePreviewWithTools +const ThemePreviewWithTools = () => { + // State to track the currently selected section index in the navigation bar + const [index, setIndex] = useState(0); + + //State for the skin name, defaulting to custom-skin + const [skinName, setSkinName] = useState('custom-skin'); + + //State for the currently selected color in he color picker + const [selectedColor, setSelectedColor] = useState('#0072F0'); + + //State to manage the theme's color palette, initialized from localStorage or default + const [colors, setColors] = useState(() => { + const storedColors = localStorage.getItem("skinColors"); + return storedColors ? JSON.parse(storedColors) : { + primaryColor: '#0072F0', + secondaryColor: '#FFB600', + warningColor: '#FF4C4C', + successColor: '#00C9B0', + highlightColor: '#B24FFF', + neutralColor: '#001E64' + }; + }); + + //State to store the generated color scale for the selected color + const [colorScaleOutput, setColorScaleOutput] = useState({}); + + //State to track the key of the currently selected color for editing + const [selectedColorKey, setSelectedColorKey] = useState(null); + + //State to control the visibility of the color dialog + const [isColorDialogOpen, setIsColorDialogOpen] = useState(false); + + //State to track the current color key being edited in the color dialog + const [currentColorKey, setCurrentColorKey] = useState(null); + + // Temporary color value used in the color dialog before saving + const [tempColor, setTempColor] = useState('#0072F0'); + + + // Array defining the sections available in the navigation bar + const sections = [ + { title: "Color Palette", onPress: () => setIndex(0) }, + { title: "Preview", onPress: () => setIndex(1) }, + { title: "Typography", onPress: () => setIndex(2) }, + { title: "Border", onPress: () => setIndex(3) } + ]; + + // Function to export the current theme configuration as a JSON file + const handleExportJSON = () => { + const exportData = { + name: skinName, + colors: colors + }; + const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = `${skinName}.json`; + link.click(); + }; + + // Function to handle clicking a color box, updating the selected color and its scale + const handleColorClick = (key) => { + setSelectedColor(colors[key]); + setSelectedColorKey(key); + setColorScaleOutput({ + [key]: getColorScale(colors[key]) + }); + }; + + // Function to open the color dialog for editting a specific color + const openColorDialog = (colorKey) => { + setCurrentColorKey(colorKey); + setTempColor(colors[colorKey] || '#0072F0'); + setIsColorDialogOpen(true); + }; + + // Function to save the edited color from the dialog and update the theme + const handleSaveColor = () => { + if (currentColorKey) { + const newColors = { + ...colors, + [currentColorKey]: tempColor + }; + setColors(newColors); + setSelectedColor(tempColor); + setColorScaleOutput({ + [currentColorKey]: getColorScale(tempColor) + }); + localStorage.setItem("skinColors", JSON.stringify(newColors)); + } + setIsColorDialogOpen(false); + }; + + //Function to update the temporary color value in the dialog + const handleCancelColor = () => { + setIsColorDialogOpen(false); + setTempColor(colors[currentColorKey] || '#0072F0'); + }; + + //Function to update the color value in the dialog + const handleColorChange = (newColor) => { + setTempColor(newColor); + }; + + // Component to render a single color box with optional edit icon + const ColorBox = ({ colorKey, label, showEditIcon = false }) => { + return ( +
handleColorClick(colorKey)} + style={{ + cursor: 'pointer', + padding: '12px', + borderRadius: '8px', + backgroundColor: selectedColor === colors[colorKey] ? 'rgba(0, 0, 0, 0.05)' : 'transparent', + display: 'flex', + alignItems: 'center', + flexDirection: 'row', + marginBottom: '16px', + width: '100%' + }} + > +
+
+ {label} + + {colors[colorKey] ? colors[colorKey].toUpperCase() : 'Definir color'} + +
+ {showEditIcon && ( +
+ openColorDialog(colorKey)} + Icon={IconEditPencilRegular} + type="brand" + /> +
+ )} +
+ ); + }; + + //Main render function for the component ColorBox + return ( + <> + + + + Export JSON + + } + /> + + {index === 0 && ( +
+
+ Core palette colors +
+ + + + + + + + +
+
+
+ Primary colors + + Based on the core color of the palette, the rest of the colors are derived. You can manually adjust each derived color if you need to. + + {selectedColorKey && ( + + )} + {Object.entries(colorScaleOutput).map(([key, scale]) => ( +
+ {key.replace('Color', '')} Scale +
+ {scale.map((color, index) => ( +
+ ))} +
+
+ ))} + +
+
+ )} + + {index === 1 && ( +
+
+ Core palette colors +
+ + + + + + + + +
+
+
+ Preview + +
+
+
+ + +
+ } + extra={ + + } title="Ends Tue 28 Feb" /> + } title="Online" /> + + } + /> +
+
+ At a glance + } + description="Enjoy day of access to over 1000 gyms nationwide" + /> + } + description="Only available for gyms with a Day Pass RRP of 15 or under" + /> + {}}>Use Now} + secondaryButton={ {}}>Save} + /> +
+
+
+
+
+ )} + + + + + ); +}; + +export default ThemePreviewWithTools; \ No newline at end of file diff --git a/src/pages/skin-tool/fonts/Telefonica Sans/General use/Telefonica Sans Bold.otf b/src/pages/skin-tool/fonts/Telefonica Sans/General use/Telefonica Sans Bold.otf new file mode 100644 index 00000000000..3bde3150df8 Binary files /dev/null and b/src/pages/skin-tool/fonts/Telefonica Sans/General use/Telefonica Sans Bold.otf differ diff --git a/src/pages/skin-tool/fonts/Telefonica Sans/General use/Telefonica Sans Light.otf b/src/pages/skin-tool/fonts/Telefonica Sans/General use/Telefonica Sans Light.otf new file mode 100644 index 00000000000..6a02bc83ff4 Binary files /dev/null and b/src/pages/skin-tool/fonts/Telefonica Sans/General use/Telefonica Sans Light.otf differ diff --git a/src/pages/skin-tool/fonts/Telefonica Sans/General use/Telefonica Sans Medium.otf b/src/pages/skin-tool/fonts/Telefonica Sans/General use/Telefonica Sans Medium.otf new file mode 100644 index 00000000000..6596d787c8d Binary files /dev/null and b/src/pages/skin-tool/fonts/Telefonica Sans/General use/Telefonica Sans Medium.otf differ diff --git a/src/pages/skin-tool/fonts/Telefonica Sans/General use/Telefonica Sans Regular.otf b/src/pages/skin-tool/fonts/Telefonica Sans/General use/Telefonica Sans Regular.otf new file mode 100644 index 00000000000..767996408ff Binary files /dev/null and b/src/pages/skin-tool/fonts/Telefonica Sans/General use/Telefonica Sans Regular.otf differ diff --git a/src/pages/skin-tool/fonts/Vivo/VivoTypeBold.otf b/src/pages/skin-tool/fonts/Vivo/VivoTypeBold.otf new file mode 100644 index 00000000000..a7dff2e6944 Binary files /dev/null and b/src/pages/skin-tool/fonts/Vivo/VivoTypeBold.otf differ diff --git a/src/pages/skin-tool/fonts/Vivo/VivoTypeLight.otf b/src/pages/skin-tool/fonts/Vivo/VivoTypeLight.otf new file mode 100644 index 00000000000..b00dbe6fe44 Binary files /dev/null and b/src/pages/skin-tool/fonts/Vivo/VivoTypeLight.otf differ diff --git a/src/pages/skin-tool/fonts/Vivo/VivoTypeRegular.otf b/src/pages/skin-tool/fonts/Vivo/VivoTypeRegular.otf new file mode 100644 index 00000000000..d14c02657d6 Binary files /dev/null and b/src/pages/skin-tool/fonts/Vivo/VivoTypeRegular.otf differ diff --git a/src/pages/skin-tool/fonts/fonts.css b/src/pages/skin-tool/fonts/fonts.css new file mode 100644 index 00000000000..2b227d6e4dd --- /dev/null +++ b/src/pages/skin-tool/fonts/fonts.css @@ -0,0 +1,50 @@ +/* VIVO */ +@font-face { + font-family: 'VivoType'; + src: url('./Vivo/VivoTypeRegular.otf') format('opentype'); + font-weight: normal; + font-style: normal; + } + + @font-face { + font-family: 'VivoType'; + src: url('./Vivo/VivoTypeBold.otf') format('opentype'); + font-weight: bold; + font-style: normal; + } + + @font-face { + font-family: 'VivoType'; + src: url('./Vivo/VivoTypeLight.otf') format('opentype'); + font-weight: 300; + font-style: normal; + } + + /* Telefonica Sans */ +@font-face { + font-family: 'Telefonica Sans'; + src: url('./Telefonica Sans/General use/Telefonica Sans Regular.otf') format('opentype'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'Telefonica Sans'; + src: url('./Telefonica Sans/General use/Telefonica Sans Bold.otf') format('opentype'); + font-weight: bold; + font-style: normal; +} + +@font-face { + font-family: 'Telefonica Sans'; + src: url('./Telefonica Sans/General use/Telefonica Sans Light.otf') format('opentype'); + font-weight: 300; + font-style: normal; +} + +@font-face { + font-family: 'Telefonica Sans'; + src: url('./Telefonica Sans/General use/Telefonica Sans Medium.otf') format('opentype'); + font-weight: 500; + font-style: normal; +} \ No newline at end of file diff --git a/src/pages/skin-tool/index.jsx b/src/pages/skin-tool/index.jsx index 9e3d940d3d1..fb4f89feb54 100644 --- a/src/pages/skin-tool/index.jsx +++ b/src/pages/skin-tool/index.jsx @@ -1,16 +1,60 @@ -import { MainNavigationBar, ResponsiveLayout } from "@telefonica/mistica"; +// This file defines the `SkinTool` component, the entry point for the skin creation tool. +// It provides options to start creating a skin from scratch or remix an existing one. + +// To-do: improve UI and add a Mockup for decoration purpose and to let the user know how the platform works + +import { + ResponsiveLayout, + skinVars, + ButtonPrimary, + ButtonSecondary, +} from "@telefonica/mistica"; import AppLayout from "../../components/app-layout"; +import { useNavigate } from "react-router-dom"; + const SkinTool = () => { - return ( - - -
-

Skin Tool

-
-
-
- ); + const navigate = useNavigate(); + return ( + + +
+
+

+ Your brand's new look starts here. + + Discover Mística Skintool + +

+
+ navigate('/create-skin')}>Start from Scratch + navigate('/remix-skin')}>Remix an Existing Skin +
+ {/* Here should be a Mockup for decoration purpose */} +
+
+
+
+ ); }; export default SkinTool; diff --git a/src/pages/skin-tool/onboarding/create-border.jsx b/src/pages/skin-tool/onboarding/create-border.jsx new file mode 100644 index 00000000000..716f49586df --- /dev/null +++ b/src/pages/skin-tool/onboarding/create-border.jsx @@ -0,0 +1,313 @@ +// This file defines the `CreateBorder` component, the third step in the skin creation flow. +// It allows users to adjust border radius styles for UI elements and toggle rounded buttons, persisting selections in localStorage. + +// To-do: fix the problem with the rounded borders not appearing and fix buttons roundness when checkbox is not selected. Add final ultra soft, soft and square pixel values + +import React, { useState, useEffect, useContext } from "react"; +import { + ResponsiveLayout, + Text2, + ButtonPrimary, + Text6, + ProgressBarStepped, + skinVars, + ButtonSecondary, + Grid, + GridItem, + ButtonLayout, + IconWifiRegular, + DataCard, + HighlightedCard, + PosterCard, + Image, + BoxedRow, + Tag, + Inline, + Row, + ThemeContext, +} from "@telefonica/mistica"; +import { useNavigate } from "react-router-dom"; +import { STORAGE_KEYS, setStorageItem, getStorageItem, DEFAULT_VALUES } from '../utils/storageUtils'; +import './create-typo.css'; + +const CreateBorder = () => { + const navigate = useNavigate(); + + // Holds the current theme context from ThemeContext, providing baseline theme settings to extend with custom border styles + const currentTheme = useContext(ThemeContext); + + // Defines the available border radius options in pixels, corresponding to Ultra Soft, Soft and Square * NOT FINAL VALUES + const borderRadiusValues = [32, 24, 0]; + + // Drives the custom theme’s border settings and is updated when the user selects a new radius or toggles rounded buttons, persisting changes to localStorage + const [borderConfig, setBorderConfig] = useState(() => + getStorageItem(STORAGE_KEYS.BORDER, DEFAULT_VALUES.border) + ); + + // An extended version of currentTheme with updated border-related properties reflecting user selections + const customTheme = { + ...currentTheme, + borders: { + ...currentTheme.borders, + radius: borderConfig.radius + "px", + }, + borderRadii: { + ...currentTheme.borderRadii, + container: borderConfig.radius + "px", + button: borderConfig.roundedButtons ? "999px" : borderConfig.radius + "px", + }, + components: { + ...currentTheme.components, + Card: { + ...currentTheme.components?.Card, + borderRadius: borderConfig.radius + "px", + }, + DataCard: { + ...currentTheme.components?.DataCard, + borderRadius: borderConfig.radius + "px", + }, + HighlightedCard: { + ...currentTheme.components?.HighlightedCard, + borderRadius: borderConfig.radius + "px", + }, + PosterCard: { + ...currentTheme.components?.PosterCard, + borderRadius: borderConfig.radius + "px", + }, + BoxedRow: { + ...currentTheme.components?.BoxedRow, + borderRadius: borderConfig.radius + "px", + }, + }, + }; + + // Array of objects defining the border style options, each with a label and an SVG icon for visual representation + const borders = [ + { + label: "Ultra Soft", + svg: ( + + + + ), + }, + { + label: "Soft", + svg: ( + + + + ), + }, + { + label: "Square", + svg: ( + + + + ), + }, + ]; + + // Determines the initial index of the selected border radius in borderRadiusValues based on the stored borderConfig.radius + const getInitialBorderIndex = () => { + const savedRadius = borderConfig.radius; + return borderRadiusValues.findIndex(value => value === savedRadius) || 0; + }; + + // State variable tracking the currently selected border style’s index in the borders array + const [activeBorderIndex, setActiveBorderIndex] = useState(getInitialBorderIndex); + + // Effect hook to persist borderConfig changes to localStorage + useEffect(() => { + setStorageItem(STORAGE_KEYS.BORDER, borderConfig); + }, [borderConfig]); + + // Updates the selected border radius when a border style button is clicked + const handleBorderClick = (index) => { + setActiveBorderIndex(index); + setBorderConfig(prev => ({ + ...prev, + radius: borderRadiusValues[index] + })); + }; + + // Toggles the roundedButtons property in borderConfig when the user switches the "Rounded buttons" option * FIX, variable works but is not reflected on the UI + const handleRoundedButtonsChange = (value) => { + setBorderConfig(prev => ({ + ...prev, + roundedButtons: value + })); + }; + + // Defines CSS styles for the currently selected border style button, providing visual feedback. * TRY TO CONVERT TO MISTICA + const selectedStyle = { + border: `2px solid ${skinVars.colors.brand}`, + cursor: "pointer", + width: 60, + height: 60, + fontSize: 28, + backgroundColor: "white", + }; + + // A wrapper component that applies the selected border radius to its children (various Mística cards). * FIX, the border stroke disappears when is rounded + const CardWrapper = ({ children, style, ...props }) => ( +
+ {children} +
+ ); + + return ( + + +
+

loguito

+
+ +
+ Step 3 of 4 +
+ +
+ + + + + + Adjust border radius + + Set a border radius style for elements and components with visible corners. + +
+ +
+
+ + + + + + + + + {}} + button={ + {}}> + Explore marketplace + + } + /> + + + + + {}}>Hey Ho! + } + secondaryButton={ + {}}>Let's Go! + } + /> + } + asset={} + title="Title" + description="Description" + /> + + + + + + } + headline={Teléfono móvil} + title="iPhone 12 128GB" + onPress={() => {}} + /> + + + +
+ + +
+ {borders.map((border, index) => ( +
+ +
{border.label}
+
+ ))} +
+ +
+ +
+
+
+ +
+ navigate('/create-typo')}>Back to typography + navigate('/onboarding-complete')}>Finish +
+
+
+ ); +}; + +export default CreateBorder; \ No newline at end of file diff --git a/src/pages/skin-tool/onboarding/create-color.css b/src/pages/skin-tool/onboarding/create-color.css new file mode 100644 index 00000000000..3c019112162 --- /dev/null +++ b/src/pages/skin-tool/onboarding/create-color.css @@ -0,0 +1,109 @@ +/* Layout Principal */ +.header { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + width: 100%; + margin-top: 35px; + } + + .progress-bar { + width: 750px; + } + + .title-section { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin-top: 76px; + gap: 16px; + } + + /* Card Principal */ + .color-card { + display: grid; + grid-template-columns: 1fr 2fr; /* Primera columna 1/3, segunda 2/3 */ + gap: 32px; + padding: 32px; + margin: 40px auto; + border: 1px solid lightgrey; + border-radius: 32px; + max-width: 840px; + } + + /* Columna Brand (izquierda) */ + .primary-child { + height: 100%; + display: flex; + flex-direction: column; + } + + .primary-child .each-color { + height: 100%; + display: flex; + flex-direction: column; + gap: 16px; + } + + .primary-child .color-preview { + flex-grow: 1; + width: 100%; + border-radius: 8px; + display: flex; + justify-content: center; + align-items: center; + } + + /* Columna Derecha (Secundarios y Neutrales) */ + .right-col { + display: flex; + flex-direction: column; + gap: 32px; + } + + /* Grid de Colores Secundarios */ + .secondary-child { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 24px; + } + + /* Grid de Colores Neutrales */ + .neutral-child { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 24px; + } + + /* Estilos comunes para cada color */ + .each-color { + display: flex; + flex-direction: column; + gap: 8px; + } + + .color-preview { + aspect-ratio: 1; + width: 100%; + border-radius: 8px; + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + } + + .text { + display: flex; + flex-direction: column; + gap: 4px; + } + + /* Botones inferiores */ + .buttons { + display: flex; + gap: 16px; + margin-top: 32px; + justify-content: center; + } \ No newline at end of file diff --git a/src/pages/skin-tool/onboarding/create-color.jsx b/src/pages/skin-tool/onboarding/create-color.jsx new file mode 100644 index 00000000000..a94d9e6f071 --- /dev/null +++ b/src/pages/skin-tool/onboarding/create-color.jsx @@ -0,0 +1,147 @@ +// This file defines the `CreateColor` component, the first step in the skin creation flow. +// It allows users to define a color palette for their brand using color pickers, persists selections in localStorage, and provides navigation to other steps. + +import { + ResponsiveLayout, + TextLink, + skinVars, + ButtonPrimary, + ButtonSecondary, + Text6, + Text2, + ProgressBarStepped, + IconLayersRegular, + Text1, + } from "@telefonica/mistica"; + import React, { useState, useEffect } from 'react'; + import './create-color.css'; + import { useNavigate } from "react-router-dom"; + import { useLocation } from 'react-router-dom'; + + // Initilal color palette with empty values + const initialColors = { + brandColor: '', + successColor: '', + errorColor: '', + warningColor: '', + promoColor: '', + neutral1: '', + neutral2: '', + neutral3: '', + }; + + const CreateColor = () => { + const navigate = useNavigate(); + const location = useLocation(); + const isCreateSkinPage = location.pathname === '/create-skin'; + + // State to manage the color palette, initialized from localStorage or initialColors + const [colors, setColors] = useState(() => { + const storedColors = localStorage.getItem("skinColors"); + return storedColors ? JSON.parse(storedColors) : initialColors; + }); + + // Effect hook to persist color changes in localStorage + useEffect(() => { + localStorage.setItem("skinColors", JSON.stringify(colors)); + }, [colors]); + + // Function to update a specific color in the palette + const handleColorChange = (key, value) => { + setColors((prevColors) => ({ + ...prevColors, + [key]: value, + })); + }; + + // Function to reset all the colors to their initial empty state + const handleResetColors = () => { + setColors(initialColors); + localStorage.setItem('userColors', JSON.stringify(initialColors)); + }; + + // Component to render a single color picker box + const ColorBox = ({ colorKey, label }) => ( + +
document.getElementById(`${colorKey}-input`).click()} + > + {!colors[colorKey] && } +
+ handleColorChange(colorKey, e.target.value)} + style={{ + position: 'absolute', + visibility: 'hidden', + }} + /> + + {label} + {colors[colorKey] ? ( + + {colors[colorKey].toUpperCase()} + + ) : ( + document.getElementById(`${colorKey}-input`).click()}> + Definir color + + )} + +
+ ); + + return ( + +
+

loguito

+
+ +
+ Step 1 of 4 +
+ +
+ + Build Your Brand's Palette + + Choose the colors that will define your Mística Skin. + +
+ +
+
+ +
+
+
+ + + + +
+
+ + + +
+
+
+ +
+ navigate('/skin-tool')}>Back + Restaurar valores + navigate('/create-typo')}>Next step: Typography +
+
+ ); + }; + + export default CreateColor; \ No newline at end of file diff --git a/src/pages/skin-tool/onboarding/create-typo.css b/src/pages/skin-tool/onboarding/create-typo.css new file mode 100644 index 00000000000..2efa7d33cd2 --- /dev/null +++ b/src/pages/skin-tool/onboarding/create-typo.css @@ -0,0 +1,22 @@ +.weightSelected { + border: 1px solid blue; +} + +.weightStyle { + width: 60px; + height: 60px; + background-color: white; + font-size: 28px; + cursor: pointer; +} + +.typo-card { + display: grid; + grid-template-columns: 1fr; + gap: 32px; + padding: 32px; + margin: 40px auto; + border: 1px solid lightgrey; + border-radius: 32px; + max-width: fit-content; +} \ No newline at end of file diff --git a/src/pages/skin-tool/onboarding/create-typo.jsx b/src/pages/skin-tool/onboarding/create-typo.jsx new file mode 100644 index 00000000000..130de8c9736 --- /dev/null +++ b/src/pages/skin-tool/onboarding/create-typo.jsx @@ -0,0 +1,219 @@ +// This file defines the `CreateTypo` component, the second step in the skin creation flow. +// It allows users to select a font and weight for display text, persists selections in localStorage using storageUtils, and provides a preview of the typography choices. + +// To-do: try to convert custom components to existing Mística components + +import React, { useState, useEffect } from "react"; +import { + ResponsiveLayout, + Text4, + Text2, + ButtonPrimary, + Box, + Text6, + Text5, + ProgressBarStepped, + skinVars, + Inline, + ButtonSecondary, + DisplayDataCard, + Stack, + DisplayMediaCard, + Select, +} from "@telefonica/mistica"; +import { useNavigate } from "react-router-dom"; +import { STORAGE_KEYS, setStorageItem, getStorageItem, DEFAULT_VALUES } from '../utils/storageUtils'; +import "./create-typo.css"; +import "../fonts/fonts.css"; + +const CreateTypo = () => { + + // To redirect the user to previous or next steps + const navigate = useNavigate(); + + // Initialize states using getStorageItem from storageUtils + const [typography, setTypography] = useState(() => + getStorageItem(STORAGE_KEYS.TYPOGRAPHY, DEFAULT_VALUES.typography) + ); + + // Highlights the selected weight button and applies corresponding weight + const [activeIndex, setActiveIndex] = useState(() => { + const weights = ["bold", "medium", "regular", "light"]; + return weights.indexOf(typography.weight); + }); + + const fonts = ["Telefonica Sans", "VivoType"]; + const weights = ["Bold", "Medium", "Regular", "Light"]; + + // Effect hook to persist typography changes to localStorage + useEffect(() => { + setStorageItem(STORAGE_KEYS.TYPOGRAPHY, typography); + }, [typography]); + + // Function to handle font selection from the dropdown + const handleFontChange = (newFont) => { + setTypography(prev => ({ + ...prev, + font: newFont + })); + }; + + // Function to handle weight selection from buttons + const handleClick = (index) => { + const weight = weights[index].toLowerCase(); + setActiveIndex(index); + setTypography(prev => ({ + ...prev, + weight: weight + })); + }; + + // Applied condititonally to the active weight button to visually indicate selection + const selectedStyle = { + border: `2px solid ${skinVars.colors.brand}`, + cursor: "pointer", + width: 60, + height: 60, + fontSize: 28, + backgroundColor: "white", + }; + + return ( + +
+

loguito

+
+ +
+ Step 2 of 4 +
+ +
+ + + + + Define the typography + + Choose a font and a weight for the display texts. + +
+ +
+
+ + + + Preview +
+ + Step outside your comfort zone. Dream big. Discover what's possible. + +
+
+
+ } + /> + + +
+ + + This is an example of Display Text + + } + description="Display Texts are often used in large text" + /> + +
+
+ + onColorNameChange(e.target.value)} + placeholder="Name" + style={{ + width: "100%", + marginBottom: "16px", + padding: "8px", + borderRadius: "4px", + border: `1px solid ${skinVars.colors.border}`, + }} + /> + onColorChange(e.target.value)} + style={{ marginBottom: "16px", width: "100%" }} + /> + onColorChange(e.target.value)} + placeholder="HEX #" + style={{ + width: "100%", + marginBottom: "16px", + padding: "8px", + borderRadius: "4px", + border: `1px solid ${skinVars.colors.border}`, + }} + /> +

+ If you change a core color of the palette, the derived tones will automatically change, taking the new color as reference. +

+ Save} + secondaryButton={Cancel} + /> +
+
+ + + + ); +}; + +export default ColorDialog; \ No newline at end of file diff --git a/src/pages/skin-tool/utils/getColorScale.js b/src/pages/skin-tool/utils/getColorScale.js new file mode 100644 index 00000000000..79b7c0ab148 --- /dev/null +++ b/src/pages/skin-tool/utils/getColorScale.js @@ -0,0 +1,90 @@ +// This file defines utility functions to generate a color scale from a base color. +// It converts HEX to HSL, adjusts lightness to create darker and lighter shades, and converts back to HEX. +// The main function `getColorScale` returns an array of color values for use in the UI color picker. + +// To-do: simplify color shading by adapting each scale to the color brightness: if it's too dark, most light color shades should appear and viceversa. + +const hueToRgb = (p, q, t) => { + //helper function to convert hue to RGB values + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1/6) return p + (q - p) * 6 * t; + if (t < 1/2) return q; + if (t < 2/3) return p + (q - p) * (2/3 - t) * 6; + return p; + }; + + const hslToRgb = (h, s, l) => { + // Function to convert HSL values to RGB + let r, g, b; + if (s === 0) { + r = g = b = l; + } else { + const q = l < 0.5 ? l * (1 + s) : l + s - l * s; + const p = 2 * l - q; + r = hueToRgb(p, q, h + 1/3); + g = hueToRgb(p, q, h); + b = hueToRgb(p, q, h - 1/3); + } + return [ + Math.round(r * 255), + Math.round(g * 255), + Math.round(b * 255) + ]; + }; + + const rgbToHex = (r, g, b) => + // Function to convert RGB values to HEX string + `#${[r, g, b].map(x => { + const hex = x.toString(16); + return hex.length === 1 ? '0' + hex : hex; + }).join('')}`; + + const getColorScale = (baseColor, darkCount = 4, lightCount = 4) => { + // Main function to generate a color scale from a base HEX color + const r = parseInt(baseColor.slice(1, 3), 16) / 255; + const g = parseInt(baseColor.slice(3, 5), 16) / 255; + const b = parseInt(baseColor.slice(5, 7), 16) / 255; + + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + let h, s, l = (max + min) / 2; + + if (max === min) { + h = s = 0; + } else { + const d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + switch (max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + default: + throw new Error('Unexpected max value in color scale calculation'); + } + h /= 6; + } + + const colorScale = []; + + // Generate darker colors (shades) + for (let i = 1; i <= darkCount; i++) { + const newL = l * (1 - i * 0.1); + const [nr, ng, nb] = hslToRgb(h, s, newL); + colorScale.unshift(rgbToHex(nr, ng, nb)); + } + + // Add base color + colorScale.push(baseColor); + + // Generate lighter colors (tints) + for (let i = 1; i <= lightCount; i++) { + const newL = l + (1 - l) * (i * 0.1); + const [nr, ng, nb] = hslToRgb(h, s, newL); + colorScale.push(rgbToHex(nr, ng, nb)); + } + + return colorScale; + }; + + export default getColorScale; \ No newline at end of file diff --git a/src/pages/skin-tool/utils/storageUtils.js b/src/pages/skin-tool/utils/storageUtils.js new file mode 100644 index 00000000000..f240a55abaa --- /dev/null +++ b/src/pages/skin-tool/utils/storageUtils.js @@ -0,0 +1,106 @@ + +// This file provides utility functions and constants for managing skin configuration data in localStorage. +// It defines storage keys, default values, and functions to get/set items, mark the flow as completed, and generate a final skin configuration JSON. +// It ensures persistence and retrieval of user selections across the skin creation flow. + +// Storage keys for localStorage +const STORAGE_KEYS = { + COLORS: 'skinColors', + TYPOGRAPHY: 'skinTypography', + BORDER: 'skinBorder', + FLOW_COMPLETED: 'flowCompleted', + SKIN_NAME: 'skinName', + }; + + // Default values for the skin configuration. * Should all variables appear? + const DEFAULT_VALUES = { + colors: { + brandColor: '#0066FF', + successColor: '#FFFFFF', + errorColor: '#FFFFFF', + warningColor: '#FFFFFF', + promoColor: '#777777', + neutral1: '#FFFFFF', + neutral2: '#FFFFFF', + neutral3: '#FFFFFF' + }, + typography: { + font: 'Telefonica Sans', + weight: 'regular' + }, + border: { + radius: 4, + roundedButtons: true + }, + skinName: 'custom-skin' + }; + + // Function to retrieve an item from localStorage with a fallback to default value + const getStorageItem = (key, defaultValue) => { + const item = localStorage.getItem(key); + return item ? JSON.parse(item) : defaultValue; + }; + + // Function to set an item in localStorage + const setStorageItem = (key, value) => { + localStorage.setItem(key, JSON.stringify(value)); + }; + + // Function to mark the skin creation flow as completed + const markFlowAsCompleted = () => { + setStorageItem(STORAGE_KEYS.FLOW_COMPLETED, true); + }; + + // Function to check if the flow is completed + const isFlowCompleted = () => { + return getStorageItem(STORAGE_KEYS.FLOW_COMPLETED, false); + }; + + // Function to reset the flow completion status + const resetFlow = () => { + localStorage.removeItem(STORAGE_KEYS.FLOW_COMPLETED); + }; + + // Function to generate the final skin configuration JSON (only if the flow is completed) + const generateSkinConfig = () => { + if (!isFlowCompleted()) { + throw new Error('Flujo no completado'); + } + + const colors = getStorageItem(STORAGE_KEYS.COLORS, DEFAULT_VALUES.colors); + const typography = getStorageItem(STORAGE_KEYS.TYPOGRAPHY, DEFAULT_VALUES.typography); + const border = getStorageItem(STORAGE_KEYS.BORDER, DEFAULT_VALUES.border); + const skinName = getStorageItem(STORAGE_KEYS.SKIN_NAME, DEFAULT_VALUES.skinName); + + return { + name: skinName, + colors: { + brand: colors.brandColor, + success: colors.successColor, + error: colors.errorColor, + warning: colors.warningColor, + promo: colors.promoColor, + background: colors.neutral1, + backgroundAlternative: colors.neutral2, + borderLow: colors.neutral3, + // Should return the rest of color variables or only the ones skin tool lets you choose? + }, + typography: { + fontFamily: typography.font, + fontWeight: typography.weight + }, + borderRadius: border.radius, + roundedButtons: border.roundedButtons + }; + }; + + export { + STORAGE_KEYS, + DEFAULT_VALUES, + getStorageItem, + setStorageItem, + generateSkinConfig, + markFlowAsCompleted, + isFlowCompleted, + resetFlow + }; \ No newline at end of file diff --git a/src/pages/skin-tool/utils/theme.jsx b/src/pages/skin-tool/utils/theme.jsx new file mode 100644 index 00000000000..5952aa76d04 --- /dev/null +++ b/src/pages/skin-tool/utils/theme.jsx @@ -0,0 +1,413 @@ +// This file defines the `Theme` component, which provides a theme context for the Mística design system. +// It accepts custom theme colors as props, merges them with default values, and defines a comprehensive color palette, border radius, and text presets. +// It wraps children in a ThemeContextProvider to apply the generated skin. + +// Some doubts: is it possible to change palette variables name? +// if so, which would be the correct name for a variable that doesn't have always the same color? + +import { applyAlpha, MOVISTAR_SKIN, ThemeContextProvider } from "@telefonica/mistica"; +const Theme = ({ children, themeColors }) => { + + // Default color values if not provided in themeColors + const defaultColors = { + primaryColor: '#008000', + secondaryColor: '#FFB600', + warningColor: '#FF4C4C', + successColor: '#00C9B0', + highlightColor: '#B24FFF', + neutralColor: '#001E64' + }; + + // Merge provided themeColors with defaultColors + const colors = { + ...themeColors, + ...defaultColors, + }; + + // Comprehensive color palette for the theme + const palette = { + ...colors, + movistarBlue: colors.brandColor || '#fabada', + movistarBlue10: '#fabad1', + movistarBlue15: '#fabad2', + movistarBlue20: '#fabad3', + movistarBlue30: '#fabad4', + movistarBlue40: '#fabad5', + movistarBlue55: '#fabad6', + movistarBlueHC: colors.selectedColor || '#fabad7', + movistarBlueHC55: '#fabada', + movistarBlueHC65: '#055398', + movistarGreen: '#5CB615', + movistarGreen10: '#EFF8E8', + movistarGreen30: '#ADDA8A', + movistarGreen40: '#8DCC5B', + movistarGreen55: '#52A413', + movistarGreen60: '#499110', + movistarGreen70: '#407F0F', + pepper: '#FF374A', + pepper10: '#FFEBED', + pepper40: '#FF7380', + pepper45: '#FF5F6E', + pepper55: '#D73241', + pepper60: '#CC2C3B', + pepper65: '#BF2937', + pepper70: '#B22634', + egg: '#F28D15', + egg10: '#FEF4E8', + egg40: '#F6AF5B', + egg55: '#D97D0D', + egg80: '#6D3F09', + pink: '#E63780', + pink45: '#EB5F99', + pink55: '#C42F6D', + purple: '#A13EA1', + purple10: '#F6ECF6', + purple35: '#C78BC7', + purple40: '#BD78BD', + purple70: '#712B71', + grey1: '#F6F6F6', + grey2: '#EEEEEE', + grey3: '#DDDDDD', + grey4: '#949494', + grey5: '#6B6C6F', + grey6: '#313235', + white: '#FFFFFF', + black: '#000000', + movistarBlueDark: '#0B2739', + darkModeBlack: '#061824', + darkModeGrey: '#081F2E', + darkModeGrey2: '#EAEBEE', + darkModeGrey3: '#CED4D7', + darkModeGrey4: '#85939C', + darkModeGrey5: '#6D7D88', + darkModeGrey6: '#3C5261', + darkModeGrey7: '#032F46', + }; + + // Generated skin configuration including colors, dark mode colors, border radius and text presets + const generatedSkin = { + name: MOVISTAR_SKIN, + colors: { + background: palette.white, + backgroundAlternative: palette.grey1, + backgroundBrand: palette.movistarBlue, + backgroundBrandSecondary: palette.movistarBlueDark, + backgroundContainer: palette.white, + backgroundContainerError: palette.pepper10, + backgroundContainerHover: applyAlpha(palette.black, 0.05), + backgroundContainerPressed: applyAlpha(palette.black, 0.08), + backgroundContainerBrand: palette.movistarBlue, + backgroundContainerBrandHover: applyAlpha(palette.black, 0.1), + backgroundContainerBrandPressed: applyAlpha(palette.black, 0.2), + backgroundContainerBrandOverInverse: palette.movistarBlue55, + backgroundContainerAlternative: palette.grey1, + backgroundOverlay: applyAlpha(palette.movistarBlueDark, 0.6), + backgroundSkeleton: palette.grey2, + backgroundSkeletonInverse: palette.movistarBlue55, + backgroundBrandTop: palette.movistarBlue, + backgroundBrandBottom: palette.movistarBlue, + appBarBackground: palette.white, + navigationBarBackground: palette.movistarBlue, + skeletonWave: palette.grey2, + borderLow: palette.grey1, + border: palette.grey3, + borderHigh: palette.grey5, + borderSelected: palette.movistarBlue, + coverBackgroundHover: applyAlpha(palette.black, 0.25), + coverBackgroundPressed: applyAlpha(palette.black, 0.35), + buttonDangerBackground: palette.pepper55, + buttonDangerBackgroundPressed: palette.pepper70, + buttonDangerBackgroundHover: palette.pepper65, + buttonLinkDangerBackgroundPressed: palette.pepper10, + buttonLinkDangerBackgroundInverse: palette.white, + buttonLinkDangerBackgroundInversePressed: palette.pepper10, + buttonLinkBackgroundPressed: palette.movistarBlue10, + buttonLinkBackgroundInversePressed: applyAlpha(palette.white, 0.2), + buttonPrimaryBackground: palette.movistarBlueHC, + buttonPrimaryBackgroundInverse: palette.white, + buttonPrimaryBackgroundPressed: palette.movistarBlueHC65, + buttonPrimaryBackgroundHover: palette.movistarBlueHC55, + buttonPrimaryBackgroundInversePressed: palette.movistarBlue10, + buttonSecondaryBorder: palette.movistarBlueHC, + buttonSecondaryBorderPressed: palette.movistarBlueHC65, + buttonSecondaryBackgroundHover: palette.movistarBlue10, + buttonSecondaryBackgroundPressed: palette.movistarBlue15, + buttonSecondaryBorderInverse: palette.white, + buttonSecondaryBorderInversePressed: palette.white, + buttonSecondaryBackgroundInverseHover: applyAlpha(palette.white, 0.2), + buttonSecondaryBackgroundInversePressed: applyAlpha(palette.white, 0.3), + textButtonPrimary: palette.white, + textButtonPrimaryInverse: palette.movistarBlueHC, + textButtonPrimaryInversePressed: palette.movistarBlueHC, + textButtonSecondary: palette.movistarBlueHC, + textButtonSecondaryPressed: palette.movistarBlueHC55, + textButtonSecondaryInverse: palette.white, + textButtonSecondaryInversePressed: palette.white, + textLink: palette.movistarBlueHC, + textLinkInverse: palette.white, + textLinkDanger: palette.pepper60, + textLinkSnackbar: palette.movistarBlue30, + textActivated: palette.movistarBlueHC, + textBrand: palette.movistarBlueHC, + control: palette.grey4, + controlActivated: palette.movistarBlue, + controlInverse: palette.movistarBlue20, + controlActivatedInverse: palette.white, + controlError: palette.pepper55, + barTrack: palette.grey3, + barTrackInverse: applyAlpha(palette.black, 0.14), + loadingBar: palette.movistarBlue, + loadingBarBackground: palette.grey2, + toggleAndroidInactive: palette.grey2, + toggleAndroidBackgroundActive: palette.movistarBlue20, + iosControlKnob: palette.white, + controlKnobInverse: palette.movistarBlue, + divider: palette.grey3, + dividerInverse: applyAlpha(palette.white, 0.2), + navigationBarDivider: palette.movistarBlue, + badge: palette.pepper55, + feedbackErrorBackground: palette.pepper55, + feedbackInfoBackground: palette.movistarBlueDark, + brand: palette.movistarBlue, + brandHigh: palette.movistarBlue55, + inverse: palette.white, + neutralHigh: palette.movistarBlueDark, + neutralMedium: palette.grey5, + neutralMediumInverse: palette.grey5, + neutralLow: palette.grey1, + neutralLowAlternative: palette.grey2, + textPrimary: palette.movistarBlueDark, + textPrimaryInverse: palette.white, + textSecondary: palette.grey5, + textSecondaryInverse: palette.movistarBlue10, + success: palette.movistarGreen55, + warning: palette.egg55, + error: palette.pepper55, + textError: palette.pepper55, + textErrorInverse: palette.white, + promo: palette.purple, + highlight: palette.pink55, + successLow: palette.movistarGreen10, + warningLow: palette.egg10, + errorLow: palette.pepper10, + promoLow: palette.purple10, + brandLow: palette.movistarBlue10, + successHigh: palette.movistarGreen70, + warningHigh: palette.egg80, + errorHigh: palette.pepper70, + promoHigh: palette.purple70, + successHighInverse: palette.movistarGreen70, + warningHighInverse: palette.egg80, + errorHighInverse: palette.pepper70, + promoHighInverse: palette.purple70, + textNavigationBarPrimary: palette.white, + textNavigationBarSecondary: palette.movistarBlue20, + textNavigationSearchBarHint: palette.movistarBlue20, + textNavigationSearchBarText: palette.white, + textAppBar: palette.grey5, + textAppBarSelected: palette.movistarBlueHC, + customTabsBackground: palette.white, + tagTextPromo: palette.purple70, + tagTextActive: palette.movistarBlueHC, + tagTextInactive: palette.grey5, + tagTextSuccess: palette.movistarGreen70, + tagTextWarning: palette.egg80, + tagTextError: palette.pepper70, + tagBackgroundPromo: palette.purple10, + tagBackgroundActive: palette.movistarBlue10, + tagBackgroundInactive: palette.grey1, + tagBackgroundSuccess: palette.movistarGreen10, + tagBackgroundWarning: palette.egg10, + tagBackgroundError: palette.pepper10, + cardContentOverlay: `linear-gradient(180deg, ${applyAlpha(palette.black, 0)} 0%, ${applyAlpha(palette.black, 0.4)} 30%, ${applyAlpha(palette.black, 0.7)} 100%)`, + }, + darkModeColors: { + background: palette.darkModeBlack, + backgroundAlternative: palette.darkModeBlack, + backgroundBrand: palette.darkModeBlack, + backgroundBrandSecondary: palette.darkModeBlack, + backgroundContainer: palette.darkModeGrey, + backgroundContainerError: palette.darkModeGrey, + backgroundContainerHover: applyAlpha(palette.white, 0.05), + backgroundContainerPressed: applyAlpha(palette.white, 0.08), + backgroundContainerBrand: palette.darkModeGrey, + backgroundContainerBrandHover: applyAlpha(palette.white, 0.03), + backgroundContainerBrandPressed: applyAlpha(palette.white, 0.05), + backgroundContainerBrandOverInverse: palette.darkModeGrey, + backgroundContainerAlternative: palette.darkModeGrey, + backgroundOverlay: applyAlpha(palette.darkModeGrey, 0.8), + backgroundSkeleton: palette.darkModeGrey6, + backgroundSkeletonInverse: palette.darkModeGrey6, + backgroundBrandTop: palette.darkModeBlack, + backgroundBrandBottom: palette.darkModeBlack, + appBarBackground: palette.darkModeGrey, + navigationBarBackground: palette.darkModeBlack, + skeletonWave: palette.darkModeGrey6, + borderLow: palette.darkModeBlack, + border: palette.darkModeGrey, + borderHigh: palette.darkModeGrey5, + borderSelected: palette.movistarBlue, + coverBackgroundHover: applyAlpha(palette.darkModeBlack, 0.25), + coverBackgroundPressed: applyAlpha(palette.darkModeBlack, 0.35), + buttonDangerBackground: palette.pepper55, + buttonDangerBackgroundPressed: palette.pepper70, + buttonDangerBackgroundHover: palette.pepper65, + buttonLinkDangerBackgroundPressed: applyAlpha(palette.white, 0.08), + buttonLinkDangerBackgroundInverse: applyAlpha(palette.white, 0), + buttonLinkDangerBackgroundInversePressed: applyAlpha(palette.white, 0.08), + buttonLinkBackgroundPressed: applyAlpha(palette.white, 0.08), + buttonLinkBackgroundInversePressed: applyAlpha(palette.white, 0.08), + buttonPrimaryBackground: palette.movistarBlueHC, + buttonPrimaryBackgroundInverse: palette.movistarBlueHC, + buttonPrimaryBackgroundPressed: palette.movistarBlueHC65, + buttonPrimaryBackgroundHover: palette.movistarBlueHC55, + buttonPrimaryBackgroundInversePressed: palette.movistarBlueHC65, + buttonSecondaryBackgroundHover: applyAlpha(palette.white, 0.15), + buttonSecondaryBackgroundPressed: applyAlpha(palette.white, 0.25), + buttonSecondaryBorder: palette.white, + buttonSecondaryBorderPressed: palette.white, + buttonSecondaryBorderInverse: palette.darkModeGrey2, + buttonSecondaryBorderInversePressed: palette.darkModeGrey2, + buttonSecondaryBackgroundInverseHover: applyAlpha(palette.white, 0.15), + buttonSecondaryBackgroundInversePressed: applyAlpha(palette.white, 0.25), + textButtonPrimary: palette.white, + textButtonPrimaryInverse: palette.white, + textButtonPrimaryInversePressed: palette.white, + textButtonSecondary: palette.darkModeGrey2, + textButtonSecondaryPressed: palette.darkModeGrey2, + textButtonSecondaryInverse: palette.darkModeGrey2, + textButtonSecondaryInversePressed: palette.darkModeGrey2, + textLink: palette.movistarBlue, + textLinkInverse: palette.movistarBlue, + textLinkDanger: palette.pepper45, + textLinkSnackbar: palette.movistarBlue, + textActivated: palette.movistarBlue, + textBrand: palette.movistarBlue, + control: palette.darkModeGrey4, + controlActivated: palette.movistarBlue, + controlInverse: palette.darkModeGrey4, + controlActivatedInverse: palette.movistarBlue, + controlError: palette.pepper45, + barTrack: palette.darkModeGrey6, + barTrackInverse: palette.darkModeGrey6, + loadingBar: palette.movistarBlue, + loadingBarBackground: palette.darkModeGrey6, + toggleAndroidInactive: palette.darkModeGrey2, + toggleAndroidBackgroundActive: palette.movistarBlue20, + iosControlKnob: palette.darkModeGrey2, + controlKnobInverse: palette.darkModeGrey2, + divider: applyAlpha(palette.white, 0.1), + dividerInverse: applyAlpha(palette.white, 0.1), + navigationBarDivider: palette.darkModeBlack, + badge: palette.pepper55, + feedbackErrorBackground: palette.pepper55, + feedbackInfoBackground: palette.movistarBlueDark, + brand: palette.movistarBlue, + brandHigh: palette.movistarBlue40, + inverse: palette.darkModeGrey2, + neutralHigh: palette.darkModeGrey2, + neutralMedium: palette.darkModeGrey5, + neutralMediumInverse: palette.grey5, + neutralLow: palette.darkModeGrey6, + neutralLowAlternative: palette.darkModeGrey6, + textPrimary: palette.darkModeGrey2, + textPrimaryInverse: palette.darkModeGrey2, + textSecondary: palette.darkModeGrey4, + textSecondaryInverse: palette.darkModeGrey4, + success: palette.movistarGreen, + warning: palette.egg, + error: palette.pepper45, + textError: palette.pepper45, + textErrorInverse: palette.pepper45, + promo: palette.purple40, + highlight: palette.pink45, + successLow: palette.darkModeGrey7, + warningLow: palette.darkModeGrey7, + errorLow: palette.darkModeGrey7, + promoLow: palette.darkModeGrey7, + brandLow: palette.darkModeGrey7, + successHigh: palette.movistarGreen40, + warningHigh: palette.egg40, + errorHigh: palette.pepper45, + promoHigh: palette.purple35, + successHighInverse: palette.movistarGreen70, + warningHighInverse: palette.egg80, + errorHighInverse: palette.pepper70, + promoHighInverse: palette.purple70, + textNavigationBarPrimary: palette.darkModeGrey2, + textNavigationBarSecondary: palette.darkModeGrey4, + textNavigationSearchBarHint: palette.darkModeGrey4, + textNavigationSearchBarText: palette.darkModeGrey2, + textAppBar: palette.darkModeGrey4, + textAppBarSelected: palette.movistarBlue, + customTabsBackground: palette.darkModeBlack, + tagTextPromo: palette.purple35, + tagTextActive: palette.movistarBlue, + tagTextInactive: palette.darkModeGrey3, + tagTextSuccess: palette.movistarGreen40, + tagTextWarning: palette.egg40, + tagTextError: palette.pepper45, + tagBackgroundPromo: palette.darkModeGrey7, + tagBackgroundActive: palette.darkModeGrey7, + tagBackgroundInactive: palette.darkModeGrey7, + tagBackgroundSuccess: palette.darkModeGrey7, + tagBackgroundWarning: palette.darkModeGrey7, + tagBackgroundError: palette.darkModeGrey7, + cardContentOverlay: `linear-gradient(180deg, ${applyAlpha(palette.black, 0)} 0%, ${applyAlpha(palette.black, 0.4)} 30%, ${applyAlpha(palette.black, 0.7)} 100%)`, + }, + borderRadii: { + avatar: '50%', + bar: '999px', + button: '4px', + checkbox: '2px', + container: '8px', + indicator: '999px', + input: '8px', + legacyDisplay: '16px', + popup: '8px', + sheet: '8px', + mediaSmall: '8px', + }, + textPresets: { + button: {weight: 'medium'}, + cardTitle: {weight: 'bold'}, + indicator: {weight: 'medium'}, + link: {weight: 'medium'}, + navigationBar: {weight: 'medium'}, + tabsLabel: { + lineHeight: {desktop: 24, mobile: 24}, + size: {desktop: 18, mobile: 16}, + weight: 'medium', + }, + text1: {lineHeight: {desktop: 20, mobile: 16}, size: {desktop: 14, mobile: 12}}, + text2: {lineHeight: {desktop: 24, mobile: 20}, size: {desktop: 16, mobile: 14}}, + text3: {lineHeight: {desktop: 24, mobile: 24}, size: {desktop: 18, mobile: 16}}, + text4: {lineHeight: {desktop: 28, mobile: 24}, size: {desktop: 20, mobile: 18}}, + text5: {lineHeight: {desktop: 32, mobile: 24}, size: {desktop: 28, mobile: 20}, weight: 'bold'}, + text6: {lineHeight: {desktop: 40, mobile: 32}, size: {desktop: 32, mobile: 24}, weight: 'bold'}, + text7: {lineHeight: {desktop: 48, mobile: 32}, size: {desktop: 40, mobile: 28}, weight: 'bold'}, + text8: {lineHeight: {desktop: 56, mobile: 40}, size: {desktop: 48, mobile: 32}, weight: 'bold'}, + text9: {lineHeight: {desktop: 64, mobile: 48}, size: {desktop: 56, mobile: 40}, weight: 'bold'}, + text10: {lineHeight: {desktop: 72, mobile: 56}, size: {desktop: 64, mobile: 48}, weight: 'bold'}, + title1: {weight: 'medium'}, + title2: {weight: 'bold'}, + title3: {lineHeight: {desktop: 32, mobile: 24}, size: {desktop: 28, mobile: 20}, weight: 'bold'}, + } + } + + + + return ( + + {children} + + ); +}; + +export default Theme; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 4271c77b32e..96e6b775227 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1995,10 +1995,10 @@ resolved "https://registry.npmjs.org/@telefonica/libphonenumber/-/libphonenumber-2.9.0.tgz" integrity sha512-na1nty3dCxgtjtLTmcDD15VQ2+kABSmfSEzIZBUqdw3HI6ttrVGJHwc4iwvhBg+5OnuRkfOSeJkrqZJP+xVIhA== -"@telefonica/mistica@^16.4.0": - version "16.4.0" - resolved "https://registry.yarnpkg.com/@telefonica/mistica/-/mistica-16.4.0.tgz#e40d359d834c1dff044e823702b0e4ceb7995701" - integrity sha512-CXw39F2CywDcKt3WgyQnSjpPW6Ja5IxNVPmjB8P5pKaIFpO+XUBFVFFI9D5v4YUGwNcCAmu9wCVPHIVEuqinDA== +"@telefonica/mistica@^16.8.0": + version "16.8.0" + resolved "https://registry.yarnpkg.com/@telefonica/mistica/-/mistica-16.8.0.tgz#691aff05ff767c4fb5d607da4265653ce6b1b6cd" + integrity sha512-W3iqyCqpLpKoR0Y35uPQVvEtEUuokfocPcoYaQ82eKZTtoWb1WvjREvw5O+UcXEfUNUk/ZOFuLE8CDsHhKfeBw== dependencies: "@juggle/resize-observer" "^3.3.1" "@tef-novum/webview-bridge" "^3.39.0" @@ -2007,10 +2007,11 @@ "@vanilla-extract/dynamic" "^2.1.1" "@vanilla-extract/sprinkles" "^1.6.2" classnames "^2.3.1" + cubic-bezier "^0.1.2" lottie-react "^2.4.0" moment "^2.29.1" react-autosuggest "^10.1.0" - react-datetime "^3.1.1" + react-datetime "^3.2.0" react-focus-lock "^2.8.1" react-transition-group "^4.4.5" rifm "^0.12.1" @@ -3874,6 +3875,11 @@ csstype@^3.0.2, csstype@^3.0.7: resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz" integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== +cubic-bezier@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/cubic-bezier/-/cubic-bezier-0.1.2.tgz#d4970942002e45372e0aa92db657e39eaf6824d7" + integrity sha512-kIojIIJQ9QZ5kYaeEeFG+a9oCgPtw1CHqF8Y52enHzu2Cz3NjwfbDdbO7MOxXvVjEZ4tSU3sCGykpZrnoHYtQA== + d3-color@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz" @@ -8055,10 +8061,10 @@ react-clientside-effect@^1.2.6: dependencies: "@babel/runtime" "^7.12.13" -react-datetime@^3.1.1: - version "3.2.0" - resolved "https://registry.npmjs.org/react-datetime/-/react-datetime-3.2.0.tgz" - integrity sha512-w5XdeNIGzBht9CadaZIJhKUhEcDTgH0XokKxGPCxeeJRYL7B3HIKA8CM6Q0xej2JFJt0n5d+zi3maMwaY3262A== +react-datetime@^3.2.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/react-datetime/-/react-datetime-3.3.1.tgz#60870ef7cb70f3a98545385e068f16344a50b1db" + integrity sha512-CMgQFLGidYu6CAlY6S2Om2UZiTfZsjC6j4foXcZ0kb4cSmPomdJ2S1PhK0v3fwflGGVuVARGxwkEUWtccHapJA== dependencies: prop-types "^15.5.7"