diff --git a/common-react/@dbeaver/ui-kit/src/Button/_base.css b/common-react/@dbeaver/ui-kit/src/Button/_base.css index 20427e561f..bd6c4bb750 100644 --- a/common-react/@dbeaver/ui-kit/src/Button/_base.css +++ b/common-react/@dbeaver/ui-kit/src/Button/_base.css @@ -9,7 +9,7 @@ --dbv-kit-btn-background: var(--dbv-kit-color-primary-600); --dbv-kit-btn-background-hover: var(--dbv-kit-color-primary-700); --dbv-kit-btn-background-active: var(--dbv-kit-color-primary-800); - --dbv-kit-btn-outline-color: var(--dbv-kit-color-outline); + --dbv-kit-btn-outline-color: var(--dbv-kit-control-outline-color); --dbv-kit-btn-border-width: var(--dbv-kit-control-border-width); --dbv-kit-btn-border-color: transparent; diff --git a/common-react/@dbeaver/ui-kit/src/Checkbox/Checkbox.css b/common-react/@dbeaver/ui-kit/src/Checkbox/Checkbox.css index 1c6f5383ca..f54727cfc5 100644 --- a/common-react/@dbeaver/ui-kit/src/Checkbox/Checkbox.css +++ b/common-react/@dbeaver/ui-kit/src/Checkbox/Checkbox.css @@ -11,13 +11,12 @@ line-height: var(--dbv-kit-checkbox-height); font-size: var(--dbv-kit-checkbox-font-size); color: var(--dbv-kit-checkbox-foreground); - outline: var(--dbv-kit-color-outline); + outline: var(--dbv-kit-control-outline-color); &:hover { cursor: pointer; .dbv-kit-checkbox__check { - border-color: var(--dbv-kit-checkbox-border-color-checked); box-shadow: 0 0 0 2px var(--dbv-kit-checkbox-hover-shadow-color); } } @@ -33,11 +32,12 @@ } .dbv-kit-checkbox__check { + --check-size: round(calc(var(--dbv-kit-checkbox-height) * var(--dbv-kit-checkbox-check-size)), 2px); margin-top: calc(var(--dbv-kit-checkbox-height) * (calc(1 - var(--dbv-kit-checkbox-check-size)) / 2)); - height: calc(var(--dbv-kit-checkbox-height) * var(--dbv-kit-checkbox-check-size)); - line-height: calc(var(--dbv-kit-checkbox-height) * var(--dbv-kit-checkbox-check-size)); - width: calc(var(--dbv-kit-checkbox-height) * var(--dbv-kit-checkbox-check-size)); - min-width: calc(var(--dbv-kit-checkbox-height) * var(--dbv-kit-checkbox-check-size)); + height: var(--check-size); + line-height: var(--check-size); + width: var(--check-size); + min-width: var(--check-size); border-radius: var(--dbv-kit-checkbox-border-radius); background-color: var(--dbv-kit-checkbox-background); border-color: var(--dbv-kit-checkbox-border-color); @@ -46,7 +46,7 @@ color: var(--dbv-kit-checkbox-icon-color); outline-offset: var(--dbv-kit-control-outline-offset); outline-width: var(--dbv-kit-control-outline-width); - outline-color: var(--dbv-kit-color-outline); + outline-color: var(--dbv-kit-control-outline-color); transition-property: stroke-dashoffset; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-duration: 150ms; @@ -57,10 +57,6 @@ outline-style: solid; } - &:hover { - border-color: var(--dbv-kit-checkbox-border-color-checked); - } - &[data-checked='true'], &[data-indeterminate='true'] { background-color: var(--dbv-kit-checkbox-background-checked); diff --git a/common-react/@dbeaver/ui-kit/src/Checkbox/_base.css b/common-react/@dbeaver/ui-kit/src/Checkbox/_base.css index f8822b8f03..d56eb91acb 100644 --- a/common-react/@dbeaver/ui-kit/src/Checkbox/_base.css +++ b/common-react/@dbeaver/ui-kit/src/Checkbox/_base.css @@ -3,22 +3,21 @@ /* Checkbox sizes */ --dbv-kit-checkbox-height: var(--dbv-kit-control-height-medium); --dbv-kit-checkbox-gap: calc(var(--tw-spacing) * 1.25); - --dbv-kit-checkbox-check-size: 0.75; + --dbv-kit-checkbox-check-size: 0.5625; /* Checkbox colors */ --dbv-kit-checkbox-foreground: var(--tw-color-black); --dbv-kit-checkbox-background: transparent; --dbv-kit-checkbox-background-checked: var(--dbv-kit-color-primary-600); - --dbv-kit-checkbox-background-active: var(--dbv-kit-color-primary-800); - --dbv-kit-checkbox-hover-shadow-color: var(--dbv-kit-color-primary-100); + --dbv-kit-checkbox-hover-shadow-color: var(--dbv-kit-color-primary-200); --dbv-kit-checkbox-icon-color: var(--tw-color-white); /* Checkbox border */ - --dbv-kit-checkbox-border-width: var(--dbv-kit-control-border-width); + --dbv-kit-checkbox-border-width: calc(var(--dbv-kit-control-border-width) * 2); --dbv-kit-checkbox-border-radius: var(--dbv-kit-control-border-radius); --dbv-kit-checkbox-border-style: solid; - --dbv-kit-checkbox-border-color: var(--tw-color-gray-300); + --dbv-kit-checkbox-border-color: var(--dbv-kit-color-control-inactive); --dbv-kit-checkbox-border-color-checked: var(--dbv-kit-color-primary-600); /* Checkbox label font */ diff --git a/common-react/@dbeaver/ui-kit/src/Checkbox/_sizes.css b/common-react/@dbeaver/ui-kit/src/Checkbox/_sizes.css index d60dc99888..142f7c7fa4 100644 --- a/common-react/@dbeaver/ui-kit/src/Checkbox/_sizes.css +++ b/common-react/@dbeaver/ui-kit/src/Checkbox/_sizes.css @@ -2,22 +2,22 @@ :root { /* Small checkbox */ --dbv-kit-checkbox-small-font-size: calc(var(--dbv-kit-checkbox-font-size) * 0.875); - --dbv-kit-checkbox-small-height: calc(var(--dbv-kit-control-height-small) * 0.875); + --dbv-kit-checkbox-small-height: var(--dbv-kit-control-height-small); --dbv-kit-checkbox-small-gap: calc(var(--dbv-kit-checkbox-gap) * 0.5); /* Medium checkbox */ --dbv-kit-checkbox-medium-font-size: var(--dbv-kit-checkbox-font-size); - --dbv-kit-checkbox-medium-height: calc(var(--dbv-kit-control-height-medium) * 0.75); + --dbv-kit-checkbox-medium-height: var(--dbv-kit-control-height-medium); --dbv-kit-checkbox-medium-gap: var(--dbv-kit-checkbox-gap); /* Large checkbox */ --dbv-kit-checkbox-large-font-size: calc(var(--dbv-kit-checkbox-font-size) * 1.125); - --dbv-kit-checkbox-large-height: calc(var(--dbv-kit-control-height-large) * 0.75); + --dbv-kit-checkbox-large-height: var(--dbv-kit-control-height-large); --dbv-kit-checkbox-large-gap: calc(var(--dbv-kit-checkbox-gap) * 1.25); /* Extra large checkbox */ --dbv-kit-checkbox-xlarge-font-size: calc(var(--dbv-kit-checkbox-font-size) * 1.25); - --dbv-kit-checkbox-xlarge-height: calc(var(--dbv-kit-control-height-xlarge) * 0.75); + --dbv-kit-checkbox-xlarge-height: var(--dbv-kit-control-height-xlarge); --dbv-kit-checkbox-xlarge-gap: calc(var(--dbv-kit-checkbox-gap) * 1.5); } } diff --git a/common-react/@dbeaver/ui-kit/src/Input/Input.css b/common-react/@dbeaver/ui-kit/src/Input/Input.css index 18b16f2c46..1e34821b05 100644 --- a/common-react/@dbeaver/ui-kit/src/Input/Input.css +++ b/common-react/@dbeaver/ui-kit/src/Input/Input.css @@ -15,21 +15,44 @@ border-width: var(--dbv-kit-input-border-width); border-style: var(--dbv-kit-input-border-style); color: var(--dbv-kit-input-foreground); - padding: var(--dbv-kit-input-padding); + padding-inline: var(--dbv-kit-input-padding); font-size: var(--dbv-kit-input-font-size); height: var(--dbv-kit-input-height); + &:not(:disabled):hover { + border-color: var(--dbv-kit-color-control-hover); + } + + &:disabled { + cursor: not-allowed; + opacity: var(--dbv-kit-control-disabled-opacity); + } + &::placeholder { color: var(--dbv-kit-input-placeholder-foreground); } &:focus-visible, &[data-focus-visible] { - outline: var(--dbv-kit-control-outline-width) solid var(--dbv-kit-color-outline); + outline: var(--dbv-kit-control-outline-width) solid var(--dbv-kit-control-outline-color); outline-offset: var(--dbv-kit-control-outline-offset); } } +.dbv-kit-input__title { + color: var(--dbv-kit-input-label-foreground); + padding-inline-start: var(--dbv-kit-input-label-padding); + margin-block-end: var(--tw-spacing); +} + +.dbv-kit-input__title--required { + &:after { + content: '*'; + color: var(--dbv-kit-color-control-required); + margin-inline-start: 0.125rem; + } +} + /* ---------------------------- */ /* Sizes */ diff --git a/common-react/@dbeaver/ui-kit/src/Input/Input.tsx b/common-react/@dbeaver/ui-kit/src/Input/Input.tsx index 9671043f5e..1f3b8134bb 100644 --- a/common-react/@dbeaver/ui-kit/src/Input/Input.tsx +++ b/common-react/@dbeaver/ui-kit/src/Input/Input.tsx @@ -6,18 +6,20 @@ * you may not use this file except in compliance with the License. */ -import type { ComponentPropsWithRef } from 'react'; +import { type ComponentPropsWithRef } from 'react'; import './Input.css'; export interface InputProps extends Omit, 'size'> { size?: 'small' | 'medium' | 'large' | 'xlarge'; + label?: React.ReactNode; } export const Input: React.FC = function Input({ size, className, ...props }) { - const classNameToApply = `dbv-kit-input dbv-kit-input--${size ?? 'medium'} ${className}`.trim(); + const classNameToApply = `dbv-kit-input dbv-kit-input--${size ?? 'medium'} ${className ?? ''}`.trim(); return ( -
+
+ ); }; diff --git a/common-react/@dbeaver/ui-kit/src/Input/_base.css b/common-react/@dbeaver/ui-kit/src/Input/_base.css index 8d9638aa93..9429412ed9 100644 --- a/common-react/@dbeaver/ui-kit/src/Input/_base.css +++ b/common-react/@dbeaver/ui-kit/src/Input/_base.css @@ -3,12 +3,16 @@ --dbv-kit-input-radius: var(--dbv-kit-control-border-radius); --dbv-kit-input-font-size: calc(var(--dbv-kit-font-size-base) * 0.875); --dbv-kit-input-background: var(--dbv-kit-color-background); - --dbv-kit-input-border-color: var(--tw-color-gray-300); + --dbv-kit-input-border-color: var(--dbv-kit-color-control-inactive); --dbv-kit-input-border-width: var(--dbv-kit-control-border-width); --dbv-kit-input-border-style: solid; --dbv-kit-input-foreground: var(--dbv-kit-color-foreground); + + --dbv-kit-input-label-foreground: var(--dbv-kit-color-control-label); + --dbv-kit-input-label-padding: var(--tw-spacing); + --dbv-kit-input-height: var(--dbv-kit-control-height-medium); - --dbv-kit-input-padding: calc(var(--tw-spacing) * 1.1); + --dbv-kit-input-padding: calc(var(--tw-spacing) * 2.5); --dbv-kit-input-placeholder-foreground: var(--tw-color-gray-500); --dbv-kit-input-option-active-background: var(--dbv-kit-color-primary-200); --dbv-kit-input-option-active-border: var(--dbv-kit-color-primary-500); diff --git a/common-react/@dbeaver/ui-kit/src/Radio/Radio.css b/common-react/@dbeaver/ui-kit/src/Radio/Radio.css new file mode 100644 index 0000000000..cab310873a --- /dev/null +++ b/common-react/@dbeaver/ui-kit/src/Radio/Radio.css @@ -0,0 +1,115 @@ +@import './_base.css'; +@import './_sizes.css'; + +@layer base { + .dbv-kit-radio { + display: inline-flex; + align-items: start; + gap: var(--dbv-kit-radio-gap); + cursor: pointer; + height: var(--dbv-kit-radio-height); + + &:hover .dbv-kit-radio__input:not([aria-disabled='true']) ~ .dbv-kit-radio__control { + background-color: var(--dbv-kit-radio-inactive-hover-background); + box-shadow: 0 0 0 var(--dbv-kit-control-outline-offset) var(--dbv-kit-radio-hover-shadow-color); + } + } + + .dbv-kit-radio--small { + --dbv-kit-radio-font-size: var(--dbv-kit-radio-small-font-size); + --dbv-kit-radio-gap: var(--dbv-kit-radio-small-gap); + --dbv-kit-radio-height: var(--dbv-kit-radio-small-height); + --dbv-kit-radio-dot-size: var(--dbv-kit-radio-small-dot-size); + } + + .dbv-kit-radio--medium { + --dbv-kit-radio-font-size: var(--dbv-kit-radio-medium-font-size); + --dbv-kit-radio-gap: var(--dbv-kit-radio-medium-gap); + --dbv-kit-radio-height: var(--dbv-kit-radio-medium-height); + --dbv-kit-radio-dot-size: var(--dbv-kit-radio-medium-dot-size); + } + + .dbv-kit-radio--large { + --dbv-kit-radio-font-size: var(--dbv-kit-radio-large-font-size); + --dbv-kit-radio-gap: var(--dbv-kit-radio-large-gap); + --dbv-kit-radio-height: var(--dbv-kit-radio-large-height); + --dbv-kit-radio-dot-size: var(--dbv-kit-radio-large-dot-size); + } + + .dbv-kit-radio--xlarge { + --dbv-kit-radio-font-size: var(--dbv-kit-radio-xlarge-font-size); + --dbv-kit-radio-gap: var(--dbv-kit-radio-xlarge-gap); + --dbv-kit-radio-height: var(--dbv-kit-radio-xlarge-height); + --dbv-kit-radio-dot-size: var(--dbv-kit-radio-xlarge-dot-size); + } + + .dbv-kit-radio__input { + border: 0px; + clip: rect(0px, 0px, 0px, 0px); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0px; + position: absolute; + white-space: nowrap; + width: 1px; + + &[aria-checked='true'] + .dbv-kit-radio__control { + border-color: var(--dbv-kit-radio-active-border); + background-color: var(--dbv-kit-radio-active-background); + + &::after { + scale: 1; + } + } + + &[data-focus-visible='true'] ~ .dbv-kit-radio__control { + outline: var(--dbv-kit-control-outline-width) solid var(--dbv-kit-control-outline-color); + outline-offset: var(--dbv-kit-control-outline-offset); + box-shadow: 0 0 0 var(--dbv-kit-control-outline-offset) var(--dbv-kit-radio-hover-shadow-color); + } + + &[aria-disabled='true'] ~ .dbv-kit-radio__text, + &[aria-disabled='true'] ~ .dbv-kit-radio__control { + opacity: var(--dbv-kit-control-disabled-opacity); + } + } + + .dbv-kit-radio__control { + --ring-size: round(calc(var(--dbv-kit-radio-height) * var(--dbv-kit-radio-ring-size)), 2px); + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + width: var(--ring-size); + height: var(--ring-size); + margin-top: calc((var(--dbv-kit-radio-height) - (var(--ring-size))) * 0.5); + border-radius: 50%; + border: 2px solid var(--dbv-kit-radio-inactive-border); + background-color: var(--dbv-kit-radio-inactive-background); + transition: + background-color 0.3s, + border-color 0.3s; + + &::after { + content: ''; + width: round(calc(var(--dbv-kit-radio-height) * var(--dbv-kit-radio-dot-size)), 2px); + height: round(calc(var(--dbv-kit-radio-height) * var(--dbv-kit-radio-dot-size)), 2px); + border-radius: 50%; + background-color: var(--dbv-kit-radio-active-foreground); + scale: 0; + + transition-property: scale; + transition-timing-function: var(--tw-ease-in-out); + transition-duration: 0.2s; + } + } + + .dbv-kit-radio__title { + display: inline-block; + vertical-align: middle; + line-height: var(--dbv-kit-radio-height); + font-size: var(--dbv-kit-radio-font-size); + } +} diff --git a/common-react/@dbeaver/ui-kit/src/Radio/Radio.tsx b/common-react/@dbeaver/ui-kit/src/Radio/Radio.tsx new file mode 100644 index 0000000000..94883d1217 --- /dev/null +++ b/common-react/@dbeaver/ui-kit/src/Radio/Radio.tsx @@ -0,0 +1,25 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2025 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +import type { ControlSize } from '../types/controls.js'; +import { _Radio, type RadioProps as _RadioProps } from './index.js'; +import './Radio.css'; + +export interface RadioProps extends Omit<_RadioProps, 'size'> { + size?: ControlSize; +} + +export function Radio({ children, className, size = 'medium', ...props }: RadioProps) { + return ( +