diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0df4566228..f4db4a6727 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1364,6 +1364,31 @@ importers: specifier: ^16.0.0 version: 16.0.0(@semcore/core@semcore+core) + semcore/header-controls: + dependencies: + '@semcore/base-trigger': + specifier: ^16.0.0 + version: link:../base-trigger + '@semcore/button': + specifier: ^16.0.0 + version: link:../button + '@semcore/input': + specifier: ^16.0.0 + version: link:../input + '@semcore/select': + specifier: ^16.0.0 + version: link:../select + '@semcore/typography': + specifier: ^16.0.0 + version: link:../typography + devDependencies: + '@semcore/base-components': + specifier: workspace:* + version: link:../base-components + '@semcore/testing-utils': + specifier: workspace:* + version: link:../../tools/testing-utils + semcore/i18n-unplugin: dependencies: '@semcore/core': @@ -2408,6 +2433,9 @@ importers: '@semcore/grid': specifier: 16.0.0 version: 16.0.0(@semcore/base-components@16.0.0(@semcore/core@16.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3))) + '@semcore/header-controls': + specifier: 16.0.0 + version: link:../header-controls '@semcore/i18n-unplugin': specifier: 16.0.0 version: 16.0.0(@semcore/core@16.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3)) @@ -3017,7 +3045,7 @@ importers: version: 0.14.29 vitest-axe: specifier: 0.1.0 - version: 0.1.0(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.15.30)(@vitest/browser@3.0.6)(@vitest/ui@3.0.6)(happy-dom@9.20.3)(jsdom@22.1.0)(less@3.13.1)(msw@2.9.0(@types/node@22.15.30)(typescript@5.8.3))(sass@1.89.1)(terser@5.41.0)(yaml@2.8.0)) + version: 0.1.0(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.15.30)(@vitest/browser@3.0.6(@types/node@18.16.15)(playwright@1.48.0)(typescript@5.8.3)(vite@6.3.5(@types/node@18.16.15)(less@3.13.1)(sass@1.89.1)(terser@5.41.0)(yaml@2.8.0))(vitest@3.0.9)(webdriverio@8.24.3(typescript@5.8.3)))(@vitest/ui@3.0.6(vitest@3.0.9))(happy-dom@9.20.3)(jsdom@22.1.0)(less@3.13.1)(msw@2.9.0(@types/node@22.15.30)(typescript@5.8.3))(sass@1.89.1)(terser@5.41.0)(yaml@2.8.0)) website: dependencies: @@ -28845,7 +28873,7 @@ snapshots: - typescript - universal-cookie - vitest-axe@0.1.0(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.15.30)(@vitest/browser@3.0.6)(@vitest/ui@3.0.6)(happy-dom@9.20.3)(jsdom@22.1.0)(less@3.13.1)(msw@2.9.0(@types/node@22.15.30)(typescript@5.8.3))(sass@1.89.1)(terser@5.41.0)(yaml@2.8.0)): + vitest-axe@0.1.0(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.15.30)(@vitest/browser@3.0.6(@types/node@18.16.15)(playwright@1.48.0)(typescript@5.8.3)(vite@6.3.5(@types/node@18.16.15)(less@3.13.1)(sass@1.89.1)(terser@5.41.0)(yaml@2.8.0))(vitest@3.0.9)(webdriverio@8.24.3(typescript@5.8.3)))(@vitest/ui@3.0.6(vitest@3.0.9))(happy-dom@9.20.3)(jsdom@22.1.0)(less@3.13.1)(msw@2.9.0(@types/node@22.15.30)(typescript@5.8.3))(sass@1.89.1)(terser@5.41.0)(yaml@2.8.0)): dependencies: aria-query: 5.3.2 axe-core: 4.9.1 @@ -28853,7 +28881,7 @@ snapshots: dom-accessibility-api: 0.5.16 lodash-es: 4.17.21 redent: 3.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.30)(@vitest/browser@3.0.6)(@vitest/ui@3.0.6)(happy-dom@9.20.3)(jsdom@22.1.0)(less@3.13.1)(msw@2.9.0(@types/node@22.15.30)(typescript@5.8.3))(sass@1.89.1)(terser@5.41.0)(yaml@2.8.0) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.30)(@vitest/browser@3.0.6(@types/node@18.16.15)(playwright@1.48.0)(typescript@5.8.3)(vite@6.3.5(@types/node@18.16.15)(less@3.13.1)(sass@1.89.1)(terser@5.41.0)(yaml@2.8.0))(vitest@3.0.9)(webdriverio@8.24.3(typescript@5.8.3)))(@vitest/ui@3.0.6(vitest@3.0.9))(happy-dom@9.20.3)(jsdom@22.1.0)(less@3.13.1)(msw@2.9.0(@types/node@22.15.30)(typescript@5.8.3))(sass@1.89.1)(terser@5.41.0)(yaml@2.8.0) vitest@3.0.9(@types/debug@4.1.12)(@types/node@18.16.15)(@vitest/browser@3.0.6)(@vitest/ui@3.0.6)(happy-dom@9.20.3)(jsdom@22.1.0)(less@3.13.1)(msw@2.9.0(@types/node@18.16.15)(typescript@5.8.3))(sass@1.89.1)(terser@5.41.0)(yaml@2.8.0): dependencies: @@ -28898,7 +28926,7 @@ snapshots: - tsx - yaml - vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.15.30)(@vitest/browser@3.0.6)(@vitest/ui@3.0.6)(happy-dom@9.20.3)(jsdom@22.1.0)(less@3.13.1)(msw@2.9.0(@types/node@22.15.30)(typescript@5.8.3))(sass@1.89.1)(terser@5.41.0)(yaml@2.8.0): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.15.30)(@vitest/browser@3.0.6(@types/node@18.16.15)(playwright@1.48.0)(typescript@5.8.3)(vite@6.3.5(@types/node@18.16.15)(less@3.13.1)(sass@1.89.1)(terser@5.41.0)(yaml@2.8.0))(vitest@3.0.9)(webdriverio@8.24.3(typescript@5.8.3)))(@vitest/ui@3.0.6(vitest@3.0.9))(happy-dom@9.20.3)(jsdom@22.1.0)(less@3.13.1)(msw@2.9.0(@types/node@22.15.30)(typescript@5.8.3))(sass@1.89.1)(terser@5.41.0)(yaml@2.8.0): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 diff --git a/semcore/core/src/theme/light.json b/semcore/core/src/theme/light.json index d2403f043c..29ae7082e7 100644 --- a/semcore/core/src/theme/light.json +++ b/semcore/core/src/theme/light.json @@ -2462,11 +2462,42 @@ }, "border": { "primary": { - "value": "{violet.dusty.700}", + "value": "transparent", "type": "color" }, "secondary": { - "value": "rgba(255,255,255,0.15)", + "value": "rgba({gray.white}, 0.15)", + "type": "color" + } + }, + "control": { + "secondary": { + "value": "{violet.dusty.600}", + "type": "color" + }, + "secondary-hover": { + "value": "{violet.dusty.500}", + "type": "color" + }, + "secondary-active": { + "value": "{violet.dusty.500}", + "type": "color", + "$extensions": { + "studio.tokens": { + "modify": { + "type": "lighten", + "value": "0.15", + "space": "hsl" + } + } + } + }, + "tertiary-hover": { + "value": "{violet.dusty.600}", + "type": "color" + }, + "tertiary-active": { + "value": "{violet.dusty.500}", "type": "color" } } diff --git a/semcore/core/src/theme/themes/auto.css b/semcore/core/src/theme/themes/auto.css index c9faf849d7..40b52fef1d 100644 --- a/semcore/core/src/theme/themes/auto.css +++ b/semcore/core/src/theme/themes/auto.css @@ -828,8 +828,13 @@ /* Background color for the "Start tracking" date on the X-axis of the chart grid. */ --intergalactic-chart-x-axis-accent-data-start-tracking: rgba(0, 159, 129, 0.2); --intergalactic-header-bg: #382E5E; - --intergalactic-header-border-primary: #382E5E; - --intergalactic-header-border-secondary: rgba(255,255,255, 0.15); + --intergalactic-header-border-primary: transparent; + --intergalactic-header-border-secondary: rgba(255, 255, 255, 0.15); + --intergalactic-header-control-secondary: #4D407E; + --intergalactic-header-control-secondary-hover: #6D619F; + --intergalactic-header-control-secondary-active: #8379ad; + --intergalactic-header-control-tertiary-hover: #4D407E; + --intergalactic-header-control-tertiary-active: #6D619F; --intergalactic-sidebar-nav-control-hover: rgba(224, 225, 233, 0.7); --intergalactic-sidebar-nav-control-active: #E2DDFF; --intergalactic-sidebar-nav-control-text-normal: #6D619F; diff --git a/semcore/core/src/theme/themes/default.css b/semcore/core/src/theme/themes/default.css index ff18dd5173..eb41a301b9 100644 --- a/semcore/core/src/theme/themes/default.css +++ b/semcore/core/src/theme/themes/default.css @@ -828,8 +828,13 @@ /* Background color for the "Start tracking" date on the X-axis of the chart grid. */ --intergalactic-chart-x-axis-accent-data-start-tracking: rgba(0, 159, 129, 0.2); --intergalactic-header-bg: #382E5E; - --intergalactic-header-border-primary: #382E5E; - --intergalactic-header-border-secondary: rgba(255,255,255, 0.15); + --intergalactic-header-border-primary: transparent; + --intergalactic-header-border-secondary: rgba(255, 255, 255, 0.15); + --intergalactic-header-control-secondary: #4D407E; + --intergalactic-header-control-secondary-hover: #6D619F; + --intergalactic-header-control-secondary-active: #8379ad; + --intergalactic-header-control-tertiary-hover: #4D407E; + --intergalactic-header-control-tertiary-active: #6D619F; --intergalactic-sidebar-nav-control-hover: rgba(224, 225, 233, 0.7); --intergalactic-sidebar-nav-control-active: #E2DDFF; --intergalactic-sidebar-nav-control-text-normal: #6D619F; diff --git a/semcore/core/src/theme/themes/default.ts b/semcore/core/src/theme/themes/default.ts index 468d51a7dd..ad4972bf30 100644 --- a/semcore/core/src/theme/themes/default.ts +++ b/semcore/core/src/theme/themes/default.ts @@ -459,8 +459,13 @@ export default { '--intergalactic-chart-x-axis-accent-period-active': '#6c6e79', '--intergalactic-chart-x-axis-accent-data-start-tracking': 'rgba(0, 159, 129, 0.2)', '--intergalactic-header-bg': '#382E5E', - '--intergalactic-header-border-primary': '#382E5E', - '--intergalactic-header-border-secondary': 'rgba(255,255,255, 0.15)', + '--intergalactic-header-border-primary': 'transparent', + '--intergalactic-header-border-secondary': 'rgba(255, 255, 255, 0.15)', + '--intergalactic-header-control-secondary': '#4D407E', + '--intergalactic-header-control-secondary-hover': '#6D619F', + '--intergalactic-header-control-secondary-active': '#8379ad', + '--intergalactic-header-control-tertiary-hover': '#4D407E', + '--intergalactic-header-control-tertiary-active': '#6D619F', '--intergalactic-sidebar-nav-control-hover': 'rgba(224, 225, 233, 0.7)', '--intergalactic-sidebar-nav-control-active': '#E2DDFF', '--intergalactic-sidebar-nav-control-text-normal': '#6D619F', diff --git a/semcore/core/src/theme/themes/light.css b/semcore/core/src/theme/themes/light.css index ff18dd5173..eb41a301b9 100644 --- a/semcore/core/src/theme/themes/light.css +++ b/semcore/core/src/theme/themes/light.css @@ -828,8 +828,13 @@ /* Background color for the "Start tracking" date on the X-axis of the chart grid. */ --intergalactic-chart-x-axis-accent-data-start-tracking: rgba(0, 159, 129, 0.2); --intergalactic-header-bg: #382E5E; - --intergalactic-header-border-primary: #382E5E; - --intergalactic-header-border-secondary: rgba(255,255,255, 0.15); + --intergalactic-header-border-primary: transparent; + --intergalactic-header-border-secondary: rgba(255, 255, 255, 0.15); + --intergalactic-header-control-secondary: #4D407E; + --intergalactic-header-control-secondary-hover: #6D619F; + --intergalactic-header-control-secondary-active: #8379ad; + --intergalactic-header-control-tertiary-hover: #4D407E; + --intergalactic-header-control-tertiary-active: #6D619F; --intergalactic-sidebar-nav-control-hover: rgba(224, 225, 233, 0.7); --intergalactic-sidebar-nav-control-active: #E2DDFF; --intergalactic-sidebar-nav-control-text-normal: #6D619F; diff --git a/semcore/core/src/theme/themes/light.ts b/semcore/core/src/theme/themes/light.ts index 468d51a7dd..ad4972bf30 100644 --- a/semcore/core/src/theme/themes/light.ts +++ b/semcore/core/src/theme/themes/light.ts @@ -459,8 +459,13 @@ export default { '--intergalactic-chart-x-axis-accent-period-active': '#6c6e79', '--intergalactic-chart-x-axis-accent-data-start-tracking': 'rgba(0, 159, 129, 0.2)', '--intergalactic-header-bg': '#382E5E', - '--intergalactic-header-border-primary': '#382E5E', - '--intergalactic-header-border-secondary': 'rgba(255,255,255, 0.15)', + '--intergalactic-header-border-primary': 'transparent', + '--intergalactic-header-border-secondary': 'rgba(255, 255, 255, 0.15)', + '--intergalactic-header-control-secondary': '#4D407E', + '--intergalactic-header-control-secondary-hover': '#6D619F', + '--intergalactic-header-control-secondary-active': '#8379ad', + '--intergalactic-header-control-tertiary-hover': '#4D407E', + '--intergalactic-header-control-tertiary-active': '#6D619F', '--intergalactic-sidebar-nav-control-hover': 'rgba(224, 225, 233, 0.7)', '--intergalactic-sidebar-nav-control-active': '#E2DDFF', '--intergalactic-sidebar-nav-control-text-normal': '#6D619F', diff --git a/semcore/core/src/theme/utils.ts b/semcore/core/src/theme/utils.ts index 9765ac64f3..1acbb51465 100644 --- a/semcore/core/src/theme/utils.ts +++ b/semcore/core/src/theme/utils.ts @@ -168,7 +168,7 @@ export const processTokens = (base: TokensInput, tokens: TokensInput, featureHig } return resolvedColor; } - if (color.startsWith('#')) { + if (color.startsWith('#') || color === 'transparent') { return color; } throw new Error(`Unable to process color ${color}`); diff --git a/semcore/header-controls/.npmignore b/semcore/header-controls/.npmignore new file mode 100644 index 0000000000..229b5c941a --- /dev/null +++ b/semcore/header-controls/.npmignore @@ -0,0 +1,4 @@ +node_modules +__tests__ +src +tsconfig.json \ No newline at end of file diff --git a/semcore/header-controls/CHANGELOG.md b/semcore/header-controls/CHANGELOG.md new file mode 100644 index 0000000000..f514ba3967 --- /dev/null +++ b/semcore/header-controls/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +CHANGELOG.md standards are inspired by [keepachangelog.com](https://keepachangelog.com/en/1.0.0/). + +## [16.0.0] - 2025-07-18 + +### Added + +- Initial release. diff --git a/semcore/header-controls/README.md b/semcore/header-controls/README.md new file mode 100644 index 0000000000..dab6ccf80f --- /dev/null +++ b/semcore/header-controls/README.md @@ -0,0 +1,39 @@ +# @semcore/header-controls + +[![version](https://img.shields.io/npm/v/@semcore/core.svg)](https://www.npmjs.com/@semcore/header-controls) +[![downloads](https://img.shields.io/npm/dt/@semcore/core.svg)](https://www.npmjs.com/package/@semcore/header-controls) +[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/semrush/intergalactic/blob/master/LICENSE) + +> This component is part of the Intergalactic Design System + +[//]: # '### 📖 [Component documentation](https://developer.semrush.com/intergalactic/components/core/)' + +### 🏠 [Design system](https://developer.semrush.com/intergalactic/) + +## Install + +```sh +npm install @semcore/header-controls +``` + + + +## 👤 Author + +[UI-kit team](https://github.com/semrush/intergalactic/blob/master/MAINTAINERS) +and [others ❤️](https://github.com/semrush/intergalactic/graphs/contributors) + +## 🤝 Contributing + +Contributions, issues and feature requests are welcome! + +Feel free to check [issues page](https://github.com/semrush/intergalactic/issues). You can also take a look at +the [contributing guide](https://github.com/semrush/intergalactic/blob/master/CONTRIBUTING.md). + +## Show your support + +Give a ⭐️ if this project helped you! + +## 📝 License + +This project is [MIT](https://github.com/semrush/intergalactic/blob/master/LICENSE) licensed. diff --git a/semcore/header-controls/package.json b/semcore/header-controls/package.json new file mode 100644 index 0000000000..6fe5a14e1c --- /dev/null +++ b/semcore/header-controls/package.json @@ -0,0 +1,40 @@ +{ + "name": "@semcore/header-controls", + "description": "Semrush toolkit for global header", + "version": "16.0.1", + "main": "lib/cjs/index.js", + "module": "lib/esm/index.mjs", + "typings": "lib/types/index.d.ts", + "sideEffects": false, + "author": "UI-kit team ", + "license": "MIT", + "scripts": { + "build": "pnpm semcore-builder && pnpm vite build" + }, + "exports": { + ".": { + "require": "./lib/cjs/index.js", + "import": "./lib/esm/index.mjs", + "types": "./lib/types/index.d.ts" + } + }, + "dependencies": { + "@semcore/base-trigger": "^16.0.0", + "@semcore/button": "^16.0.0", + "@semcore/input": "^16.0.0", + "@semcore/select": "^16.0.0", + "@semcore/typography": "^16.0.0" + }, + "peerDependencies": { + "@semcore/base-components": "^16.0.0" + }, + "repository": { + "type": "git", + "url": "https://github.com/semrush/intergalactic.git", + "directory": "semcore/header-controls" + }, + "devDependencies": { + "@semcore/base-components": "workspace:*", + "@semcore/testing-utils": "workspace:*" + } +} diff --git a/semcore/header-controls/src/components/button-link/ButtonLink.tsx b/semcore/header-controls/src/components/button-link/ButtonLink.tsx new file mode 100644 index 0000000000..b7c751f9b3 --- /dev/null +++ b/semcore/header-controls/src/components/button-link/ButtonLink.tsx @@ -0,0 +1,19 @@ +import { ButtonLink } from '@semcore/button'; +import { createComponent, Root, sstyled, Component } from '@semcore/core'; +import React from 'react'; + +import style from './buttonLink.shadow.css'; + +class HeaderButtonLinkRoot extends Component { + static displayName = 'HeaderButtonLink'; + static style = style; + + render() { + const SHeaderButtonLink = Root; + return sstyled(this.asProps.styles)( + , + ); + } +} + +export const HeaderButtonLink = createComponent(HeaderButtonLinkRoot) as typeof ButtonLink; diff --git a/semcore/header-controls/src/components/button-link/buttonLink.shadow.css b/semcore/header-controls/src/components/button-link/buttonLink.shadow.css new file mode 100644 index 0000000000..94473dc66a --- /dev/null +++ b/semcore/header-controls/src/components/button-link/buttonLink.shadow.css @@ -0,0 +1,14 @@ +SButton:not([data-group-focused="true"]) { + color: var(--intergalactic-text-secondary-invert, rgba(255, 255, 255, 0.8)); + + &[active], + &:hover, + &:active, + &[enableVisited]:visited:hover { + color: var(--intergalactic-text-primary-invert, #ffffff); + } + + &[enableVisited]:visited { + color: var(--intergalactic-text-secondary-invert, rgba(255, 255, 255, 0.8)); + } +} \ No newline at end of file diff --git a/semcore/header-controls/src/components/button/Button.tsx b/semcore/header-controls/src/components/button/Button.tsx new file mode 100644 index 0000000000..89fd195a4d --- /dev/null +++ b/semcore/header-controls/src/components/button/Button.tsx @@ -0,0 +1,23 @@ +import Button from '@semcore/button'; +import { createComponent, Root, sstyled, Component } from '@semcore/core'; +import React from 'react'; + +import style from './button.shadow.css'; + +class HeaderButtonRoot extends Component { + static displayName = 'HeaderButton'; + static style = style; + + static defaultProps = { + theme: 'invert', + }; + + render() { + const SHeaderButton = Root; + return sstyled(this.asProps.styles)( + , + ); + } +} + +export const HeaderButton = createComponent(HeaderButtonRoot) as typeof Button; diff --git a/semcore/header-controls/src/components/button/button.shadow.css b/semcore/header-controls/src/components/button/button.shadow.css new file mode 100644 index 0000000000..84de384e6e --- /dev/null +++ b/semcore/header-controls/src/components/button/button.shadow.css @@ -0,0 +1,79 @@ +SHeaderButton[size='m'] { + height: var(--intergalactic-spacing-8x, 32px); + min-width: var(--intergalactic-spacing-8x, 32px); + /* we should have min-width in the default button styles */ +} + +SAddon[size='m'] { + &:not(:only-child):first-child { + margin-left: var(--intergalactic-spacing-3x, 12px); + } + + &:not(:only-child):last-child { + margin-right: var(--intergalactic-spacing-3x, 12px); + } +} + +SText { + margin-left: var(--intergalactic-spacing-2x, 8px); + margin-right: var(--intergalactic-spacing-2x, 8px); + + &:first-child { + margin-left: var(--intergalactic-spacing-3x, 12px); + } + &:last-child { + margin-right: var(--intergalactic-spacing-3x, 12px); + } +} + +SHeaderButton[neighborLocation='both'], +SHeaderButton[neighborLocation='left'] { + &:after { + background-color: var(--intergalactic-header-border-primary, transparent); + } +} + +SHeaderButton[theme='secondary-invert'] { + background-color: var(--intergalactic-header-control-secondary, #4D407E); + border-color: var(--intergalactic-header-border-primary, transparent); + + &:not(:hover, :active) { + color: var(--intergalactic-text-secondary-invert, rgba(255, 255, 255, 0.8)); + } + + & SText { + color: var(--intergalactic-text-primary-invert, #ffffff); + } + + &:hover { + background-color: var(--intergalactic-header-control-secondary-hover, #6D619F); + } + + &:active { + background-color: var(--intergalactic-header-control-secondary-active, #8379ad); + } + + &[neighborLocation='both'], + &[neighborLocation='left'] { + &:after { + background-color: var(--intergalactic-header-border-secondary, rgba(255, 255, 255, 0.15)); + } + } + + :not(SButton) + &[neighborLocation='left'], + :not(SButton) + &[neighborLocation='both'] { + border-left-color: var(--intergalactic-header-border-secondary, rgba(255, 255, 255, 0.15)); + } +} + +SHeaderButton[theme='tertiary-invert'] { + &:not(:hover, :active) { + color: var(--intergalactic-text-secondary-invert, rgba(255, 255, 255, 0.8)); + } + &:hover { + background-color: var(--intergalactic-header-control-tertiary-hover, #4D407E); + } + &:active { + background-color: var(--intergalactic-header-control-tertiary-active, #6D619F); + } +} diff --git a/semcore/header-controls/src/components/input/Input.tsx b/semcore/header-controls/src/components/input/Input.tsx new file mode 100644 index 0000000000..8615371c8a --- /dev/null +++ b/semcore/header-controls/src/components/input/Input.tsx @@ -0,0 +1,19 @@ +import { Component, createComponent, Root, sstyled } from '@semcore/core'; +import Input from '@semcore/input'; +import React from 'react'; + +import style from './input.shadow.css'; + +class HeaderInputRoot extends Component { + static displayName = 'HeaderInput'; + static style = style; + + render() { + const SHeaderInput = Root; + return sstyled(this.asProps.styles)( + , + ); + } +} + +export const HeaderInput = createComponent(HeaderInputRoot) as typeof Input; diff --git a/semcore/header-controls/src/components/input/input.shadow.css b/semcore/header-controls/src/components/input/input.shadow.css new file mode 100644 index 0000000000..c6ae5be8b2 --- /dev/null +++ b/semcore/header-controls/src/components/input/input.shadow.css @@ -0,0 +1,53 @@ +SHeaderInput[size='m'] { + height: var(--intergalactic-spacing-8x, 32px); +} + +SHeaderInput:not(:has(SValue:focus)) { + &:not(:hover) SOutline { + background: var(--intergalactic-header-control-secondary, #4D407E); + } + &:hover SOutline { + background: var(--intergalactic-header-control-secondary-hover, #6D619F); + } + &[state='normal'] SOutline { + border: var(--intergalactic-header-border-primary, transparent); + } + SAddon { + color: var(--intergalactic-text-secondary-invert, rgba(255, 255, 255, 0.8)); + } +} + +SValue[size='m'] { + &:first-child { + padding-left: var(--intergalactic-spacing-3x, 12px); + } + &:last-child { + padding-right: var(--intergalactic-spacing-3x, 12px); + } +} + +SValue:not(:focus) { + color: var(--intergalactic-text-primary-invert, #ffffff); + + &::placeholder { + color: var(--intergalactic-text-secondary-invert, rgba(255, 255, 255, 0.8)); + } +} + +SHeaderInput SAddon { + + /* &:first-child { + padding-right: 0; + padding-left: var(--intergalactic-spacing-3x, 12px); + } */ + + &:nth-last-child(2) { + margin-right: var(--intergalactic-spacing-1x, 4px); + } + + &:has(SAddon) { + /* should be in the common styles */ + padding-left: 0; + padding-right: 0; + } +} \ No newline at end of file diff --git a/semcore/header-controls/src/components/select/Select.tsx b/semcore/header-controls/src/components/select/Select.tsx new file mode 100644 index 0000000000..77b5d64771 --- /dev/null +++ b/semcore/header-controls/src/components/select/Select.tsx @@ -0,0 +1,30 @@ +import type { IRootComponentProps } from '@semcore/core'; +import { createComponent, Root, Component } from '@semcore/core'; +import Select from '@semcore/select'; +import React from 'react'; + +import { HeaderButtonTrigger } from '../../inner-components/button-trigger/ButtonTrigger'; + +class HeaderSelectRoot extends Component { + static displayName = 'HeaderSelect'; + + render() { + return ( + + ); + } +} + +function Trigger(props: IRootComponentProps) { + return ( + + ); +} + +export const HeaderSelect = createComponent(HeaderSelectRoot, { + Trigger, + Popper: Select.Popper, + Menu: Select.Menu, + Option: Select.Option, + List: Select.List, +}) as typeof Select; diff --git a/semcore/header-controls/src/components/vars.css b/semcore/header-controls/src/components/vars.css new file mode 100644 index 0000000000..48efbc9373 --- /dev/null +++ b/semcore/header-controls/src/components/vars.css @@ -0,0 +1,49 @@ +SHeaderButton, SHeaderInput, SHeaderButtonTrigger { + /* ====== REMOVE ALL COMMENTS AFTER REVIEW ====== */ + + /* I decided to add new semantic tokens locally for convenience, + you can either add them to the token list, or remove them and instead + define the corresponding CSS properties with raw values */ + + --intergalactic-header-control-m: var(--intergalactic-spacing-8x, 32px); + --intergalactic-border-primary-header: transparent; + --intergalactic-border-secondary-header: rgba(255, 255, 255, 0.15); /* white 0.15 */ + --intergalactic-control-secondary-header: #4D407E; /* violet-dusty-600 */ + --intergalactic-control-secondary-header-hover: #6D619F; /* violet-dusty-500 */ + /* disable-tokens-validator */ + --intergalactic-control-secondary-header-active: hsl(from var(--intergalactic-control-secondary-header-hover) h s calc(l * 1.15)); + /* relative color support is around 80% on desktop globally, but we probably can + skip adding a fallback because the active state is not that critical */ + --intergalactic-control-tertiary-header-hover: #4D407E; /* violet-dusty-600 */ + --intergalactic-control-tertiary-header-active: #6D619F; /* violet-dusty-500 */ + --intergalactic-text-placeholder-header: rgba(255, 255, 255, 0.8); + + /* + --violet-dusty-400: #9083C5; + --violet-dusty-500: #6D619F; + --violet-dusty-600: #4D407E; + */ +} + + +/* ====================== NOTES ====================== */ + +/* 1. Issues with theme processing + + - if we redefine imported tokens locally, fallbacks stay the same (i.e. if import fails, styles will break) + - we don't process base tokens + - we don't process local tokens (defined in this file) + */ + + +/* 2. Alternative for active state (instead of relative color) + support is much better - 97%, but requires changing border and border-radius */ + +/* border: none; + SInner { + border-radius: inherit; + } + &[theme='secondary-invert']:active SInner { + backdrop-filter: brightness(1.25) saturate(0.8); + } */ + diff --git a/semcore/header-controls/src/index.ts b/semcore/header-controls/src/index.ts new file mode 100644 index 0000000000..eaa9dd700f --- /dev/null +++ b/semcore/header-controls/src/index.ts @@ -0,0 +1,5 @@ +export * from './components/button/Button'; +export * from './components/button-link/ButtonLink'; +export * from './components/input/Input'; +export * from './components/select/Select'; +export * from './inner-components/button-trigger/ButtonTrigger'; // for testing, remove later diff --git a/semcore/header-controls/src/inner-components/button-trigger/ButtonTrigger.tsx b/semcore/header-controls/src/inner-components/button-trigger/ButtonTrigger.tsx new file mode 100644 index 0000000000..4d0e44def8 --- /dev/null +++ b/semcore/header-controls/src/inner-components/button-trigger/ButtonTrigger.tsx @@ -0,0 +1,19 @@ +import { ButtonTrigger } from '@semcore/base-trigger'; +import { createComponent, Root, sstyled, Component } from '@semcore/core'; +import React from 'react'; + +import style from './buttonTrigger.shadow.css'; + +class HeaderButtonTriggerRoot extends Component { + static displayName = 'HeaderButtonTrigger'; + static style = style; + + render() { + const SHeaderButtonTrigger = Root; + return sstyled(this.asProps.styles)( + , + ); + } +} + +export const HeaderButtonTrigger = createComponent(HeaderButtonTriggerRoot) as typeof ButtonTrigger; diff --git a/semcore/header-controls/src/inner-components/button-trigger/buttonTrigger.shadow.css b/semcore/header-controls/src/inner-components/button-trigger/buttonTrigger.shadow.css new file mode 100644 index 0000000000..12ba47eea8 --- /dev/null +++ b/semcore/header-controls/src/inner-components/button-trigger/buttonTrigger.shadow.css @@ -0,0 +1,31 @@ +SHeaderButtonTrigger { + background-color: var(--intergalactic-header-control-secondary, #4D407E); + border-color: var(--intergalactic-header-border-primary, transparent); + color: var(--intergalactic-text-primary-invert, #ffffff); + &:hover { + background-color: var(--intergalactic-header-control-secondary-hover, #6D619F); + } + &:active { + /* but we have two versions: one that stays purple when active, and another that turns white */ + background-color: var(--intergalactic-header-control-secondary-active, #8379ad); + border-color: var(--intergalactic-header-border-primary, transparent); + } +} + +SHeaderButtonTrigger[size='m'] { + height: var(--intergalactic-spacing-8x, 32px); + min-width: var(--intergalactic-spacing-8x, 32px); + padding: 0 var(--intergalactic-spacing-3x, 12px); +} + +SHeaderButtonTrigger[neighborLocation='both'], +SHeaderButtonTrigger[neighborLocation='left'] { + border-left-color: var(--intergalactic-header-border-secondary, rgba(255, 255, 255, 0.15)); +} + +SAddon { + color: var(--intergalactic-text-secondary-invert, rgba(255, 255, 255, 0.8)); + SHeaderButtonTrigger:hover &, SHeaderButtonTrigger:active & { + color: var(--intergalactic-icon-primary-invert, #ffffff); + } +} \ No newline at end of file diff --git a/semcore/header-controls/tsconfig.json b/semcore/header-controls/tsconfig.json new file mode 100644 index 0000000000..596e2cf729 --- /dev/null +++ b/semcore/header-controls/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src"] +} diff --git a/semcore/header-controls/vite.config.ts b/semcore/header-controls/vite.config.ts new file mode 100644 index 0000000000..b6d956ee9a --- /dev/null +++ b/semcore/header-controls/vite.config.ts @@ -0,0 +1,33 @@ +import copy from 'rollup-plugin-copy'; +import { defineConfig, mergeConfig } from 'vite'; + +import viteConfig from '../../commonVite.config'; + +export default mergeConfig( + viteConfig, + defineConfig({ + build: { + lib: { + entry: './src/index.ts', + }, + rollupOptions: { + external: [ + 'react', + 'react-dom', + 'react/jsx-runtime', + /@babel\/runtime\/*/, + /@semcore\/*/, + ], + plugins: [ + // @ts-ignore + copy({ + targets: [ + { src: 'src/theme/**/*.css', dest: 'lib/esm' }, + ], + flatten: false, + }), + ], + }, + }, + }), +); diff --git a/semcore/ui/package.json b/semcore/ui/package.json index 5822c9f837..f8b184f478 100644 --- a/semcore/ui/package.json +++ b/semcore/ui/package.json @@ -46,6 +46,7 @@ "@semcore/format-text": "16.0.0", "@semcore/fullscreen-modal": "16.0.0", "@semcore/grid": "16.0.0", + "@semcore/header-controls": "16.0.0", "@semcore/i18n-unplugin": "16.0.0", "@semcore/icon": "16.0.0", "@semcore/illustration": "16.0.0", diff --git a/stories/components/header-controls/tests/button_tests.stories.tsx b/stories/components/header-controls/tests/button_tests.stories.tsx new file mode 100644 index 0000000000..912a41725c --- /dev/null +++ b/stories/components/header-controls/tests/button_tests.stories.tsx @@ -0,0 +1,14 @@ +import type { Meta, StoryObj } from '@storybook/react-vite'; + +import HeaderControlsExample from './examples/header-controls'; + +const meta: Meta = { + title: 'Components/Header controls/Tests', +}; + +export default meta; +type Story = StoryObj; + +export const HeaderControls: Story = { + render: HeaderControlsExample, +}; diff --git a/stories/components/header-controls/tests/examples/header-controls.tsx b/stories/components/header-controls/tests/examples/header-controls.tsx new file mode 100644 index 0000000000..9f1532772c --- /dev/null +++ b/stories/components/header-controls/tests/examples/header-controls.tsx @@ -0,0 +1,121 @@ +import { Flex } from '@semcore/base-components'; +import Button, { ButtonLink } from '@semcore/button'; +import { HeaderButton, HeaderInput, HeaderButtonTrigger, HeaderButtonLink } from '@semcore/header-controls'; +import Chevron from '@semcore/icon/ChevronDown/m'; +import Close from '@semcore/icon/Close/m'; +import Plus from '@semcore/icon/MathPlus/m'; +import Search from '@semcore/icon/Search/m'; +import Input from '@semcore/input'; +import React from 'react'; + +const Demo = () => { + const [focused, setFocused] = React.useState(false); + + return ( + + + Log in + + + + + + + Log in + + + + Sign up + + + + + Create project + + +
+ Menu + +
+
+ Menu + +
+ More + + Pricing + +
+ + + + +
+ Root domain +
+ + setFocused(true)} + onBlur={() => setFocused(false)} + /> + + alert('asdf')} + data-group-focused={focused} + /> + + + + Root domain + + +
+
+ + + + + + Root domain + + +
+ Clear + Large +
+ ); +}; + +export default Demo; diff --git a/website/docs/components/button/button-code.md b/website/docs/components/button/button-code.md index 5c64f447d7..62dda289f9 100644 --- a/website/docs/components/button/button-code.md +++ b/website/docs/components/button/button-code.md @@ -13,7 +13,7 @@ Addons can be added: ::: sandbox ::: diff --git a/website/docs/style/design-tokens/design-tokens.json b/website/docs/style/design-tokens/design-tokens.json index bb5f85d04a..df6cdef1a9 100644 --- a/website/docs/style/design-tokens/design-tokens.json +++ b/website/docs/style/design-tokens/design-tokens.json @@ -496,6 +496,7 @@ "dot", "feature-highlight", "feature-popover", + "header-controls", "notice-bubble", "notice-global", "tag", @@ -509,7 +510,9 @@ "rawValue": "{gray.white}, 0.8", "computedValue": "rgba(255, 255, 255, 0.8)", "description": "Inverted version of the secondary text.", - "components": [] + "components": [ + "header-controls" + ] }, { "name": "--intergalactic-text-link", @@ -1392,7 +1395,8 @@ "computedValue": "#ffffff", "description": "Inverted version of the primary icon.", "components": [ - "carousel" + "carousel", + "header-controls" ] }, { @@ -2740,6 +2744,7 @@ "feature-popover", "format-text", "fullscreen-modal", + "header-controls", "inline-input", "input-tags", "link", @@ -2780,6 +2785,7 @@ "feedback-form", "format-text", "fullscreen-modal", + "header-controls", "input", "input-tags", "modal", @@ -2825,6 +2831,7 @@ "feedback-form", "format-text", "fullscreen-modal", + "header-controls", "input", "modal", "notice", @@ -2915,6 +2922,7 @@ "errors", "format-text", "fullscreen-modal", + "header-controls", "notice-bubble", "notice-global", "typography" @@ -4033,16 +4041,65 @@ { "name": "--intergalactic-header-border-primary", "type": "color", - "rawValue": "{violet.dusty.700}", - "computedValue": "#382E5E", - "components": [] + "rawValue": "transparent", + "computedValue": "transparent", + "components": [ + "header-controls" + ] }, { "name": "--intergalactic-header-border-secondary", "type": "color", - "rawValue": "rgba(255,255,255,0.15)", - "computedValue": "rgba(255,255,255, 0.15)", - "components": [] + "rawValue": "rgba({gray.white}, 0.15)", + "computedValue": "rgba(255, 255, 255, 0.15)", + "components": [ + "header-controls" + ] + }, + { + "name": "--intergalactic-header-control-secondary", + "type": "color", + "rawValue": "{violet.dusty.600}", + "computedValue": "#4D407E", + "components": [ + "header-controls" + ] + }, + { + "name": "--intergalactic-header-control-secondary-hover", + "type": "color", + "rawValue": "{violet.dusty.500}", + "computedValue": "#6D619F", + "components": [ + "header-controls" + ] + }, + { + "name": "--intergalactic-header-control-secondary-active", + "type": "color", + "rawValue": "{violet.dusty.500} / lighten(0.15) / hsl", + "computedValue": "#8379ad", + "components": [ + "header-controls" + ] + }, + { + "name": "--intergalactic-header-control-tertiary-hover", + "type": "color", + "rawValue": "{violet.dusty.600}", + "computedValue": "#4D407E", + "components": [ + "header-controls" + ] + }, + { + "name": "--intergalactic-header-control-tertiary-active", + "type": "color", + "rawValue": "{violet.dusty.500}", + "computedValue": "#6D619F", + "components": [ + "header-controls" + ] }, { "name": "--intergalactic-sidebar-nav-control-hover",