diff --git a/lib/angular-ui/src/lib/molecules/copy-to-clipboard/copy-to-clipboard.component.ts b/lib/angular-ui/src/lib/molecules/copy-to-clipboard/copy-to-clipboard.component.ts index 41d6ad69..b9b26d1b 100644 --- a/lib/angular-ui/src/lib/molecules/copy-to-clipboard/copy-to-clipboard.component.ts +++ b/lib/angular-ui/src/lib/molecules/copy-to-clipboard/copy-to-clipboard.component.ts @@ -20,6 +20,8 @@ export class CopyToClipboardComponent { public readonly value = input.required(); + public readonly type = input(); + constructor( private readonly ngZone: NgZone, private readonly elementRef: ElementRef, @@ -31,5 +33,17 @@ export class CopyToClipboardComponent { this.elementRef.nativeElement.value = value; }); }); + + effect(() => { + const type = this.type(); + // [TODO]: use `isNil` from `@cg/utils` package + if (!type) { + return; + } + + this.ngZone.runOutsideAngular(() => { + this.elementRef.nativeElement.type = type; + }); + }); } } diff --git a/lib/styles/common.scss b/lib/styles/common.scss index 1f50ca19..5a9538b7 100644 --- a/lib/styles/common.scss +++ b/lib/styles/common.scss @@ -4,6 +4,7 @@ @forward 'common/dropdown'; @forward 'common/effects'; @forward 'common/elevation'; +@forward 'common/forms'; @forward 'common/icons'; @forward 'common/responsiveness'; @forward 'common/sizing'; diff --git a/lib/styles/common/forms.scss b/lib/styles/common/forms.scss new file mode 100644 index 00000000..9a7a76fc --- /dev/null +++ b/lib/styles/common/forms.scss @@ -0,0 +1,40 @@ +@use 'sizing'; +@use 'colors'; +@use 'dark-mode'; +@use 'effects'; + +@mixin input-common { + display: block; + width: 100%; + + border-radius: sizing.$border-sm-radius; + border: sizing.$border-md-size solid colors.$slate-400; + padding: sizing.size(2.5); + + font-size: 0.875rem; + line-height: 1.25rem; + + color: colors.$slate-900; + background-color: colors.$white; + outline: none; + + &:focus { + border-color: colors.$primary; + @include effects.ring(colors.$primary-400); + + @include dark-mode.dark { + @include effects.ring(colors.$primary-800); + } + } + + @include dark-mode.dark { + color: colors.$slate-200; + background-color: colors.$slate-950; + border-color: colors.$slate-500; + } +} + +@mixin text-area { + height: sizing.size(24); + resize: vertical; +} diff --git a/lib/styles/global/buttons.scss b/lib/styles/global/buttons.scss index 6864d6d9..c5c3dd9f 100644 --- a/lib/styles/global/buttons.scss +++ b/lib/styles/global/buttons.scss @@ -1,24 +1,19 @@ -@use '../common/colors'; -@use '../common/dark-mode'; -@use '../common/effects'; -@use '../common/icons'; -@use '../common/sizing'; -@use '../common/utils'; +@use '../common'; -$btn-color: colors.$primary; -$bn-hover-color: colors.$primary-600; -$btn-disabled-color: rgba(colors.$primary, 0.45); +$btn-color: common.$primary; +$bn-hover-color: common.$primary-600; +$btn-disabled-color: rgba(common.$primary, 0.45); .btn { - @include sizing.px(4); - @include sizing.py(1); - @include utils.no-underline; + @include common.px(4); + @include common.py(1); + @include common.no-underline; - border: sizing.$border-md-size solid transparent; - border-radius: sizing.$border-sm-radius; + border: common.$border-md-size solid transparent; + border-radius: common.$border-sm-radius; outline: none; - color: colors.$white; + color: common.$white; background-color: $btn-color; position: relative; @@ -28,10 +23,10 @@ $btn-disabled-color: rgba(colors.$primary, 0.45); } &:focus:not(:disabled) { - @include effects.ring(colors.$primary-400); + @include common.ring(common.$primary-400); - @include dark-mode.dark { - @include effects.ring(colors.$primary-800); + @include common.dark { + @include common.ring(common.$primary-800); } } @@ -50,7 +45,7 @@ $btn-disabled-color: rgba(colors.$primary, 0.45); background-color: transparent; &:hover:not(:disabled) { - color: colors.$white; + color: common.$white; } &:disabled { @@ -60,22 +55,22 @@ $btn-disabled-color: rgba(colors.$primary, 0.45); } &.btn--success { - color: colors.$success; - border-color: colors.$success; + color: common.$success; + border-color: common.$success; &:hover:not(:disabled) { - background-color: colors.$success; - border-color: colors.$success; + background-color: common.$success; + border-color: common.$success; } } &.btn--error { - color: colors.$error; - border-color: colors.$error; + color: common.$error; + border-color: common.$error; &:hover:not(:disabled) { - background-color: colors.$error; - border-color: colors.$error; + background-color: common.$error; + border-color: common.$error; } } } @@ -85,10 +80,10 @@ $btn-disabled-color: rgba(colors.$primary, 0.45); flex-direction: row; justify-content: end; align-items: center; - margin-top: sizing.size(4); + margin-top: common.size(4); > :not(:last-child) { - margin-right: sizing.size(2); + margin-right: common.size(2); } } @@ -96,6 +91,6 @@ $btn-disabled-color: rgba(colors.$primary, 0.45); position: absolute; left: 50%; top: 50%; - @include icons.icon-xxl; + @include common.icon-xxl; transform: translate(-50%, -50%); } diff --git a/lib/styles/global/forms.scss b/lib/styles/global/forms.scss index 6e14af4f..8070a960 100644 --- a/lib/styles/global/forms.scss +++ b/lib/styles/global/forms.scss @@ -1,58 +1,27 @@ -@use '../common/colors'; -@use '../common/dark-mode'; -@use '../common/effects'; -@use '../common/sizing'; +@use '../common'; .input { - display: block; - width: 100%; - - border-radius: sizing.$border-sm-radius; - border: sizing.$border-md-size solid colors.$slate-400; - padding: sizing.size(2.5); - - font-size: 0.875rem; - line-height: 1.25rem; - - color: colors.$slate-900; - background-color: colors.$white; - outline: none; - - &:focus { - border-color: colors.$primary; - @include effects.ring(colors.$primary-400); - - @include dark-mode.dark { - @include effects.ring(colors.$primary-800); - } - } - - @include dark-mode.dark { - color: colors.$slate-200; - background-color: colors.$slate-950; - border-color: colors.$slate-500; - } + @include common.input-common; } .input--invalid { - border-color: colors.$error; + border-color: common.$error; &:focus { - border-color: colors.$error; + border-color: common.$error; - @include effects.ring(colors.$error); + @include common.ring(common.$error); } } .input--textarea { - height: sizing.size(24); - resize: vertical; + @include common.text-area; } .label { - color: colors.$black; + color: common.$black; - @include dark-mode.dark { - color: colors.$white; + @include common.dark { + color: common.$white; } } diff --git a/lib/styles/global/icons.scss b/lib/styles/global/icons.scss index 9442a8a8..e2c9b3a6 100644 --- a/lib/styles/global/icons.scss +++ b/lib/styles/global/icons.scss @@ -1,4 +1,4 @@ -@use '../common/icons'; +@use '../common'; .icon { width: 100%; @@ -8,25 +8,25 @@ } .icon-xs { - @include icons.icon-xs; + @include common.icon-xs; } .icon-sm { - @include icons.icon-sm; + @include common.icon-sm; } .icon-md { - @include icons.icon-md; + @include common.icon-md; } .icon-lg { - @include icons.icon-lg; + @include common.icon-lg; } .icon-xl { - @include icons.icon-xl; + @include common.icon-xl; } .icon-xxl { - @include icons.icon-xxl; + @include common.icon-xxl; } diff --git a/lib/styles/global/overlays.scss b/lib/styles/global/overlays.scss index 9c6debfc..86aab5ed 100644 --- a/lib/styles/global/overlays.scss +++ b/lib/styles/global/overlays.scss @@ -1,12 +1,11 @@ -@use '../common/colors'; -@use '../common/elevation'; +@use '../common'; .backdrop { - @include elevation.layer-40; + @include common.layer-40; position: fixed; top: 0; left: 0; right: 0; bottom: 0; - background-color: rgba(colors.$slate-800, 0.8); + background-color: rgba(common.$slate-800, 0.8); } diff --git a/lib/styles/global/typography.scss b/lib/styles/global/typography.scss index 5647fb2a..eb06b4da 100644 --- a/lib/styles/global/typography.scss +++ b/lib/styles/global/typography.scss @@ -1,53 +1,50 @@ -@use '../common/colors'; -@use '../common/dark-mode'; -@use '../common/sizing'; -@use '../common/typography'; +@use '../common'; %heading { margin-top: 0; - margin-bottom: sizing.size(3); - font-weight: typography.$font-weight-semibold; + margin-bottom: common.size(3); + font-weight: common.$font-weight-semibold; } h1, .h1 { @extend %heading; - @include typography.text-xxxxl; + @include common.text-xxxxl; } h2, .h2 { @extend %heading; - @include typography.text-xxxl; + @include common.text-xxxl; } h3, .h3 { @extend %heading; - @include typography.text-xxl; + @include common.text-xxl; } h4, .h4 { @extend %heading; - @include typography.text-xl; + @include common.text-xl; } h5, .h5 { @extend %heading; - @include typography.text-lg; + @include common.text-lg; } h6, .h6 { @extend %heading; - @include typography.text-md; + @include common.text-md; } p { margin-top: 0; - margin-bottom: sizing.size(3); + margin-bottom: common.size(3); a { text-decoration-line: underline; @@ -61,7 +58,7 @@ p { a { outline: none; - color: colors.$primary; + color: common.$primary; text-underline-offset: 2px; text-decoration-thickness: 1px; text-decoration: none; @@ -75,8 +72,8 @@ a { ul, ol { margin-top: 0; - margin-bottom: sizing.size(3); - padding-left: sizing.size(8); + margin-bottom: common.size(3); + padding-left: common.size(8); } ul { @@ -95,16 +92,16 @@ li { } blockquote { - @include sizing.mx(0); - @include sizing.my(4); - padding: sizing.size(4); + @include common.mx(0); + @include common.my(4); + padding: common.size(4); - border-left: sizing.$border-lg-size solid colors.$slate-500; - background-color: colors.$slate-200; + border-left: common.$border-lg-size solid common.$slate-500; + background-color: common.$slate-200; - @include dark-mode.dark { - color: colors.$slate-300; - background-color: colors.$slate-900; + @include common.dark { + color: common.$slate-300; + background-color: common.$slate-900; } & > p { @@ -113,31 +110,31 @@ blockquote { } hr { - margin-top: sizing.size(8); - margin-bottom: sizing.size(8); + margin-top: common.size(8); + margin-bottom: common.size(8); border: none; - background-color: colors.$slate-200; - height: sizing.$border-lg-size; + background-color: common.$slate-200; + height: common.$border-lg-size; - @include dark-mode.dark { - background-color: colors.$slate-900; + @include common.dark { + background-color: common.$slate-900; } } code { - font-family: typography.$font-family-monospace; - font-size: typography.$font-size-base; + font-family: common.$font-family-monospace; + font-size: common.$font-size-base; display: block; width: 100%; overflow-x: auto; - padding: sizing.size(4); - border-radius: sizing.$border-sm-radius; + padding: common.size(4); + border-radius: common.$border-sm-radius; - background-color: colors.$slate-200; - @include dark-mode.dark { - background-color: colors.$slate-900; + background-color: common.$slate-200; + @include common.dark { + background-color: common.$slate-900; } } @@ -146,7 +143,7 @@ code { overflow-x: hidden; white-space: nowrap; text-overflow: ellipsis; - margin-right: sizing.size(4); + margin-right: common.size(4); } .page-heading { @@ -154,7 +151,7 @@ code { flex-direction: row; justify-content: space-between; align-items: center; - margin-bottom: sizing.size(4); + margin-bottom: common.size(4); > * { margin-bottom: 0; diff --git a/lib/ui/src/components/atoms/overlay/overlay.tsx b/lib/ui/src/components/atoms/overlay/overlay.tsx index 094bb65f..6c42fd9f 100644 --- a/lib/ui/src/components/atoms/overlay/overlay.tsx +++ b/lib/ui/src/components/atoms/overlay/overlay.tsx @@ -19,7 +19,7 @@ const overlayAnimation: PropertyIndexedKeyframes = { scoped: true, }) export class OverlayComponent implements ComponentInterface { - @Prop() + @Prop({ reflect: true }) public isOpen = false; @Element() diff --git a/lib/ui/src/components/atoms/text-area/text-area.scss b/lib/ui/src/components/atoms/text-area/text-area.scss new file mode 100644 index 00000000..7b1f9543 --- /dev/null +++ b/lib/ui/src/components/atoms/text-area/text-area.scss @@ -0,0 +1,11 @@ +@use '~@cg/styles/common'; + +:host-context(.text-area) { + position: relative; + border-radius: common.$border-sm-radius; +} + +.text-area__input { + @include common.input-common; + @include common.text-area; +} diff --git a/lib/ui/src/components/atoms/text-area/text-area.tsx b/lib/ui/src/components/atoms/text-area/text-area.tsx new file mode 100644 index 00000000..741a3588 --- /dev/null +++ b/lib/ui/src/components/atoms/text-area/text-area.tsx @@ -0,0 +1,103 @@ +import { + AttachInternals, + Component, + ComponentInterface, + Host, + Method, + Prop, + State, + Watch, + h, +} from '@stencil/core'; + +@Component({ + tag: 'cg-text-area', + styleUrl: 'text-area.scss', + formAssociated: true, + scoped: true, +}) +export class TextArea implements ComponentInterface { + @Prop({ reflect: true }) + public value?: string; + + @Prop({ reflect: true }) + public placeholder?: string; + + @Prop({ reflect: true }) + public readonly = false; + + @State() + private isFocused = false; + + @AttachInternals() + private internals!: ElementInternals; + + private inputElem!: HTMLTextAreaElement; + + @Watch('value') + public valueChanged(): void { + this.setTextAreaHeight(); + } + + @Method() + public async select(): Promise { + this.inputElem.select(); + } + + public componentWillLoad() { + this.internals.setFormValue(this.value ?? null); + } + + public componentDidLoad() { + this.setTextAreaHeight(); + } + + public render() { + return ( + + + +