diff --git a/.vscode/settings.json b/.vscode/settings.json index 6cef7d76f..b57665d55 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,9 @@ { - "files.exclude": { - "**/.clj-kondo": true, - "**/.cpcache": true, - "**/.lsp": true, - "**/.shadow-cljs": true, - "**/node_modules": true - } + "files.exclude": { + "**/.clj-kondo": true, + "**/.cpcache": true, + "**/.lsp": true, + "**/.shadow-cljs": true, + "**/node_modules": true + } } diff --git a/frontend/resources/images/icons/msg-error-refactor.svg b/frontend/resources/images/icons/msg-error-refactor.svg new file mode 100644 index 000000000..a5871b29f --- /dev/null +++ b/frontend/resources/images/icons/msg-error-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/msg-neutral-refactor.svg b/frontend/resources/images/icons/msg-neutral-refactor.svg new file mode 100644 index 000000000..63dec6c2b --- /dev/null +++ b/frontend/resources/images/icons/msg-neutral-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/msg-success-refactor.svg b/frontend/resources/images/icons/msg-success-refactor.svg new file mode 100644 index 000000000..f7cf5e6de --- /dev/null +++ b/frontend/resources/images/icons/msg-success-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/msg-warning-refactor.svg b/frontend/resources/images/icons/msg-warning-refactor.svg new file mode 100644 index 000000000..df700ade5 --- /dev/null +++ b/frontend/resources/images/icons/msg-warning-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/tree-refactor.svg b/frontend/resources/images/icons/tree-refactor.svg new file mode 100644 index 000000000..b9405eaac --- /dev/null +++ b/frontend/resources/images/icons/tree-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/user-refactor.svg b/frontend/resources/images/icons/user-refactor.svg new file mode 100644 index 000000000..0d763101a --- /dev/null +++ b/frontend/resources/images/icons/user-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/styles/common/refactor/basic-rules.scss b/frontend/resources/styles/common/refactor/basic-rules.scss index b9d9d85a7..76cf29754 100644 --- a/frontend/resources/styles/common/refactor/basic-rules.scss +++ b/frontend/resources/styles/common/refactor/basic-rules.scss @@ -66,7 +66,8 @@ } } &:global(.disabled), - &[disabled] { + &[disabled], + &:disabled { background-color: var(--button-background-color-disabled); border: $s-1 solid var(--button-border-color-disabled); color: var(--button-foreground-color-disabled); @@ -107,7 +108,8 @@ } } &:global(.disabled), - &[disabled] { + &[disabled], + &:disabled { background-color: var(--button-background-color-disabled); border: $s-1 solid var(--button-border-color-disabled); color: var(--button-foreground-color-disabled); @@ -147,7 +149,8 @@ } } &:global(.disabled), - &[disabled] { + &[disabled], + &:disabled { color: var(--button-foreground-color-disabled); cursor: unset; pointer-events: none; @@ -202,9 +205,9 @@ .button-disabled { @include buttonStyle; @include flexCenter; - background-color: var(--button-disabled-background-color-rest); - border: $s-1 solid var(--button-disabled-border-color-rest); - color: var(--button-disabled-foreground-color-rest); + background-color: var(--button-background-color-disabled); + border: $s-1 solid var(--button-border-color-disabled); + color: var(--button-foreground-color-disabled); cursor: unset; } @@ -254,7 +257,6 @@ } // INPUTS - .input-base { @include removeInputStyle; @include titleTipography; @@ -313,9 +315,11 @@ input { @extend .input-base; } + ::placeholder { color: var(--input-placeholder-color); } + &:hover { span { color: var(--input-foreground-color); @@ -327,9 +331,48 @@ } } +.input-element-label { + @include titleTipography; + display: flex; + align-items: flex-start; + padding: 0; + input { + @extend .input-base; + padding-left: $s-8; + display: flex; + align-items: flex-start; + height: $s-32; + box-sizing: border-box; + width: 100%; + margin: 0; + border-radius: $br-8; + border: $s-1 solid var(--input-background-color); + color: var(--input-foreground-color-active); + background-color: var(--input-background-color); + } + ::placeholder { + color: var(--input-placeholder-color); + } + &:hover { + input { + color: var(--input-foreground-color-active); + background-color: var(--input-background-color-hover); + } + } + + &:focus-within, + &:active { + input { + color: var(--input-foreground-color-active); + background-color: var(--input-background-color-active); + border: $s-1 solid var(--input-border-color-active); + } + } +} + .disabled-input { background-color: var(--input-background-color-disabled); - border: 1px solid var(--input-border-color-disabled); + border: $s-1 solid var(--input-border-color-disabled); color: var(--input-foreground-color-disabled); input { pointer-events: none; @@ -348,17 +391,40 @@ min-width: $s-16; min-height: $s-16; border-radius: $br-6; - background-color: var(--input-background-color); + background-color: var(--input-checkbox-background-color-rest); + border: $s-1 solid var(--input-checkbox-background-color-rest); svg { display: none; } + &:hover { + border-color: var(--input-checkbox-border-color-hover); + } + &:focus { + border-color: var(--input-checkbox-border-color-focus); + } &:global(.checked) { background-color: var(--input-border-color-active); + border-color: var(--input-checkbox-border-color-active); svg { @extend .button-icon-small; stroke: var(--input-details-color); } } + &:global(.intermediate) { + background-color: var(--input-checkbox-background-color-intermediate); + border-color: var(--input-checkbox-border-color-active); + svg { + @extend .button-icon-small; + stroke: var(--input-details-color); + } + } + &:global(.unchecked) { + background-color: var(--input-checkbox-background-color-rest); + border: $s-1 solid var(--input-checkbox-background-color-rest); + svg { + display: none; + } + } } .input-checkbox { @@ -376,6 +442,53 @@ input { margin: 0; } + &:hover { + span { + border-color: var(--input-checkbox-border-color-hover); + } + } + } +} + +.input-with-label { + display: flex; + flex-direction: column; + label { + @include titleTipography; + display: flex; + flex-direction: column; + justify-content: flex-start; + min-height: $s-16; + color: var(--input-foreground-color-active); + } + + input { + @extend .input-base; + @include titleTipography; + border-radius: $br-8; + height: $s-32; + min-height: $s-32; + margin-top: $s-8; + background-color: var(--input-background-color); + border: $s-1 solid var(--input-background-color); + color: var(--input-foreground-color-active); + &:focus-within, + &:active { + input { + color: var(--input-foreground-color-active); + } + background-color: var(--input-background-color-active); + border: $s-1 solid var(--input-border-color-active); + } + } + &:global(.disabled) { + @extend .disabled-input; + } + + &:global(.invalid) { + input { + border: $s-1 solid var(--input-border-color-error); + } } } @@ -392,6 +505,83 @@ background-color: var(--modal-background-color); } +.modal-overlay-base { + @include flexCenter; + position: fixed; + left: 0; + top: 0; + height: 100%; + width: 100%; + z-index: $z-index-modal; + background-color: var(--color-background-subtle); +} + +.modal-container-base { + position: relative; + padding: $s-32; + border-radius: $br-8; + background-color: var(--modal-background-color); + min-width: $s-364; + min-height: $s-192; + max-width: $s-512; + max-height: $s-512; +} + +.modal-close-btn-base { + @extend .button-tertiary; + position: absolute; + top: $s-8; + right: $s-6; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } +} + +.modal-hint-base { + @include titleTipography; + color: var(--modal-title-foreground-color); + border-top: $s-1 solid var(--modal-hint-border-color); + border-bottom: $s-1 solid var(--modal-hint-border-color); +} + +.modal-action-btns { + display: flex; + justify-content: flex-end; + gap: $s-16; +} + +.modal-cancel-btn { + @extend .button-secondary; + @include tabTitleTipography; + padding: $s-8 $s-24; + border-radius: $br-8; + height: $s-32; + margin: 0; +} + +.modal-accept-btn { + @extend .button-primary; + @include tabTitleTipography; + padding: $s-8 $s-24; + border-radius: $br-8; + height: $s-32; + margin: 0; +} + +.modal-danger-btn { + @extend .button-primary; + @include tabTitleTipography; + padding: $s-8 $s-24; + border-radius: $br-8; + height: $s-32; + margin: 0; + background-color: var(--modal-button-background-color-error); + border: $s-1 solid var(--modal-button-background-color-error); + color: var(--modal-button-foreground-color-error); +} + // UI ELEMENTS .asset-element { @include titleTipography; @@ -641,3 +831,13 @@ background-color: var(--menu-background-color); color: var(--menu-foreground-color); } + +.select-wrapper { + @include titleTipography; + position: relative; + display: flex; + align-items: center; + justify-content: normal; + width: 100%; + overflow: hidden; +} diff --git a/frontend/resources/styles/common/refactor/color-defs.scss b/frontend/resources/styles/common/refactor/color-defs.scss index 41b0077ce..b1c0a2b0f 100644 --- a/frontend/resources/styles/common/refactor/color-defs.scss +++ b/frontend/resources/styles/common/refactor/color-defs.scss @@ -21,9 +21,9 @@ // NOTIFICATION --dark-ok-color: var(--strong-green); - --dark-warning-color: #ff9b49; + --dark-warning-color: #ff6432; --dark-pending-color: var(--lilac); - --dark-error-color: #ff4986; + --dark-error-color: #ec1f1f; // LIGHT --light-gray-1: #fff; diff --git a/frontend/resources/styles/common/refactor/design-tokens.scss b/frontend/resources/styles/common/refactor/design-tokens.scss index 829364a1f..cbaaba240 100644 --- a/frontend/resources/styles/common/refactor/design-tokens.scss +++ b/frontend/resources/styles/common/refactor/design-tokens.scss @@ -7,6 +7,7 @@ .light, .default { + // BASE COLORS --canvas-background-color: var(--color-background-primary); --canvas-fill-color: var(--canvas-color); @@ -15,7 +16,6 @@ --panel-title-background-color: var(--color-background-secondary); // BUTTONS - --button-foreground-hover: var(--color-accent-primary); --button-background-color-focus: var(--color-background-secondary); --button-foreground-color-focus: var(--color-foreground-primary); @@ -88,22 +88,19 @@ --constraint-widget-background-color: var(--color-background-tertiary); --constraint-center-area-background-color: var(--color-background-primary); - // ICONS - --icon-foreground: var(--color-foreground-secondary); - --icon-foreground-hover: var(--color-foreground-primary); - - --link-foreground-color: var(--color-accent-primary); - + // TABS --tab-background-color-hover: var(--color-background-primary); --tab-background-color-selected: var(--color-background-quaternary); --tab-foreground-color: var(--color-foreground-secondary); --tab-foreground-color-hover: var(--color-foreground-primary); --tab-foreground-color-selected: var(--color-accent-primary); + // SECTION TITLE --title-background-color: var(--color-background-primary); --title-foreground-color: var(--color-foreground-secondary); --title-foreground-color-hover: var(--color-foreground-primary); + // LAYER ELEMENT --layer-row-background-color: var(--color-background-primary); --layer-row-background-color-hover: var(--color-background-secondary); --layer-row-background-color-selected: var(--color-background-quaternary); @@ -118,9 +115,26 @@ --layer-child-row-foreground-color: var(--color-foreground-secondary); --layer-row-component-foreground-color: var(--color-accent-secondary); - --search-bar-background-color: var(--color-background-primary); - --search-bar-input-background-color: var(--color-background-tertiary); - --search-bar-input-border-color: var(--color-background-tertiary); + // PALETTE & COLOR BULLET + --palette-background-color: var(--color-background-primary); + --palette-btn-background-color-selected: var(--color-background-secondary); + --palette-btn-border-color-selected: var(--color-background-quaternary); + --palette-btn-foreground-color-selected: var(--color-accent-primary); + --palette-text-color: var(--color-foreground-secondary); + --palette-text-color-selected: var(--color-foreground-primary); + --palette-text-background-color: var(--color-background-tertiary); + --palette-text-background-color-hover: var(--color-background-quaternary); + --palette-button-shadow-initial: var(--color-background-primary); + --palette-button-shadow-final: transparent; + --palette-handler-background-color: var(--color-background-quaternary); + + --color-bullet-background-color: var(--white); // We don't want this color to change with palette + --color-bullet-border-color: var(--color-background-quaternary); + --color-bullet-border-color-selected: var(--color-accent-primary); + + // ICONS + --icon-foreground: var(--color-foreground-secondary); + --icon-foreground-hover: var(--color-foreground-primary); // INPUTS, SELECTS, DROPDOWNS --input-background-color: var(--color-background-tertiary); @@ -136,7 +150,14 @@ --input-background-color-disabled: var(--color-background-primary); --input-foreground-color-disabled: var(--color-foreground-secondary); --input-border-color-disabled: var(--color-background-quaternary); + --input-border-color-error: var(--error-color); --input-details-color: var(--color-background-primary); + --input-checkbox-background-color-rest: var(--color-background-quaternary); + --input-checkbox-border-color-active: var(--color-background-quaternary); + --input-checkbox-border-color-focus: var(--color-accent-primary); + --input-checkbox-border-color: var(--color-background-secondary); + --input-checkbox-border-color-hover: var(--color-accent-primary-muted); + --input-checkbox-background-color-intermediate: var(--color-foreground-secondary); --menu-background-color: var(--color-background-tertiary); --menu-foreground-color: var(--color-foreground-primary); @@ -156,32 +177,12 @@ --menu-foreground-color-disabled: var(--color-foreground-secondary); --menu-border-color-disabled: var(--color-background-quaternary); - --pill-background-color: var(--color-background-tertiary); - --pill-foreground-color: var(--color-foreground-primary); - - --tag-background-color: var(--color-accent-primary); - - --palette-background-color: var(--color-background-primary); - --palette-btn-background-color-selected: var(--color-background-secondary); - --palette-btn-border-color-selected: var(--color-background-quaternary); - --palette-btn-foreground-color-selected: var(--color-accent-primary); - --palette-text-color: var(--color-foreground-secondary); - --palette-text-color-selected: var(--color-foreground-primary); - --palette-text-background-color: var(--color-background-tertiary); - --palette-text-background-color-hover: var(--color-background-quaternary); - --palette-button-shadow-initial: var(--color-background-primary); - --palette-button-shadow-final: transparent; - --context-menu-background-color: var(--color-background-tertiary); --context-menu-foreground-color: var(--color-foreground-secondary); --context-menu-background-color-selected: var(--color-background-quaternary); --context-menu-foreground-color-selected: var(--color-foreground-primary); - --color-bullet-background-color: var(--white); // We don't want this color to change with palette - --color-bullet-border-color: var(--color-background-quaternary); - --color-bullet-border-color-selected: var(--color-accent-primary); - --palette-handler-background-color: var(--color-background-quaternary); - + // ASSETS --assets-title-background-color: var(--color-background-primary); --assets-item-background-color: var(--color-background-tertiary); --assets-item-background-color-hover: var(--color-background-quaternary); @@ -220,19 +221,10 @@ --empty-message-background-color: var(--color-background-tertiary); --empty-message-foreground-color: var(--color-foreground-secondary); - --status-ok-background-color: var(--ok-color); - --status-warning-background-color: var(--warning-color); - --status-pending-background-color: var(--pending-color); - --status-error-background-color: var(--error-color); - --status-icon-foreground-color: var(--color-background-primary); - --user-count-background-color: var(--color-background-secondary); --user-count-foreground-color: var(--color-accent-primary); - --modal-background-color: var(--color-background-primary); - --modal-foreground-color: var(--color-foreground-primary); - --modal-foreground-color-secondary: var(--color-foreground-secondary); - + // COLORPICKER --colorpicker-details-color: var(--color-background-quaternary); --colorpicker-details-color-selected: var(--color-accent-primary); --colorpicker-handlers-color: var(--color-foreground-primary); @@ -259,4 +251,43 @@ --grid-editor-line-color: var(--color-foreground-tertiary); --grid-editor-plus-btn-foreground: var(--white); --grid-editor-plus-btn-background: var(--color-foreground-tertiary); + + // MODALS + --modal-background-color: var(--color-background-primary); + --modal-title-foreground-color: var(--color-foreground-primary); + --modal-text-foreground-color: var(--color-foreground-secondary); + --modal-hint-border-color: var(--color-background-quaternary); + --modal-button-background-color-error: var(--error-color); + --modal-button-foreground-color-error: var(--color-foreground-primary); + --modal-link-foreground-color: var(--color-accent-primary); + --modal-border-color: var(--color-background-quaternary); + + // ALERTS & STATUS + --alert-background-color-ok: var(--ok-color); + --alert-foreground-color-ok: var(--color-background-secondary); + --alert-background-color-warning: var(--warning-color); + --alert-foreground-color-warning: var(--color-foreground-primary); + --alert-background-color-error: var(--error-color); + --alert-foreground-color-error: var(--color-foreground-primary); + --alert-background-color-neutral: var(--color-background-quaternary); + --alert-foreground-color-neutral: var(--color-foreground-secondary); + --alert-foreground-color-neutral-active: var(--color-foreground-primary); + + --status-ok-background-color: var(--ok-color); + --status-warning-background-color: var(--warning-color); + --status-pending-background-color: var(--pending-color); + --status-error-background-color: var(--error-color); + --status-icon-foreground-color: var(--color-background-primary); + + // INTERFACE ELEMENTS + --search-bar-background-color: var(--color-background-primary); + --search-bar-input-background-color: var(--color-background-tertiary); + --search-bar-input-border-color: var(--color-background-tertiary); + + --pill-background-color: var(--color-background-tertiary); + --pill-foreground-color: var(--color-foreground-primary); + + --tag-background-color: var(--color-accent-primary); + + --link-foreground-color: var(--color-accent-primary); } diff --git a/frontend/resources/styles/common/refactor/fonts.scss b/frontend/resources/styles/common/refactor/fonts.scss index ebc5b5e15..ef4df7c8c 100644 --- a/frontend/resources/styles/common/refactor/fonts.scss +++ b/frontend/resources/styles/common/refactor/fonts.scss @@ -7,24 +7,15 @@ @use "sass:math"; @import "common/dependencies/mixin"; -// Font sizes -$fs10: 0.625rem; -$fs-11: 0.688rem; -$fs12: 0.75rem; -$fs14: 0.875rem; - // Typography scale $fs-base: 16; -$fs-9: math.div(9, $fs-base) + rem; $fs-10: math.div(10, $fs-base) + rem; +$fs-11: 0.688rem; $fs-12: math.div(12, $fs-base) + rem; $fs-14: math.div(14, $fs-base) + rem; $fs-16: math.div(16, $fs-base) + rem; -$fs-19: math.div(19, $fs-base) + rem; -$fs-25: math.div(25, $fs-base) + rem; -$fs-33: math.div(33, $fs-base) + rem; -$fs-44: math.div(44, $fs-base) + rem; +$fs-24: math.div(24, $fs-base) + rem; // Font weight $fw400: 400; // Regular (CSS value: 'normal') diff --git a/frontend/resources/styles/common/refactor/mixins.scss b/frontend/resources/styles/common/refactor/mixins.scss index 744e2abcd..d5e088c12 100644 --- a/frontend/resources/styles/common/refactor/mixins.scss +++ b/frontend/resources/styles/common/refactor/mixins.scss @@ -49,13 +49,20 @@ line-height: 1.2; } -@mixin titleBigTipography { +@mixin medTitleTipography { font-family: "worksans", sans-serif; font-size: $fs-14; font-weight: $fw400; line-height: 1.2; } +@mixin bigTitleTipography { + font-family: "worksans", sans-serif; + font-size: $fs-24; + font-weight: $fw400; + line-height: 1.2; +} + @mixin textEllipsis { max-width: 99%; overflow: hidden; diff --git a/frontend/resources/styles/common/refactor/shadows.scss b/frontend/resources/styles/common/refactor/shadows.scss index bcc0f1718..ec1e172d2 100644 --- a/frontend/resources/styles/common/refactor/shadows.scss +++ b/frontend/resources/styles/common/refactor/shadows.scss @@ -7,3 +7,7 @@ @mixin menuShadow { box-shadow: 0px 0px $s-12 0px var(--menu-shadow-color); } + +@mixin alertShadow { + box-shadow: 0px $s-4 $s-4 var(--menu-shadow-color); +} diff --git a/frontend/resources/styles/common/refactor/spacing.scss b/frontend/resources/styles/common/refactor/spacing.scss index b956d7cd1..2b046700c 100644 --- a/frontend/resources/styles/common/refactor/spacing.scss +++ b/frontend/resources/styles/common/refactor/spacing.scss @@ -70,12 +70,17 @@ $s-284: calc(var(--s-4) * 71); $s-300: calc(var(--s-4) * 75); $s-320: calc(var(--s-4) * 80); $s-348: calc(var(--s-4) * 87); +$s-356: calc(var(--s-4) * 89); +$s-364: calc(var(--s-4) * 91); $s-380: calc(var(--s-4) * 95); $s-400: calc(var(--s-4) * 100); +$s-408: calc(var(--s-4) * 102); +$s-440: calc(var(--s-4) * 110); $s-480: calc(var(--s-4) * 120); $s-500: calc(var(--s-4) * 125); $s-512: calc(var(--s-4) * 128); $s-520: calc(var(--s-4) * 130); +$s-640: calc(var(--s-4) * 160); $s-664: calc(var(--s-4) * 166); $s-712: calc(var(--s-4) * 178); $s-736: calc(var(--s-4) * 184); diff --git a/frontend/resources/styles/common/refactor/z-index.scss b/frontend/resources/styles/common/refactor/z-index.scss index 7e20a36f1..fed8066ce 100644 --- a/frontend/resources/styles/common/refactor/z-index.scss +++ b/frontend/resources/styles/common/refactor/z-index.scss @@ -11,3 +11,4 @@ $z-index-4: 4; // modal $z-index-10: 10; $z-index-20: 20; $z-index-modal: 30; // When refactor finish we can reduce this number, +$z-index-alert: 40; // When refactor finish we can reduce this number, diff --git a/frontend/resources/styles/main/partials/colorpicker.scss b/frontend/resources/styles/main/partials/colorpicker.scss index 2ad36757d..e70546d65 100644 --- a/frontend/resources/styles/main/partials/colorpicker.scss +++ b/frontend/resources/styles/main/partials/colorpicker.scss @@ -315,7 +315,7 @@ .slider-selector.opacity { grid-area: "opacity"; - align-self: start; + align-self: flex-start; } } diff --git a/frontend/resources/styles/main/partials/modal.scss b/frontend/resources/styles/main/partials/modal.scss index 7498ef5b2..7615ef663 100644 --- a/frontend/resources/styles/main/partials/modal.scss +++ b/frontend/resources/styles/main/partials/modal.scss @@ -1522,7 +1522,7 @@ flex-grow: 1; display: flex; flex-direction: column; - justify-content: start; + justify-content: flex-start; align-items: flex-start; row-gap: 1.5rem; .custom-input { diff --git a/frontend/src/app/main/ui/alert.cljs b/frontend/src/app/main/ui/alert.cljs index 1acb30eb8..7511c390b 100644 --- a/frontend/src/app/main/ui/alert.cljs +++ b/frontend/src/app/main/ui/alert.cljs @@ -5,9 +5,11 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.alert + (:require-macros [app.main.style :as stl]) (:require [app.main.data.modal :as modal] [app.main.store :as st] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -25,7 +27,8 @@ hint accept-label accept-style] :as props}] - (let [on-accept (or on-accept identity) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + on-accept (or on-accept identity) message (or message (tr "ds.alert-title")) accept-label (or accept-label (tr "ds.alert-ok")) accept-style (or accept-style :danger) @@ -47,29 +50,54 @@ (on-accept props)))] (->> (events/listen js/document "keydown" on-keydown) (partial events/unlistenByKey)))) + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} title] + [:button {:class (stl/css :modal-close-btn) + :on-click accept-fn} i/close-refactor]] - [:div.modal-overlay - [:div.modal-container.alert-dialog - [:div.modal-header - [:div.modal-header-title - [:h2 title]] - [:div.modal-close-button - {:on-click accept-fn} i/close]] + [:div {:class (stl/css :modal-content)} + (when (and (string? message) (not= message "")) + [:h3 {:class (stl/css :modal-msg)} message]) + (when (and (string? scd-message) (not= scd-message "")) + [:h3 {:class (stl/css :modal-scd-msg)} scd-message]) + (when (string? hint) + [:p {:class (stl/css :modal-hint)} hint])] - [:div.modal-content - (when (and (string? message) (not= message "")) - [:h3 message]) - (when (and (string? scd-message) (not= scd-message "")) - [:h3 scd-message]) - (when (string? hint) - [:p hint])] + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + [:input {:class (stl/css-case :accept-btn true + :danger (= accept-style :danger) + :primary (= accept-style :primary)) + :type "button" + :value accept-label + :on-click accept-fn}]]]]] - [:div.modal-footer - [:div.action-buttons - [:input.accept-button - {:class (dom/classnames - :danger (= accept-style :danger) - :primary (= accept-style :primary)) - :type "button" - :value accept-label - :on-click accept-fn}]]]]])) + + [:div.modal-overlay + [:div.modal-container.alert-dialog + [:div.modal-header + [:div.modal-header-title + [:h2 title]] + [:div.modal-close-button + {:on-click accept-fn} i/close]] + + [:div.modal-content + (when (and (string? message) (not= message "")) + [:h3 message]) + (when (and (string? scd-message) (not= scd-message "")) + [:h3 scd-message]) + (when (string? hint) + [:p hint])] + + [:div.modal-footer + [:div.action-buttons + [:input.accept-button + {:class (dom/classnames + :danger (= accept-style :danger) + :primary (= accept-style :primary)) + :type "button" + :value accept-label + :on-click accept-fn}]]]]]))) diff --git a/frontend/src/app/main/ui/alert.scss b/frontend/src/app/main/ui/alert.scss new file mode 100644 index 000000000..fd7eee9a4 --- /dev/null +++ b/frontend/src/app/main/ui/alert.scss @@ -0,0 +1,51 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.modal-overlay { + @extend .modal-overlay-base; + &.transparent { + background-color: transparent; + } + .modal-container { + @extend .modal-container-base; + .modal-header { + margin-bottom: $s-24; + .modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); + } + .modal-close-btn { + @extend .modal-close-btn-base; + } + } + .modal-content { + @include titleTipography; + margin-bottom: $s-24; + .modal-msg, + .modal-scd-msg, + .modal-hint, + .modal-subtitle { + @include titleTipography; + } + } + .modal-footer { + .action-buttons { + @extend .modal-action-btns; + .cancel-button { + @extend .modal-cancel-btn; + } + .accept-btn { + @extend .modal-accept-btn; + &.danger { + @extend .modal-danger-btn; + } + } + } + } + } +} diff --git a/frontend/src/app/main/ui/components/forms.cljs b/frontend/src/app/main/ui/components/forms.cljs index 64f4e4103..a29a3f001 100644 --- a/frontend/src/app/main/ui/components/forms.cljs +++ b/frontend/src/app/main/ui/components/forms.cljs @@ -5,9 +5,11 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.components.forms + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dm] + [app.main.ui.context :as ctx] [app.main.ui.hooks :as hooks] [app.main.ui.icons :as i] [app.util.dom :as dom] @@ -24,11 +26,13 @@ (def use-form fm/use-form) (mf/defc input - [{:keys [label help-icon disabled form hint trim children data-test on-change-value] :as props}] - (let [input-type (get props :type "text") + [{:keys [label help-icon disabled form hint trim children data-test on-change-value placeholder] :as props}] + (let [new-css-system (mf/use-ctx ctx/new-css-system) + input-type (get props :type "text") input-name (get props :name) more-classes (get props :class) auto-focus? (get props :auto-focus? false) + placeholder (or placeholder label) form (or form (mf/use-ctx form-ctx)) @@ -43,6 +47,7 @@ touched? (get-in @form [:touched input-name]) error (get-in @form [:errors input-name]) + value (get-in @form [:data input-name] "") help-icon' (cond @@ -71,6 +76,13 @@ :input-radio is-radio? :input-checkbox is-checkbox?)) + new-classes (dm/str more-classes " " + (stl/css-case + :input-wrapper true + :global/invalid (and touched? error) + :checkbox is-checkbox? + :global/disabled disabled)) + swap-text-password (fn [] (swap! type' (fn [input-type] @@ -96,6 +108,8 @@ (when-not (get-in @form [:touched input-name]) (swap! form assoc-in [:touched input-name] true))) + + props (-> props (dissoc :help-icon :form :trim :children) (assoc :id (name input-name) @@ -104,7 +118,7 @@ :on-click (when (or is-radio? is-checkbox?) on-click) :on-focus on-focus :on-blur on-blur - :placeholder label + :placeholder placeholder :on-change on-change :type @type' :tab-index "0") @@ -113,30 +127,67 @@ "aria-describedby" (dm/str "error-" input-name))) (obj/clj->props))] - [:div - {:class klass} - [:* - [:> :input props] - (cond - (some? label) - [:label {:for (name input-name)} label] + (if new-css-system + [:div {:class new-classes} + [:* + (cond + (some? label) + [:label {:class (stl/css-case :input-with-label (not is-checkbox?) + :input-label is-text? + :radio-label is-radio? + :checkbox-label is-checkbox?) + :tab-index "0" + :for (name input-name)} label + (when is-checkbox? + [:span {:class (stl/css-case :global/checked value)} i/status-tick-refactor]) + [:> :input props]] - (some? children) - [:label {:for (name input-name)} children]) + (some? children) + [:label {:for (name input-name)} - (when help-icon' - [:div.help-icon - {:style {:cursor "pointer"} - :on-click (when (= "password" input-type) - swap-text-password)} - help-icon']) - (cond - (and touched? (:message error)) - [:span.error {:id (dm/str "error-" input-name) - :data-test (clojure.string/join [data-test "-error"])} (tr (:message error))] + [:> :input props] + children]) - (string? hint) - [:span.hint hint])]])) + (when help-icon' + [:span {:class (stl/css :help-icon) + :on-click (when (= "password" input-type) + swap-text-password)} + help-icon']) + (cond + (and touched? (:message error)) + [:div {:id (dm/str "error-" input-name) + :class (stl/css :error) + :data-test (clojure.string/join [data-test "-error"])} + (tr (:message error))] + + (string? hint) + [:div {:class (stl/css :hint)} hint])]] + + + [:div + {:class klass} + [:* + [:> :input props] + (cond + (some? label) + [:label {:for (name input-name)} label] + + (some? children) + [:label {:for (name input-name)} children]) + + (when help-icon' + [:div.help-icon + {:style {:cursor "pointer"} + :on-click (when (= "password" input-type) + swap-text-password)} + help-icon']) + (cond + (and touched? (:message error)) + [:span.error {:id (dm/str "error-" input-name) + :data-test (clojure.string/join [data-test "-error"])} (tr (:message error))] + + (string? hint) + [:span.hint hint])]]))) (mf/defc textarea [{:keys [label disabled form hint trim] :as props}] @@ -195,7 +246,8 @@ (mf/defc select [{:keys [options disabled label form default data-test] :as props :or {default ""}}] - (let [input-name (get props :name) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + input-name (get props :name) form (or form (mf/use-ctx form-ctx)) value (or (get-in @form [:data input-name]) default) cvalue (d/seek #(= value (:value %)) options) @@ -214,31 +266,57 @@ (fn [_] (reset! focus? false))] - [:div.custom-select - [:select {:value value - :on-change on-change - :on-focus on-focus - :on-blur on-blur - :disabled disabled - :data-test data-test} - (for [item options] - [:> :option (clj->js (cond-> {:key (:value item) :value (:value item)} - (:disabled item) (assoc :disabled "disabled") - (:hidden item) (assoc :style {:display "none"}))) - (:label item)])] + (if new-css-system + [:div {:class (stl/css :custom-select)} + [:select {:value value + :on-change on-change + :on-focus on-focus + :on-blur on-blur + :disabled disabled + :data-test data-test} + (for [item options] + [:> :option (clj->js (cond-> {:key (:value item) :value (:value item)} + (:disabled item) (assoc :disabled "disabled") + (:hidden item) (assoc :style {:display "none"}))) + (:label item)])] - [:div.input-container {:class (dom/classnames :disabled disabled :focus @focus?)} - [:div.main-content - [:label label] - [:span.value (:label cvalue "")]] + [:div {:class (stl/css-case :input-container true + :disabled disabled + :focus @focus?)} + [:div {:class (stl/css :main-content)} + [:label {:class (stl/css :label)} label] + [:span {:class (stl/css :value)} (:label cvalue "")]] - [:div.icon - i/arrow-slide]]])) + [:div {:class (stl/css :icon)} + i/arrow-refactor]]] + + + [:div.custom-select + [:select {:value value + :on-change on-change + :on-focus on-focus + :on-blur on-blur + :disabled disabled + :data-test data-test} + (for [item options] + [:> :option (clj->js (cond-> {:key (:value item) :value (:value item)} + (:disabled item) (assoc :disabled "disabled") + (:hidden item) (assoc :style {:display "none"}))) + (:label item)])] + + [:div.input-container {:class (dom/classnames :disabled disabled :focus @focus?)} + [:div.main-content + [:label label] + [:span.value (:label cvalue "")]] + + [:div.icon + i/arrow-slide]]]))) (mf/defc radio-buttons {::mf/wrap-props false} [props] - (let [form (or (unchecked-get props "form") + (let [new-css-system (mf/use-ctx ctx/new-css-system) + form (or (unchecked-get props "form") (mf/use-ctx form-ctx)) name (unchecked-get props "name") @@ -261,28 +339,56 @@ (when (fn? on-change) (on-change name value)))))] + (if new-css-system + [:div {:class (stl/css :custom-radio)} + (for [{:keys [image value label]} options] + (let [image? (some? image) + value' (encode-fn value) + checked? (= value current-value) + key (str/ffmt "%-%" name value')] + [:label {:for key + :style {:background-image (when image? (str/ffmt "url(%)" image))} + :class (stl/css-case :radio-label true + :global/checked checked? + :with-image image?)} + [:input {:on-change on-change' + :type "radio" + :class (stl/css :radio-input) + :id key + :name name + :value value' + :checked checked?}] + (when (not image?) + [:span {:class (stl/css-case :radio-icon true + :global/checked checked?)} + (when checked? [:span {:class (stl/css :radio-dot)}])]) - [:div.custom-radio - (for [{:keys [image value label]} options] - (let [image? (some? image) - value' (encode-fn value) - key (str/ffmt "%-%" name value')] - [:div.input-radio {:key key :class (when image? "with-image")} - [:input {:on-change on-change' - :type "radio" - :id key - :name name - :value value' - :checked (= value current-value)}] - [:label {:for key - :style {:background-image (when image? (str/ffmt "url(%)" image))} - :class (when image? "with-image")} - label]]))])) + label]))] + + + + [:div.custom-radio + (for [{:keys [image value label]} options] + (let [image? (some? image) + value' (encode-fn value) + key (str/ffmt "%-%" name value')] + [:div.input-radio {:key key :class (when image? "with-image")} + [:input {:on-change on-change' + :type "radio" + :id key + :name name + :value value' + :checked (= value current-value)}] + [:label {:for key + :style {:background-image (when image? (str/ffmt "url(%)" image))} + :class (when image? "with-image")} + label]]))]))) (mf/defc submit-button* {::mf/wrap-props false} [props] - (let [form (or (unchecked-get props "form") + (let [new-css-system (mf/use-ctx ctx/new-css-system) + form (or (unchecked-get props "form") (mf/use-ctx form-ctx)) label (unchecked-get props "label") @@ -296,6 +402,7 @@ (true? (unchecked-get props "disabled"))) klass (dm/str class " " (if disabled? "btn-disabled" "")) + new-klass (if disabled? (stl/css :btn-disabled) class) on-key-down (mf/use-fn @@ -310,7 +417,7 @@ (obj/set! "onKeyDown" on-key-down) (obj/set! "name" name) (obj/set! "label" mf/undefined) - (obj/set! "className" klass) + (obj/set! "className" (if new-css-system new-klass klass)) (obj/set! "type" "submit"))] [:> "button" props @@ -338,7 +445,8 @@ (mf/defc multi-input [{:keys [form label class name trim valid-item-fn caution-item-fn on-submit] :as props}] - (let [form (or form (mf/use-ctx form-ctx)) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + form (or form (mf/use-ctx form-ctx)) input-name (get props :name) touched? (get-in @form [:touched input-name]) error (get-in @form [:errors input-name]) @@ -360,10 +468,24 @@ :custom-multi-input true :custom-input true)) + + new-css-klass (str (get props :class) " " + (stl/css-case + :focus @focus? + :valid (and touched? (not error)) + :invalid (and touched? error) + :empty empty? + :custom-multi-input true)) + in-klass (str class " " (dom/classnames :no-padding (pos? (count @items)))) + new-css-in-klass (str class " " + (stl/css-case + :inside-input true + :no-padding (pos? (count @items)))) + on-focus (mf/use-fn #(reset! focus? true)) @@ -430,29 +552,59 @@ values (filterv #(:valid %) values)] (update-form! values))) - [:div {:class klass} - [:input {:id (name input-name) - :class in-klass - :type "text" - :auto-focus true - :on-focus on-focus - :on-blur on-blur - :on-key-down on-key-down - :value @value - :on-change on-change - :placeholder (when empty? label)}] - [:label {:for (name input-name)} label] + (if new-css-system + [:div {:class new-css-klass} + [:input {:id (name input-name) + :class new-css-in-klass + :type "text" + :auto-focus true + :on-focus on-focus + :on-blur on-blur + :on-key-down on-key-down + :value @value + :on-change on-change + :placeholder (when empty? label)}] + [:label {:for (name input-name)} label] - (when-let [items (seq @items)] - [:div.selected-items - (for [item items] - [:div.selected-item {:key (:text item) - :tab-index "0" - :on-key-down (partial manage-key-down item)} - [:span.around {:class (dom/classnames "invalid" (not (:valid item)) - "caution" (:caution item))} - [:span.text (:text item)] - [:span.icon {:on-click #(remove-item! item)} i/cross]]])])])) + (when-let [items (seq @items)] + [:div {:class (stl/css :selected-items)} + (for [item items] + [:div {:class (stl/css :selected-item) + :key (:text item) + :tab-index "0" + :on-key-down (partial manage-key-down item)} + [:span {:class (stl/css-case :around true + :invalid (not (:valid item)) + :caution (:caution item))} + [:span {:class (stl/css :text)} (:text item)] + [:button {:class (stl/css :icon) + :on-click #(remove-item! item)} i/close-refactor]]])])] + + + + [:div {:class klass} + [:input {:id (name input-name) + :class in-klass + :type "text" + :auto-focus true + :on-focus on-focus + :on-blur on-blur + :on-key-down on-key-down + :value @value + :on-change on-change + :placeholder (when empty? label)}] + [:label {:for (name input-name)} label] + + (when-let [items (seq @items)] + [:div.selected-items + (for [item items] + [:div.selected-item {:key (:text item) + :tab-index "0" + :on-key-down (partial manage-key-down item)} + [:span.around {:class (dom/classnames "invalid" (not (:valid item)) + "caution" (:caution item))} + [:span.text (:text item)] + [:span.icon {:on-click #(remove-item! item)} i/cross]]])])]))) ;; --- Validators diff --git a/frontend/src/app/main/ui/components/forms.scss b/frontend/src/app/main/ui/components/forms.scss new file mode 100644 index 000000000..707b1fc63 --- /dev/null +++ b/frontend/src/app/main/ui/components/forms.scss @@ -0,0 +1,336 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +// INPUT +.input-wrapper { + display: flex; + flex-direction: column; + gap: $s-6; + align-items: center; + .input-with-label { + @include flexColumn; + gap: $s-8; + @include titleTipography; + justify-content: flex-start; + align-items: flex-start; + height: 100%; + width: 100%; + padding: 0; + cursor: pointer; + color: var(--modal-title-foreground-color); + text-transform: none; + input { + @extend .input-element; + color: var(--input-foreground-color-active); + width: calc(100% - $s-1); + margin-top: 0; + } + // Input autofill + input:-webkit-autofill, + input:-webkit-autofill:hover, + input:-webkit-autofill:focus, + input:-webkit-autofill:active { + -webkit-text-fill-color: var(--input-foreground-color-active) !important; + -webkit-box-shadow: 0 0 0 28px var(--input-background-color) inset !important; + border: $s-1 solid var(--input-background-color); + } + &:focus { + outline: none; + border: $s-1 solid var(--input-border-color-focus); + border-radius: $br-8; + } + } + &:global(.invalid) { + input { + border: $s-1 solid var(--input-border-color-error); + @extend .disabled-input; + } + } +} + +.help-icon { + cursor: pointer; + svg { + @extend .button-icon-small; + stroke: var(--input-details-color); + } +} +.error { + color: var(--input-border-color-error); +} + +.hint { + @include titleTipography; + width: 99%; +} + +.checkbox { + @extend .input-checkbox; + .checkbox-label { + @include titleTipography; + display: flex; + align-items: center; + flex-direction: row-reverse; + gap: $s-6; + min-height: $s-32; + cursor: pointer; + span { + @extend .checkbox-icon; + } + input { + display: none !important; + } + &:hover { + span { + border-color: var(--input-checkbox-border-color-hover); + } + } + } +} + +// SELECT +.custom-select { + @extend .select-wrapper; + height: $s-32; + .input-container { + @include flexRow; + height: $s-32; + width: 100%; + border-radius: $br-8; + border: $s-1 solid var(--input-background-color); + color: var(--input-foreground-color-active); + background-color: var(--input-background-color); + .main-content { + @include flexColumn; + @include titleTipography; + position: relative; + justify-content: center; + flex-grow: 1; + height: 100%; + padding: $s-8; + + .label { + color: var(--input-foreground-color); + } + .value { + width: 100%; + padding: 0px; + margin: 0px; + border: 0px; + color: var(--input-foreground-color-active); + } + } + .icon { + @include flexCenter; + height: $s-32; + width: $s-24; + pointer-events: none; + svg { + @extend .button-icon-small; + stroke: var(--icon-foreground); + transform: rotate(90deg); + } + } + + &.disabled { + background-color: var(--input-background-color-disabled); + border: $s-1 solid var(--input-border-color-disabled); + color: var(--input-foreground-color-disabled); + } + &.focus { + outline: none; + color: var(--input-foreground-color-active); + background-color: var(--input-background-color-active); + border: $s-1 solid var(--input-border-color-active); + } + } + + select { + @extend .menu-dropdown; + @include titleTipography; + box-sizing: border-box; + position: absolute; + top: 0; + left: 0; + min-height: $s-32; + height: auto; + width: calc(100% - 1px); + padding: 0 $s-12; + margin: 0; + border: none; + opacity: 0; + z-index: $z-index-10; + background-color: transparent; + cursor: pointer; + option { + @include titleTipography; + color: var(--title-foreground-color-hover); + background-color: var(--menu-background-color); + appearance: none; + height: $s-32; + } + } +} + +// SUBMIT-BUTTON +.btn-disabled { + @extend .button-disabled; + height: $s-32; + padding: $s-8 $s-24; + border-radius: $br-8; +} + +// MULTI INPUT + +.custom-multi-input { + display: flex; + flex-direction: column; + position: relative; + min-height: $s-40; + max-height: $s-180; + width: 100%; + overflow-y: hidden; + .inside-input { + @include removeInputStyle; + @include titleTipography; + @include textEllipsis; + width: 100%; + max-width: calc(100% - $s-1); + min-height: $s-32; + padding-top: 0; + height: $s-32; + padding: $s-8; + margin: 0; + border-radius: $br-8; + background-color: var(--input-background-color); + border: $s-1 solid var(--input-background-color); + color: var(--input-foreground-color-active); + &:focus { + outline: none; + border: $s-1 solid var(--input-border-color-focus); + } + } + label { + display: none; + } + .selected-items { + display: flex; + flex-wrap: wrap; + gap: $s-4; + max-height: $s-136; + padding: $s-4 0; + overflow-y: scroll; + .selected-item { + .around { + @include flexRow; + height: $s-24; + width: fit-content; + padding-left: $s-6; + border-radius: $br-6; + background-color: var(--pill-background-color); + border: $s-1 solid var(--pill-background-color); + box-sizing: border-box; + .text { + @include titleTipography; + padding-right: $s-8; + color: var(--pill-foreground-color); + } + + .icon { + @include flexCenter; + @include buttonStyle; + height: $s-32; + width: $s-24; + svg { + @extend .button-icon-small; + stroke: var(--icon-foreground); + } + } + &.invalid { + background-color: var(--status-error-background-color); + .text { + color: var(--alert-foreground-color-error); + } + .icon svg { + stroke: var(--alert-foreground-color-error); + } + } + } + } + } + &.empty { + } + &.invalid { + } + + &.focus { + } + &.valid { + } +} + +// RADIO BUTTONS + +.custom-radio { + display: grid; + grid-template-columns: repeat(3, 1fr); +} + +.radio-label { + @include titleTipography; + @include flexRow; + min-height: $s-32; + border-radius: $br-8; + padding: $s-0 $s-2; + &:focus, + &:focus-within { + outline: none; + border: $s-1 solid var(--input-border-color-active); + } +} + +.radio-dot { + height: $s-8; + width: $s-8; + border-radius: $br-circle; + background-color: var(--color-background-tertiary); +} + +.radio-input { + width: 0; + margin: 0; +} + +.radio-icon { + @extend .checkbox-icon; + border-radius: $br-circle; +} + +.radio-label.with-image { + display: flex; + justify-content: center; + height: $s-120; + width: $s-140; + padding: $s-64 $s-4 0 $s-4; + margin-top: $s-16; + margin-right: 0; + border-radius: $br-8; + border: 1px solid var(--color-background-tertiary); + background-size: 50px; + background-repeat: no-repeat; + background-position: center 0.75rem; + cursor: pointer; + &:global(.checked) { + border: 1px solid var(--color-accent-primary); + } + &:focus, + &:focus-within { + outline: none; + border: $s-1 solid var(--input-border-color-active); + } +} diff --git a/frontend/src/app/main/ui/components/tab_container.scss b/frontend/src/app/main/ui/components/tab_container.scss index f30d9fea0..06ac807eb 100644 --- a/frontend/src/app/main/ui/components/tab_container.scss +++ b/frontend/src/app/main/ui/components/tab_container.scss @@ -33,7 +33,7 @@ background: var(--color-background-secondary); padding: $s-2; cursor: pointer; - font-size: $fs12; + font-size: $fs-12; .tab-container-tab-wrapper { @include flexCenter; flex-direction: row; diff --git a/frontend/src/app/main/ui/confirm.cljs b/frontend/src/app/main/ui/confirm.cljs index be4d4dc56..d17cfaee9 100644 --- a/frontend/src/app/main/ui/confirm.cljs +++ b/frontend/src/app/main/ui/confirm.cljs @@ -5,9 +5,11 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.confirm + (:require-macros [app.main.style :as stl]) (:require [app.main.data.modal :as modal] [app.main.store :as st] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr t]] @@ -29,7 +31,8 @@ cancel-label accept-label accept-style] :as props}] - (let [locale (mf/deref i18n/locale) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + locale (mf/deref i18n/locale) on-accept (or on-accept identity) on-cancel (or on-cancel identity) @@ -63,42 +66,88 @@ (->> (events/listen js/document EventType.KEYDOWN on-keydown) (partial events/unlistenByKey)))) - [:div.modal-overlay - [:div.modal-container.confirm-dialog - [:div.modal-header - [:div.modal-header-title - [:h2 title]] - [:div.modal-close-button - {:on-click cancel-fn} i/close]] - [:div.modal-content - (when (and (string? message) (not= message "")) - [:h3 message]) - (when (and (string? scd-message) (not= scd-message "")) - [:h3 scd-message]) - (when (string? hint) - [:p hint]) - (when (> (count items) 0) - [:* - [:p (tr "ds.component-subtitle")] - [:ul.component-list - (for [item items] - [:li.modal-item-element - [:span.modal-component-icon i/component] - [:span (:name item)]])]])] + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} title] + [:button {:class (stl/css :modal-close-btn) + :on-click cancel-fn} i/close-refactor]] - [:div.modal-footer - [:div.action-buttons - (when-not (= cancel-label :omit) - [:input.cancel-button - {:type "button" - :value cancel-label - :on-click cancel-fn}]) + [:div {:class (stl/css :modal-content)} + (when (and (string? message) (not= message "")) + [:h3 {:class (stl/css :modal-msg)} message]) + (when (and (string? scd-message) (not= scd-message "")) + [:h3 {:class (stl/css :modal-scd-msg)} scd-message]) + (when (string? hint) + [:p {:class (stl/css :modal-hint)} hint]) + (when (> (count items) 0) + [:* + [:p {:class (stl/css :modal-subtitle)} + (tr "ds.component-subtitle")] + [:ul {:class (stl/css :component-list)} + (for [item items] + [:li {:class (stl/css :modal-item-element)} + [:span {:class (stl/css :modal-component-icon)} + i/component-refactor] + [:span {:class (stl/css :modal-component-name)} + (:name item)]])]])] - [:input.accept-button - {:class (dom/classnames - :danger (= accept-style :danger) - :primary (= accept-style :primary)) - :type "button" - :value accept-label - :on-click accept-fn}]]]]])) + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + (when-not (= cancel-label :omit) + [:input + {:class (stl/css :cancel-button) + :type "button" + :value cancel-label + :on-click cancel-fn}]) + + [:input + {:class (stl/css-case :accept-btn true + :danger (= accept-style :danger) + :primary (= accept-style :primary)) + :type "button" + :value accept-label + :on-click accept-fn}]]]]] + + + [:div.modal-overlay + [:div.modal-container.confirm-dialog + [:div.modal-header + [:div.modal-header-title + [:h2 title]] + [:div.modal-close-button + {:on-click cancel-fn} i/close]] + + [:div.modal-content + (when (and (string? message) (not= message "")) + [:h3 message]) + (when (and (string? scd-message) (not= scd-message "")) + [:h3 scd-message]) + (when (string? hint) + [:p hint]) + (when (> (count items) 0) + [:* + [:p (tr "ds.component-subtitle")] + [:ul.component-list + (for [item items] + [:li.modal-item-element + [:span.modal-component-icon i/component] + [:span (:name item)]])]])] + + [:div.modal-footer + [:div.action-buttons + (when-not (= cancel-label :omit) + [:input.cancel-button + {:type "button" + :value cancel-label + :on-click cancel-fn}]) + + [:input.accept-button + {:class (dom/classnames + :danger (= accept-style :danger) + :primary (= accept-style :primary)) + :type "button" + :value accept-label + :on-click accept-fn}]]]]]))) diff --git a/frontend/src/app/main/ui/confirm.scss b/frontend/src/app/main/ui/confirm.scss new file mode 100644 index 000000000..84b9b7ae6 --- /dev/null +++ b/frontend/src/app/main/ui/confirm.scss @@ -0,0 +1,70 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.modal-overlay { + @extend .modal-overlay-base; + &.transparent { + background-color: transparent; + } + .modal-container { + @extend .modal-container-base; + .modal-header { + margin-bottom: $s-24; + .modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); + } + .modal-close-btn { + @extend .modal-close-btn-base; + } + } + .modal-content { + @include titleTipography; + margin-bottom: $s-24; + .modal-msg, + .modal-scd-msg, + .modal-subtitle { + @include titleTipography; + } + .component-list { + .modal-item-element { + @include flexRow; + .modal-component-icon { + @include flexCenter; + height: $s-16; + width: $s-16; + svg { + @extend .button-icon-small; + stroke: var(--color); + } + } + .modal-component-name { + @include titleTipography; + } + } + } + .modal-hint { + @extend .modal-hint-base; + } + } + .modal-footer { + .action-buttons { + @extend .modal-action-btns; + .cancel-button { + @extend .modal-cancel-btn; + } + .accept-btn { + @extend .modal-accept-btn; + &.danger { + @extend .modal-danger-btn; + } + } + } + } + } +} diff --git a/frontend/src/app/main/ui/dashboard/change_owner.cljs b/frontend/src/app/main/ui/dashboard/change_owner.cljs index 607f115e1..bb8eb1ea7 100644 --- a/frontend/src/app/main/ui/dashboard/change_owner.cljs +++ b/frontend/src/app/main/ui/dashboard/change_owner.cljs @@ -5,15 +5,17 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.dashboard.change-owner + (:require-macros [app.main.style :as stl]) (:require - [app.common.spec :as us] + [app.common.spec :as us] [app.main.data.modal :as modal] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.forms :as fm] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [tr]] - [cljs.spec.alpha :as s] + [cljs.spec.alpha :as s] [rumext.v2 :as mf])) (s/def ::member-id ::us/uuid) @@ -24,7 +26,8 @@ {::mf/register modal/components ::mf/register-as :leave-and-reassign} [{:keys [profile team accept]}] - (let [form (fm/use-form :spec ::leave-modal-form :initial {}) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + form (fm/use-form :spec ::leave-modal-form :initial {}) members-map (mf/deref refs/dashboard-team-members) members (vals members-map) @@ -39,34 +42,71 @@ (let [member-id (get-in @form [:clean-data :member-id])] (accept member-id)))] - [:div.modal-overlay - [:div.modal-container.confirm-dialog - [:div.modal-header - [:div.modal-header-title - [:h2 (tr "modals.leave-and-reassign.title")]] - [:div.modal-close-button - {:on-click on-cancel} i/close]] + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} (tr "modals.leave-and-reassign.title")] + [:button {:class (stl/css :modal-close-btn) + :on-click on-cancel} i/close-refactor]] - [:div.modal-content.generic-form - [:p (tr "modals.leave-and-reassign.hint1" (:name team))] + [:div {:class (stl/css :modal-content)} + [:p {:class (stl/css :modal-msg)} + (tr "modals.leave-and-reassign.hint1" (:name team))] - (if (empty? members) - [:p (tr "modals.leave-and-reassign.forbidden")] - [:* - [:& fm/form {:form form} - [:& fm/select {:name :member-id - :options options}]]])] + (if (empty? members) + [:p {:class (stl/css :modal-msg)} + (tr "modals.leave-and-reassign.forbidden")] + [:* + [:& fm/form {:form form} + [:& fm/select {:name :member-id + :options options}]]])] - [:div.modal-footer - [:div.action-buttons - [:input.cancel-button - {:type "button" - :value (tr "labels.cancel") - :on-click on-cancel}] + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + [:input {:class (stl/css :cancel-button) + :type "button" + :value (tr "labels.cancel") + :on-click on-cancel}] - [:input.accept-button - {:type "button" - :class (if (:valid @form) "danger" "btn-disabled") - :disabled (not (:valid @form)) - :value (tr "modals.leave-and-reassign.promote-and-leave") - :on-click on-accept}]]]]])) + [:input.accept-button + {:type "button" + :class (stl/css-case :accept-btn true + :danger (:valid @form) + :global/disabled (not (:valid @form))) + :disabled (not (:valid @form)) + :value (tr "modals.leave-and-reassign.promote-and-leave") + :on-click on-accept}]]]]] + + + [:div.modal-overlay + [:div.modal-container.confirm-dialog + [:div.modal-header + [:div.modal-header-title + [:h2 (tr "modals.leave-and-reassign.title")]] + [:div.modal-close-button + {:on-click on-cancel} i/close]] + + [:div.modal-content.generic-form + [:p (tr "modals.leave-and-reassign.hint1" (:name team))] + + (if (empty? members) + [:p (tr "modals.leave-and-reassign.forbidden")] + [:* + [:& fm/form {:form form} + [:& fm/select {:name :member-id + :options options}]]])] + + [:div.modal-footer + [:div.action-buttons + [:input.cancel-button + {:type "button" + :value (tr "labels.cancel") + :on-click on-cancel}] + + [:input.accept-button + {:type "button" + :class (if (:valid @form) "danger" "btn-disabled") + :disabled (not (:valid @form)) + :value (tr "modals.leave-and-reassign.promote-and-leave") + :on-click on-accept}]]]]]))) diff --git a/frontend/src/app/main/ui/dashboard/change_owner.scss b/frontend/src/app/main/ui/dashboard/change_owner.scss new file mode 100644 index 000000000..799d566c7 --- /dev/null +++ b/frontend/src/app/main/ui/dashboard/change_owner.scss @@ -0,0 +1,46 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.modal-overlay { + @extend .modal-overlay-base; + .modal-container { + @extend .modal-container-base; + border: $s-1 solid var(--modal-border-color); + .modal-header { + margin-bottom: $s-24; + .modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); + } + .modal-close-btn { + @extend .modal-close-btn-base; + } + } + .modal-content { + @include titleTipography; + margin-bottom: $s-24; + .input-wrapper { + @extend .input-with-label; + } + } + .modal-footer { + .action-buttons { + @extend .modal-action-btns; + .cancel-button { + @extend .modal-cancel-btn; + } + .accept-btn { + @extend .modal-accept-btn; + &.danger { + @extend .modal-danger-btn; + } + } + } + } + } +} diff --git a/frontend/src/app/main/ui/dashboard/export.cljs b/frontend/src/app/main/ui/dashboard/export.cljs index 2f7335d5f..b8ee87519 100644 --- a/frontend/src/app/main/ui/dashboard/export.cljs +++ b/frontend/src/app/main/ui/dashboard/export.cljs @@ -5,12 +5,14 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.dashboard.export + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dm] [app.main.data.modal :as modal] [app.main.features :as features] [app.main.store :as st] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.worker :as uw] [app.util.dom :as dom] @@ -23,18 +25,36 @@ (mf/defc export-entry {::mf/wrap-props false} [{:keys [file]}] - [:div.file-entry - {:class (dom/classnames - :loading (:loading? file) - :success (:export-success? file) - :error (:export-error? file))} - [:div.file-name - [:div.file-icon - (cond (:export-success? file) i/tick - (:export-error? file) i/close - (:loading? file) i/loader-pencil)] + (let [new-css-system (mf/use-ctx ctx/new-css-system)] - [:div.file-name-label (:name file)]]]) + (if new-css-system + [:div {:class (stl/css-case :file-entry true + :loading (:loading? file) + :success (:export-success? file) + :error (:export-error? file))} + + [:div {:class (stl/css :file-name)} + [:span {:class (stl/css :file-icon)} + (cond (:export-success? file) i/tick-refactor + (:export-error? file) i/close-refactor + (:loading? file) i/loader-pencil)] + + [:div {:class (stl/css :file-name-label)} + (:name file)]]] + + + [:div.file-entry + {:class (dom/classnames + :loading (:loading? file) + :success (:export-success? file) + :error (:export-error? file))} + [:div.file-name + [:div.file-icon + (cond (:export-success? file) i/tick + (:export-error? file) i/close + (:loading? file) i/loader-pencil)] + + [:div.file-name-label (:name file)]]]))) (defn- mark-file-error [files file-id] @@ -60,7 +80,8 @@ ::mf/register-as :export ::mf/wrap-props false} [{:keys [team-id files has-libraries? binary?]}] - (let [components-v2 (features/use-feature "components/v2") + (let [new-css-system (mf/use-ctx ctx/new-css-system) + components-v2 (features/use-feature :components-v2) state* (mf/use-state #(let [files (mapv (fn [file] (assoc file :loading? true)) files)] {:status :prepare @@ -120,66 +141,136 @@ (when-not has-libraries? (start-export))) - [:div.modal-overlay - [:div.modal-container.export-dialog - [:div.modal-header - [:div.modal-header-title - [:h2 (tr "dashboard.export.title")]] + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} + (tr "dashboard.export.title")] + [:button {:class (stl/css :modal-close-btn) + :on-click on-cancel} i/close-refactor]] - [:div.modal-close-button - {:on-click on-cancel} i/close]] + (cond + (= status :prepare) + [:* + [:div {:class (stl/css :modal-content)} + [:p {:class (stl/css :modal-msg)} (tr "dashboard.export.explain")] + [:p {:class (stl/css :modal-scd-msg)} (tr "dashboard.export.detail")] - (cond - (= status :prepare) - [:* - [:div.modal-content - [:p.explain (tr "dashboard.export.explain")] - [:p.detail (tr "dashboard.export.detail")] + (for [type export-types] + [:div {:class (stl/css :export-option true) + :key (name type)} + [:label {:for (str "export-" type) + :class (stl/css-case :global/checked (= selected type))} + ;; Execution time translation strings: + ;; dashboard.export.options.all.message + ;; dashboard.export.options.all.title + ;; dashboard.export.options.detach.message + ;; dashboard.export.options.detach.title + ;; dashboard.export.options.merge.message + ;; dashboard.export.options.merge.title + [:span {:class (stl/css-case :global/checked (= selected type))} + (when (= selected type) + i/status-tick-refactor)] + [:div {:class (stl/css :option-content)} + [:h3 {:class (stl/css :modal-subtitle)} (tr (dm/str "dashboard.export.options." (d/name type) ".title"))] + [:p {:class (stl/css :modal-msg)} (tr (dm/str "dashboard.export.options." (d/name type) ".message"))]] - (for [type export-types] - [:div.export-option {:class (when (= selected type) "selected") - :key (name type)} - [:label.option-container - ;; Execution time translation strings: - ;; dashboard.export.options.all.message - ;; dashboard.export.options.all.title - ;; dashboard.export.options.detach.message - ;; dashboard.export.options.detach.title - ;; dashboard.export.options.merge.message - ;; dashboard.export.options.merge.title - [:h3 (tr (dm/str "dashboard.export.options." (d/name type) ".title"))] - [:p (tr (dm/str "dashboard.export.options." (d/name type) ".message"))] - [:input {:type "radio" - :checked (= selected type) - :data-type (name type) - :on-change on-change - :name "export-option"}] - [:span {:class "option-radio-check"}]]])] + [:input {:type "radio" + :class (stl/css :option-input) + :id (str "export-" type) + :checked (= selected type) + :name "export-option" + :data-type (name type) + :on-change on-change}]]])] - [:div.modal-footer - [:div.action-buttons - [:input.cancel-button - {:type "button" - :value (tr "labels.cancel") - :on-click on-cancel}] + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + [:input {:class (stl/css :cancel-button) + :type "button" + :value (tr "labels.cancel") + :on-click on-cancel}] - [:input.accept-button - {:class "primary" - :type "button" - :value (tr "labels.continue") - :on-click on-accept}]]]] + [:input {:class (stl/css :accept-btn) + :type "button" + :value (tr "labels.continue") + :on-click on-accept}]]]] - (= status :exporting) - [:* - [:div.modal-content - (for [file (:files state)] - [:& export-entry {:file file :key (dm/str (:id file))}])] + (= status :exporting) + [:* + [:div {:class (stl/css :modal-content)} + (for [file (:files state)] + [:& export-entry {:file file :key (dm/str (:id file))}])] - [:div.modal-footer - [:div.action-buttons - [:input.accept-button - {:class "primary" - :type "button" - :value (tr "labels.close") - :disabled (->> state :files (some :loading?)) - :on-click on-cancel}]]]])]])) + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + [:input {:class (stl/css :accept-btn) + :type "button" + :value (tr "labels.close") + :disabled (->> state :files (some :loading?)) + :on-click on-cancel}]]]])]] + + + [:div.modal-overlay + [:div.modal-container.export-dialog + [:div.modal-header + [:div.modal-header-title + [:h2 (tr "dashboard.export.title")]] + + [:div.modal-close-button + {:on-click on-cancel} i/close]] + + (cond + (= status :prepare) + [:* + [:div.modal-content + [:p.explain (tr "dashboard.export.explain")] + [:p.detail (tr "dashboard.export.detail")] + + (for [type export-types] + [:div.export-option {:class (when (= selected type) "selected") + :key (name type)} + [:label.option-container + ;; Execution time translation strings: + ;; dashboard.export.options.all.message + ;; dashboard.export.options.all.title + ;; dashboard.export.options.detach.message + ;; dashboard.export.options.detach.title + ;; dashboard.export.options.merge.message + ;; dashboard.export.options.merge.title + [:h3 (tr (dm/str "dashboard.export.options." (d/name type) ".title"))] + [:p (tr (dm/str "dashboard.export.options." (d/name type) ".message"))] + [:input {:type "radio" + :checked (= selected type) + :data-type (name type) + :on-change on-change + :name "export-option"}] + [:span {:class "option-radio-check"}]]])] + + [:div.modal-footer + [:div.action-buttons + [:input.cancel-button + {:type "button" + :value (tr "labels.cancel") + :on-click on-cancel}] + + [:input.accept-button + {:class "primary" + :type "button" + :value (tr "labels.continue") + :on-click on-accept}]]]] + + (= status :exporting) + [:* + [:div.modal-content + (for [file (:files state)] + [:& export-entry {:file file :key (dm/str (:id file))}])] + + [:div.modal-footer + [:div.action-buttons + [:input.accept-button + {:class "primary" + :type "button" + :value (tr "labels.close") + :disabled (->> state :files (some :loading?)) + :on-click on-cancel}]]]])]]))) diff --git a/frontend/src/app/main/ui/dashboard/export.scss b/frontend/src/app/main/ui/dashboard/export.scss new file mode 100644 index 000000000..47a4888bd --- /dev/null +++ b/frontend/src/app/main/ui/dashboard/export.scss @@ -0,0 +1,115 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.modal-overlay { + @extend .modal-overlay-base; + + .modal-container { + @extend .modal-container-base; + .modal-header { + margin-bottom: $s-24; + .modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); + } + .modal-close-btn { + @extend .modal-close-btn-base; + } + } + + .modal-content { + @include titleTipography; + margin-bottom: $s-24; + .modal-msg, + .modal-scd-msg, + .modal-subtitle { + @include titleTipography; + } + .export-option { + @extend .input-checkbox; + width: 100%; + align-items: flex-start; + label { + align-items: flex-start; + .modal-subtitle { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); + } + } + span { + margin-top: $s-8; + } + .option-content { + @include flexColumn; + @include titleTipography; + } + } + } + + .modal-footer { + .action-buttons { + @extend .modal-action-btns; + .cancel-button { + @extend .modal-cancel-btn; + } + .accept-btn { + @extend .modal-accept-btn; + &.danger { + @extend .modal-danger-btn; + } + } + } + } + } +} + +.file-entry { + .file-name { + @include flexRow; + .file-icon { + @include flexCenter; + height: $s-16; + width: $s-16; + + svg { + @extend .button-icon-small; + stroke: var(--input-foreground); + } + } + .file-name-label { + @include titleTipography; + } + } + &.loading { + .file-name { + color: var(--pending-color); + .file-icon svg:global(#loader-pencil) { + color: var(--pending-color); + stroke: var(--pending-color); + fill: var(--pending-color); + } + } + } + &.error { + .file-name { + color: var(--error-color); + .file-icon svg { + stroke: var(--error-color); + } + } + } + + &.success { + .file-name { + color: var(--ok-color); + .file-icon svg { + stroke: var(--ok-color); + } + } + } +} diff --git a/frontend/src/app/main/ui/dashboard/import.cljs b/frontend/src/app/main/ui/dashboard/import.cljs index 4edb59bf6..e97686704 100644 --- a/frontend/src/app/main/ui/dashboard/import.cljs +++ b/frontend/src/app/main/ui/dashboard/import.cljs @@ -5,6 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.dashboard.import + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dm] @@ -16,6 +17,7 @@ [app.main.features :as features] [app.main.store :as st] [app.main.ui.components.file-uploader :refer [file-uploader]] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.worker :as uw] [app.util.dom :as dom] @@ -149,7 +151,8 @@ (mf/defc import-entry [{:keys [state file editing? can-be-deleted?]}] - (let [loading? (or (= :analyzing (:status file)) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + loading? (or (= :analyzing (:status file)) (= :importing (:status file))) analyze-error? (= :analyze-error (:status file)) import-finish? (= :import-finish (:status file)) @@ -187,32 +190,89 @@ (fn [] (swap! state update :files remove-file (:file-id file))))] - [:div.file-entry - {:class (dom/classnames - :loading loading? - :success (and import-finish? (not import-warn?) (not import-error?)) - :warning (and import-finish? import-warn? (not import-error?)) - :error (or import-error? analyze-error?) - :editable (and ready? (not editing?)))} + (if new-css-system + [:div {:class (stl/css-case :file-entry true + :loading loading? + :success (and import-finish? (not import-warn?) (not import-error?)) + :warning (and import-finish? import-warn? (not import-error?)) + :error (or import-error? analyze-error?) + :editable (and ready? (not editing?)))} - [:div.file-name - [:div.file-icon - (cond loading? i/loader-pencil - ready? i/logo-icon - import-warn? i/msg-warning - import-error? i/close - import-finish? i/tick - analyze-error? i/close)] + [:div {:class (stl/css :file-name)} + [:div {:class (stl/css-case :file-icon true + :icon-fill ready?)} + (cond loading? i/loader-pencil + ready? i/logo-icon + import-warn? i/msg-warning + import-error? i/close-refactor + import-finish? i/tick-refactor + analyze-error? i/close-refactor)] - (if editing? - [:div.file-name-edit - [:input {:type "text" - :auto-focus true - :default-value (:name file) - :on-key-press handle-edit-key-press - :on-blur handle-edit-blur}]] + (if editing? + [:div {:class (stl/css :file-name-edit)} + [:input {:type "text" + :auto-focus true + :default-value (:name file) + :on-key-press handle-edit-key-press + :on-blur handle-edit-blur}]] - [:div.file-name-label (:name file) (when is-shared? i/library)]) + [:div {:class (stl/css :file-name-label)} + (:name file) + (when is-shared? i/library-refactor)]) + + [:div {:class (stl/css :edit-entry-buttons)} + (when (= "application/zip" (:type file)) + [:button {:on-click handle-edit-entry} i/curve-refactor]) + (when can-be-deleted? + [:button {:on-click handle-remove-entry} i/delete-refactor])]] + (cond + analyze-error? + [:div {:class (stl/css :error-message)} + (tr "dashboard.import.analyze-error")] + + import-error? + [:div {:class (stl/css :error-message)} + (tr "dashboard.import.import-error")] + + (and (not import-finish?) (some? progress)) + [:div {:class (stl/css :progress-message)} (parse-progress-message progress)]) + + [:div {:class (stl/css :linked-libraries)} + (for [library-id (:libraries file)] + (let [library-data (->> @state :files (d/seek #(= library-id (:file-id %)))) + error? (or (:deleted? library-data) (:import-error library-data))] + (when (some? library-data) + [:div {:class (stl/css-case :linked-library-tag true + :error error?)} + i/detach-refactor (:name library-data)])))]] + + + [:div.file-entry + {:class (dom/classnames + :loading loading? + :success (and import-finish? (not import-warn?) (not import-error?)) + :warning (and import-finish? import-warn? (not import-error?)) + :error (or import-error? analyze-error?) + :editable (and ready? (not editing?)))} + + [:div.file-name + [:div.file-icon + (cond loading? i/loader-pencil + ready? i/logo-icon + import-warn? i/msg-warning + import-error? i/close + import-finish? i/tick + analyze-error? i/close)] + + (if editing? + [:div.file-name-edit + [:input {:type "text" + :auto-focus true + :default-value (:name file) + :on-key-press handle-edit-key-press + :on-blur handle-edit-blur}]] + + [:div.file-name-label (:name file) (when is-shared? i/library)]) [:div.edit-entry-buttons (when (= "application/zip" (:type file)) @@ -220,31 +280,32 @@ (when can-be-deleted? [:button {:on-click handle-remove-entry} i/trash])]] - (cond - analyze-error? - [:div.error-message - (tr "dashboard.import.analyze-error")] + (cond + analyze-error? + [:div.error-message + (tr "dashboard.import.analyze-error")] - import-error? - [:div.error-message - (tr "dashboard.import.import-error")] + import-error? + [:div.error-message + (tr "dashboard.import.import-error")] - (and (not import-finish?) (some? progress)) - [:div.progress-message (parse-progress-message progress)]) + (and (not import-finish?) (some? progress)) + [:div.progress-message (parse-progress-message progress)]) - [:div.linked-libraries - (for [library-id (:libraries file)] - (let [library-data (->> @state :files (d/seek #(= library-id (:file-id %)))) - error? (or (:deleted? library-data) (:import-error library-data))] - (when (some? library-data) - [:div.linked-library-tag {:class (when error? "error")} - (if error? i/unchain i/chain) (:name library-data)])))]])) + [:div.linked-libraries + (for [library-id (:libraries file)] + (let [library-data (->> @state :files (d/seek #(= library-id (:file-id %)))) + error? (or (:deleted? library-data) (:import-error library-data))] + (when (some? library-data) + [:div.linked-library-tag {:class (when error? "error")} + (if error? i/unchain i/chain) (:name library-data)])))]]))) (mf/defc import-dialog {::mf/register modal/components ::mf/register-as :import} [{:keys [project-id files template on-finish-import]}] - (let [state (mf/use-state + (let [new-css-system (mf/use-ctx ctx/new-css-system) + state (mf/use-state {:status :analyzing :editing nil :importing-templates 0 @@ -364,61 +425,122 @@ #(doseq [file files] (wapi/revoke-uri (:uri file))))) - [:div.modal-overlay - [:div.modal-container.import-dialog - [:div.modal-header - [:div.modal-header-title - [:h2 (tr "dashboard.import")]] + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} (tr "dashboard.import")] - [:div.modal-close-button - {:on-click handle-cancel} i/close]] + [:button {:class (stl/css :modal-close-btn) + :on-click handle-cancel} i/close-refactor]] - [:div.modal-content - (when (and (= :importing (:status @state)) (not pending-import?)) - (if (> warning-files 0) - [:div.feedback-banner.warning - [:div.icon i/msg-warning] - [:div.message (tr "dashboard.import.import-warning" warning-files success-files)]] + [:div {:class (stl/css :modal-content)} - [:div.feedback-banner - [:div.icon i/checkbox-checked] - [:div.message (tr "dashboard.import.import-message" (i18n/c (if (some? template) 1 success-files)))]])) + (when (and (= :importing (:status @state)) (not pending-import?)) + (if (> warning-files 0) + [:div {:class (stl/css-case :feedback-banner true + :warning true)} + [:div {:class (stl/css :icon)} i/msg-warning-refactor] + [:div {:class (stl/css :message)} (tr "dashboard.import.import-warning" warning-files success-files)]] - (for [file files] - (let [editing? (and (some? (:file-id file)) - (= (:file-id file) (:editing @state)))] + [:div {:class (stl/css :feedback-banner)} + [:div {:class (stl/css :icon)} i/msg-success-refactor] + [:div {:class (stl/css :message)} (tr "dashboard.import.import-message" (i18n/c (if (some? template) 1 success-files)))]])) + + (for [file files] + (let [editing? (and (some? (:file-id file)) + (= (:file-id file) (:editing @state)))] + [:& import-entry {:state state + :key (dm/str (:uri file)) + :file file + :editing? editing? + :can-be-deleted? (> (count files) 1)}])) + + (when (some? template) [:& import-entry {:state state - :key (dm/str (:uri file)) - :file file - :editing? editing? - :can-be-deleted? (> (count files) 1)}])) + :file (assoc template :status (if (= 1 (:importing-templates @state)) :importing :ready)) + :editing? false + :can-be-deleted? false}])] - (when (some? template) - [:& import-entry {:state state - :file (assoc template :status (if (= 1 (:importing-templates @state)) :importing :ready)) - :editing? false - :can-be-deleted? false}])] + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + (when (= :analyzing (:status @state)) + [:input {:class (stl/css :cancel-button) + :type "button" + :value (tr "labels.cancel") + :on-click handle-cancel}]) - [:div.modal-footer - [:div.action-buttons - (when (= :analyzing (:status @state)) - [:input.cancel-button - {:type "button" - :value (tr "labels.cancel") - :on-click handle-cancel}]) + (when (= :analyzing (:status @state)) + [:input {:class (stl/css :accept-btn) + :type "button" + :value (tr "labels.continue") + :disabled (or pending-analysis? (not valid-files?)) + :on-click handle-continue}]) - (when (= :analyzing (:status @state)) - [:input.accept-button - {:class "primary" - :type "button" - :value (tr "labels.continue") - :disabled (or pending-analysis? (not valid-files?)) - :on-click handle-continue}]) + (when (= :importing (:status @state)) + [:input {:class (stl/css :accept-btn) + :type "button" + :value (tr "labels.accept") + :disabled (or pending-import? (not valid-files?)) + :on-click handle-accept}])]]]] - (when (= :importing (:status @state)) - [:input.accept-button - {:class "primary" - :type "button" - :value (tr "labels.accept") - :disabled (or pending-import? (not valid-files?)) - :on-click handle-accept}])]]]])) + + + [:div.modal-overlay + [:div.modal-container.import-dialog + [:div.modal-header + [:div.modal-header-title + [:h2 (tr "dashboard.import")]] + + [:div.modal-close-button + {:on-click handle-cancel} i/close]] + + [:div.modal-content + (when (and (= :importing (:status @state)) (not pending-import?)) + (if (> warning-files 0) + [:div.feedback-banner.warning + [:div.icon i/msg-warning] + [:div.message (tr "dashboard.import.import-warning" warning-files success-files)]] + + [:div.feedback-banner + [:div.icon i/checkbox-checked] + [:div.message (tr "dashboard.import.import-message" (i18n/c (if (some? template) 1 success-files)))]])) + + (for [file files] + (let [editing? (and (some? (:file-id file)) + (= (:file-id file) (:editing @state)))] + [:& import-entry {:state state + :key (dm/str (:uri file)) + :file file + :editing? editing? + :can-be-deleted? (> (count files) 1)}])) + + (when (some? template) + [:& import-entry {:state state + :file (assoc template :status (if (= 1 (:importing-templates @state)) :importing :ready)) + :editing? false + :can-be-deleted? false}])] + + [:div.modal-footer + [:div.action-buttons + (when (= :analyzing (:status @state)) + [:input.cancel-button + {:type "button" + :value (tr "labels.cancel") + :on-click handle-cancel}]) + + (when (= :analyzing (:status @state)) + [:input.accept-button + {:class "primary" + :type "button" + :value (tr "labels.continue") + :disabled (or pending-analysis? (not valid-files?)) + :on-click handle-continue}]) + + (when (= :importing (:status @state)) + [:input.accept-button + {:class "primary" + :type "button" + :value (tr "labels.accept") + :disabled (or pending-import? (not valid-files?)) + :on-click handle-accept}])]]]]))) diff --git a/frontend/src/app/main/ui/dashboard/import.scss b/frontend/src/app/main/ui/dashboard/import.scss new file mode 100644 index 000000000..a8c8baf22 --- /dev/null +++ b/frontend/src/app/main/ui/dashboard/import.scss @@ -0,0 +1,197 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.modal-overlay { + @extend .modal-overlay-base; + + .modal-container { + @extend .modal-container-base; + border: $s-1 solid var(--modal-border-color); + .modal-header { + margin-bottom: $s-24; + .modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); + } + .modal-close-btn { + @extend .modal-close-btn-base; + } + } + + .modal-content { + @include titleTipography; + margin-bottom: $s-24; + .modal-msg, + .modal-scd-msg, + .modal-subtitle { + @include titleTipography; + } + .feedback-banner { + @include flexRow; + height: $s-32; + width: 100%; + margin-bottom: $s-24; + border-radius: $br-8; + background-color: var(--alert-background-color-ok); + color: var(--alert-foreground-color-ok); + + .icon { + @include flexCenter; + height: $s-24; + width: $s-24; + svg { + @extend .button-icon; + stroke: var(--alert-foreground-color-ok); + } + } + .message { + @include titleTipography; + } + &.warning { + background-color: var(--alert-background-color-warning); + color: var(--alert-foreground-color-warning); + .icon svg { + stroke: var(--alert-foreground-color-warning); + } + } + } + } + + .modal-footer { + .action-buttons { + @extend .modal-action-btns; + .cancel-button { + @extend .modal-cancel-btn; + } + .accept-btn { + @extend .modal-accept-btn; + &.danger { + @extend .modal-danger-btn; + } + } + } + } + } +} + +.file-entry { + .file-name { + @include flexRow; + margin-bottom: $s-8; + .file-icon { + @include flexCenter; + height: $s-24; + width: $s-16; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } + &.icon-fill svg { + fill: var(--icon-foreground); + } + } + .file-name-edit { + @extend .input-element; + flex-grow: 1; + } + .file-name-label { + @include titleTipography; + flex-grow: 1; + } + .edit-entry-buttons { + @include flexRow; + button { + @extend .button-tertiary; + width: $s-28; + height: $s-32; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } + } + } + } + .error-message, + .progress-message { + height: $s-32; + } + + .linked-libraries { + .linked-library-tag { + @include flexCenter; + height: $s-24; + width: $s-16; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } + &.error { + svg { + stroke: var(--error-color); + } + } + } + } + + &.loading { + .file-name { + color: var(--pending-color); + .file-icon { + :global(#loader-pencil) { + color: var(--pending-color); + stroke: var(--pending-color); + fill: var(--pending-color); + } + } + } + } + &.warning { + .file-name { + color: var(--warning-color); + .file-icon svg { + stroke: var(--warning-color); + } + .file-icon.icon-fill svg { + fill: var(--warning-color); + } + } + } + &.success { + .file-name { + color: var(--ok-color); + .file-icon svg { + stroke: var(--ok-color); + } + .file-icon.icon-fill svg { + fill: var(--ok-color); + } + } + } + &.error { + .file-name { + color: var(--error-color); + .file-icon svg { + stroke: var(--error-color); + } + .file-icon.icon-fill svg { + fill: var(--error-color); + } + } + } + &.editable { + .file-name { + color: var(--icon-foreground); + .file-icon svg { + stroke: var(--icon-foreground); + } + .file-icon.icon-fill svg { + fill: var(--icon-foreground); + } + } + } +} diff --git a/frontend/src/app/main/ui/dashboard/team.cljs b/frontend/src/app/main/ui/dashboard/team.cljs index 2d8c298aa..9e854ce2b 100644 --- a/frontend/src/app/main/ui/dashboard/team.cljs +++ b/frontend/src/app/main/ui/dashboard/team.cljs @@ -5,6 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.dashboard.team + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dm] @@ -20,6 +21,7 @@ [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.components.file-uploader :refer [file-uploader]] [app.main.ui.components.forms :as fm] + [app.main.ui.context :as ctx] [app.main.ui.dashboard.change-owner] [app.main.ui.dashboard.team-form] [app.main.ui.icons :as i] @@ -107,7 +109,8 @@ ::mf/register-as :invite-members ::mf/wrap-props false} [{:keys [team origin]}] - (let [members-map (mf/deref refs/dashboard-team-members) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + members-map (mf/deref refs/dashboard-team-members) perms (:permissions team) roles (mf/use-memo (mf/deps perms) #(get-available-roles perms)) @@ -153,42 +156,81 @@ (with-meta {::ev/origin origin})) (dd/fetch-team-invitations))))] - [:div.modal.dashboard-invite-modal.form-container - {:class (dom/classnames - :hero (= origin :hero))} - [:& fm/form {:on-submit on-submit :form form} - [:div.title - [:span.text (tr "modals.invite-team-member.title")]] - (when-not (= "" @error-text) - [:div.error - [:span.icon i/msg-error] - [:span.text @error-text]]) + (if new-css-system + [:div {:class (stl/css-case :modal-team-container true + :hero (= origin :hero))} + [:& fm/form {:on-submit on-submit :form form} + [:div {:class (stl/css :modal-title)} + (tr "modals.invite-team-member.title")] - (when (some current-data-emails current-members-emails) - [:div.warning - [:span.icon i/msg-warning] - [:span.text (tr "modals.invite-member.repeated-invitation")]]) + (when-not (= "" @error-text) + [:div {:class (stl/css :error-msg)} + [:span {:class (stl/css :icon)} i/msg-error] + [:span {:class (stl/css :message)} @error-text]]) - [:div.form-row - [:p.label (tr "onboarding.choice.team-up.roles")] - [:& fm/select {:name :role :options roles}]] + (when (some current-data-emails current-members-emails) + [:div {:class (stl/css :warning-msg)} + [:span {:class (stl/css :icon)} i/msg-warning] + [:span {:class (stl/css :message)} (tr "modals.invite-member.repeated-invitation")]]) - [:div.form-row - [:& fm/multi-input {:type "email" - :name :emails - :auto-focus? true - :trim true - :valid-item-fn us/parse-email - :caution-item-fn current-members-emails - :label (tr "modals.invite-member.emails") - :on-submit on-submit}]] + [:div {:class (stl/css :role-select)} + [:p {:class (stl/css :role-title)} (tr "onboarding.choice.team-up.roles")] + [:& fm/select {:name :role :options roles}]] - [:div.action-buttons - [:> fm/submit-button* - {:label (tr "modals.invite-member-confirm.accept") - :disabled (and (boolean (some current-data-emails current-members-emails)) - (empty? (remove current-members-emails current-data-emails)))}]]]])) + [:div {:class (stl/css :invitation-row)} + [:& fm/multi-input {:type "email" + :name :emails + :auto-focus? true + :trim true + :valid-item-fn us/parse-email + :caution-item-fn current-members-emails + :label (tr "modals.invite-member.emails") + :on-submit on-submit}]] + + [:div {:class (stl/css :action-buttons)} + [:> fm/submit-button* + {:label (tr "modals.invite-member-confirm.accept") + :disabled (and (boolean (some current-data-emails current-members-emails)) + (empty? (remove current-members-emails current-data-emails)))}]]]] + + + [:div.modal.dashboard-invite-modal.form-container + {:class (dom/classnames + :hero (= origin :hero))} + [:& fm/form {:on-submit on-submit :form form} + [:div.title + [:span.text (tr "modals.invite-team-member.title")]] + + (when-not (= "" @error-text) + [:div.error + [:span.icon i/msg-error] + [:span.text @error-text]]) + + (when (some current-data-emails current-members-emails) + [:div.warning + [:span.icon i/msg-warning] + [:span.text (tr "modals.invite-member.repeated-invitation")]]) + + [:div.form-row + [:p.label (tr "onboarding.choice.team-up.roles")] + [:& fm/select {:name :role :options roles}]] + + [:div.form-row + [:& fm/multi-input {:type "email" + :name :emails + :auto-focus? true + :trim true + :valid-item-fn us/parse-email + :caution-item-fn current-members-emails + :label (tr "modals.invite-member.emails") + :on-submit on-submit}]] + + [:div.action-buttons + [:> fm/submit-button* + {:label (tr "modals.invite-member-confirm.accept") + :disabled (and (boolean (some current-data-emails current-members-emails)) + (empty? (remove current-members-emails current-data-emails)))}]]]]))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; MEMBERS SECTION @@ -710,7 +752,8 @@ ::mf/register-as :webhook} [{:keys [webhook] :as props}] ;; FIXME: this is a workaround because input fields do not support rendering hooks - (let [initial (mf/use-memo (fn [] (or (some-> webhook (update :uri str)) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + initial (mf/use-memo (fn [] (or (some-> webhook (update :uri str)) {:is-active false :mtype "application/json"}))) form (fm/use-form :spec ::webhook-form :initial initial @@ -771,54 +814,100 @@ (let [data (:clean-data @form)] (if (:id data) (on-update-submit form) - (on-create-submit form)))))] + (on-create-submit form))))) - [:div.modal-overlay - [:div.modal-container.webhooks-modal - [:& fm/form {:form form :on-submit on-submit} + on-modal-close #(st/emit! (modal/hide))] + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:& fm/form {:form form :on-submit on-submit} + [:div {:class (stl/css :modal-header)} + (if webhook + [:h2 {:class (stl/css :modal-title)} (tr "modals.edit-webhook.title")] + [:h2 {:class (stl/css :modal-title)} (tr "modals.create-webhook.title")]) - [:div.modal-header - [:div.modal-header-title - (if webhook - [:h2 (tr "modals.edit-webhook.title")] - [:h2 (tr "modals.create-webhook.title")])] + [:button {:class (stl/css :modal-close-btn) + :on-click on-modal-close} i/close-refactor]] - [:div.modal-close-button - {:on-click #(st/emit! (modal/hide))} i/close]] - - [:div.modal-content.generic-form - [:div.fields-container - [:div.fields-row - [:& fm/input {:type "text" - :auto-focus? true - :form form - :name :uri - :label (tr "modals.create-webhook.url.label") - :placeholder (tr "modals.create-webhook.url.placeholder")}]] - - [:div.fields-row + [:div {:class (stl/css :modal-content)} + [:div {:class (stl/css :fields-row)} + [:& fm/input {:type "text" + :auto-focus? true + :form form + :name :uri + :label (tr "modals.create-webhook.url.label") + :placeholder (tr "modals.create-webhook.url.placeholder")}]] + [:div {:class (stl/css :fields-row)} + [:div {:class (stl/css :select-title)} (tr "dashboard.webhooks.content-type")] [:& fm/select {:options valid-webhook-mtypes - :label (tr "dashboard.webhooks.content-type") :default "application/json" - :name :mtype}]]] - [:div.fields-row - [:div.input-checkbox.check-primary - [:& fm/input {:type "checkbox" - :form form - :name :is-active - :label (tr "dashboard.webhooks.active")}]] - [:div.explain (tr "dashboard.webhooks.active.explain")]]] + :name :mtype}]] + [:div {:class (stl/css :fields-row)} + [:& fm/input {:type "checkbox" + :class (stl/css :custom-input-checkbox) + :form form + :name :is-active + :label (tr "dashboard.webhooks.active")}] + [:div {:class (stl/css :hint)} (tr "dashboard.webhooks.active.explain")]]] - [:div.modal-footer - [:div.action-buttons - [:input.cancel-button - {:type "button" - :value (tr "labels.cancel") - :on-click #(modal/hide!)}] - [:> fm/submit-button* - {:label (if webhook - (tr "modals.edit-webhook.submit-label") - (tr "modals.create-webhook.submit-label"))}]]]]]])) + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + [:input {:class (stl/css :cancel-button) + :type "button" + :value (tr "labels.cancel") + :on-click #(modal/hide!)}] + [:> fm/submit-button* + {:label (if webhook + (tr "modals.edit-webhook.submit-label") + (tr "modals.create-webhook.submit-label"))}]]]]]] + + + [:div.modal-overlay + [:div.modal-container.webhooks-modal + [:& fm/form {:form form :on-submit on-submit} + + [:div.modal-header + [:div.modal-header-title + (if webhook + [:h2 (tr "modals.edit-webhook.title")] + [:h2 (tr "modals.create-webhook.title")])] + + [:div.modal-close-button + {:on-click #(st/emit! (modal/hide))} i/close]] + + [:div.modal-content.generic-form + [:div.fields-container + [:div.fields-row + [:& fm/input {:type "text" + :auto-focus? true + :form form + :name :uri + :label (tr "modals.create-webhook.url.label") + :placeholder (tr "modals.create-webhook.url.placeholder")}]] + + [:div.fields-row + [:& fm/select {:options valid-webhook-mtypes + :label (tr "dashboard.webhooks.content-type") + :default "application/json" + :name :mtype}]]] + [:div.fields-row + [:div.input-checkbox.check-primary + [:& fm/input {:type "checkbox" + :form form + :name :is-active + :label (tr "dashboard.webhooks.active")}]] + [:div.explain (tr "dashboard.webhooks.active.explain")]]] + + [:div.modal-footer + [:div.action-buttons + [:input.cancel-button + {:type "button" + :value (tr "labels.cancel") + :on-click #(modal/hide!)}] + [:> fm/submit-button* + {:label (if webhook + (tr "modals.edit-webhook.submit-label") + (tr "modals.create-webhook.submit-label"))}]]]]]]))) (mf/defc webhooks-hero diff --git a/frontend/src/app/main/ui/dashboard/team.scss b/frontend/src/app/main/ui/dashboard/team.scss new file mode 100644 index 000000000..b7e254209 --- /dev/null +++ b/frontend/src/app/main/ui/dashboard/team.scss @@ -0,0 +1,142 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.modal-team-container { + @extend .modal-container-base; + @include menuShadow; + position: fixed; + top: $s-72; + right: $s-12; + left: unset; + width: $s-400; + padding: $s-32; + background-color: var(--modal-background-color); + border: $s-1 solid var(--modal-border-color); + &.hero { + top: $s-216; + right: $s-32; + } + .modal-title { + @include tabTitleTipography; + height: $s-32; + color: var(--modal-title-foreground-color); + } + .error-msg { + @include flexRow; + height: $s-32; + border-radius: $br-8; + background-color: var(--alert-background-color-error); + .icon { + @include flexCenter; + width: $s-16; + height: $s-24; + svg { + @extend .button-icon; + stroke: var(--alert-foreground-color-error); + } + } + .message { + @include titleTipography; + color: var(--alert-foreground-color-error); + } + } + + .warning-msg { + @include flexRow; + height: $s-32; + border-radius: $br-8; + background-color: var(--alert-background-color-warning); + .icon { + @include flexCenter; + width: $s-16; + height: $s-24; + svg { + @extend .button-icon; + stroke: var(--alert-foreground-color-warning); + } + } + .message { + @include titleTipography; + color: var(--alert-foreground-color-warning); + } + } + + .role-select { + @include flexColumn; + .role-title { + @include titleTipography; + margin: 0; + color: var(--modal-title-foreground-color); + } + } + .invitation-row { + margin-top: $s-8; + margin-bottom: $s-24; + } + + .action-buttons { + display: flex; + justify-content: flex-end; + button { + @extend .modal-accept-btn; + &:disabled { + @extend .button-disabled; + } + } + } +} + +// WEBHOOKS + +.modal-overlay { + @extend .modal-overlay-base; + .modal-container { + @extend .modal-container-base; + border: $s-1 solid var(--modal-border-color); + .modal-header { + margin-bottom: $s-24; + .modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); + } + .modal-close-btn { + @extend .modal-close-btn-base; + } + } + + .modal-content { + @include flexColumn; + gap: $s-24; + @include titleTipography; + margin-bottom: $s-24; + + .fields-row { + @include flexColumn; + .select-title { + @include titleTipography; + color: var(--modal-title-foreground-color); + } + .custom-input-checkbox { + align-items: flex-start; + } + } + } + + .modal-footer { + .action-buttons { + @extend .modal-action-btns; + button { + @extend .modal-accept-btn; + } + .cancel-button { + @extend .modal-cancel-btn; + } + } + } + } +} diff --git a/frontend/src/app/main/ui/dashboard/team_form.cljs b/frontend/src/app/main/ui/dashboard/team_form.cljs index 023cfac48..5333e7225 100644 --- a/frontend/src/app/main/ui/dashboard/team_form.cljs +++ b/frontend/src/app/main/ui/dashboard/team_form.cljs @@ -5,6 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.dashboard.team-form + (:require-macros [app.main.style :as stl]) (:require [app.common.spec :as us] [app.main.data.dashboard :as dd] @@ -12,6 +13,7 @@ [app.main.data.modal :as modal] [app.main.store :as st] [app.main.ui.components.forms :as fm] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [tr]] [app.util.router :as rt] @@ -68,36 +70,73 @@ (mf/defc team-form-modal {::mf/register modal/components ::mf/register-as :team-form} [{:keys [team] :as props}] - (let [initial (mf/use-memo (fn [] (or team {}))) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + initial (mf/use-memo (fn [] (or team {}))) form (fm/use-form :spec ::team-form :validators [(fm/validate-not-empty :name (tr "auth.name.not-all-space")) (fm/validate-length :name fm/max-length-allowed (tr "auth.name.too-long"))] - :initial initial)] - [:div.modal-overlay - [:div.modal-container.team-form-modal - [:& fm/form {:form form :on-submit on-submit} + :initial initial) + on-close #(st/emit! (modal/hide))] - [:div.modal-header - [:div.modal-header-title - (if team - [:h2 (tr "labels.rename-team")] - [:h2 (tr "labels.create-team")])] + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:& fm/form {:form form :on-submit on-submit} - [:div.modal-close-button - {:on-click #(st/emit! (modal/hide))} i/close]] + [:div {:class (stl/css :modal-header)} + (if team + [:h2 {:class (stl/css :modal-title)} + (tr "labels.rename-team")] + [:h2 {:class (stl/css :modal-title)} + (tr "labels.create-team")]) - [:div.modal-content.generic-form - [:& fm/input {:type "text" - :auto-focus? true - :form form - :name :name - :label (tr "labels.create-team.placeholder")}]] + [:button {:class (stl/css :modal-close-btn) + :on-click on-close} i/close-refactor]] - [:div.modal-footer - [:div.action-buttons - [:> fm/submit-button* - {:label (if team - (tr "labels.update-team") - (tr "labels.create-team"))}]]]]]])) + [:div {:class (stl/css :modal-content)} + [:& fm/input {:type "text" + :auto-focus? true + :class (stl/css :group-name-input) + :form form + :name :name + :placeholder "E.g. Design" + :label (tr "labels.create-team.placeholder")}]] + + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + [:> fm/submit-button* + {:label (if team + (tr "labels.update-team") + (tr "labels.create-team")) + :className (stl/css :accept-btn)}]]]]]] + + + + [:div.modal-overlay + [:div.modal-container.team-form-modal + [:& fm/form {:form form :on-submit on-submit} + + [:div.modal-header + [:div.modal-header-title + (if team + [:h2 (tr "labels.rename-team")] + [:h2 (tr "labels.create-team")])] + + [:div.modal-close-button + {:on-click #(st/emit! (modal/hide))} i/close]] + + [:div.modal-content.generic-form + [:& fm/input {:type "text" + :auto-focus? true + :form form + :name :name + :label (tr "labels.create-team.placeholder")}]] + + [:div.modal-footer + [:div.action-buttons + [:> fm/submit-button* + {:label (if team + (tr "labels.update-team") + (tr "labels.create-team"))}]]]]]]))) diff --git a/frontend/src/app/main/ui/dashboard/team_form.scss b/frontend/src/app/main/ui/dashboard/team_form.scss new file mode 100644 index 000000000..12f33182f --- /dev/null +++ b/frontend/src/app/main/ui/dashboard/team_form.scss @@ -0,0 +1,65 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.modal-overlay { + @extend .modal-overlay-base; +} + +.modal-container { + @extend .modal-container-base; + border: $s-1 solid var(--modal-border-color); +} + +.modal-header { + margin-bottom: $s-24; +} + +.modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); +} + +.modal-close-btn { + @extend .modal-close-btn-base; +} + +.modal-content { + margin-bottom: $s-24; +} + +.group-name-input { + @extend .input-element-label; + label { + @include flexColumn; + @include titleTipography; + align-items: flex-start; + width: 100%; + border: none; + background-color: transparent; + height: 100%; + + input { + @include titleTipography; + margin-top: $s-8; + } + } +} + +.action-buttons { + @extend .modal-action-btns; +} + +.cancel-button { + @extend .modal-cancel-btn; +} +.accept-btn { + @extend .modal-accept-btn; + &.danger { + @extend .modal-danger-btn; + } +} diff --git a/frontend/src/app/main/ui/debug/components_preview.scss b/frontend/src/app/main/ui/debug/components_preview.scss index a78e2e2dc..dfe86ab61 100644 --- a/frontend/src/app/main/ui/debug/components_preview.scss +++ b/frontend/src/app/main/ui/debug/components_preview.scss @@ -91,7 +91,7 @@ border-radius: $s-8; h3 { @include titleTipography; - font-size: $fs-25; + font-size: $fs-24; width: 100%; } .component { diff --git a/frontend/src/app/main/ui/delete_shared.cljs b/frontend/src/app/main/ui/delete_shared.cljs index 464a8b2e3..1fb568b96 100644 --- a/frontend/src/app/main/ui/delete_shared.cljs +++ b/frontend/src/app/main/ui/delete_shared.cljs @@ -5,11 +5,13 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.delete-shared + (:require-macros [app.main.style :as stl]) (:require [app.common.data.macros :as dm] [app.main.data.modal :as modal] [app.main.repo :as rp] [app.main.store :as st] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -25,7 +27,8 @@ ::mf/register-as :delete-shared-libraries ::mf/wrap-props false} [{:keys [ids on-accept on-cancel accept-style origin count-libraries]}] - (let [references* (mf/use-state {}) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + references* (mf/use-state {}) references (deref references*) on-accept (or on-accept noop) @@ -93,45 +96,91 @@ (let [key (events/listen js/document "keydown" on-keydown)] (partial events/unlistenByKey key)))) - [:div.modal-overlay - [:div.modal-container.confirm-dialog - [:div.modal-header - [:div.modal-header-title - [:h2 title]] - [:div.modal-close-button - {:on-click cancel-fn} i/close]] + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} title] + [:button {:class (stl/css :modal-close-btn) + :on-click cancel-fn} i/close-refactor]] - [:div.modal-content.delete-shared - (when (and (string? subtitle) (not= subtitle "")) - [:h3 subtitle]) - (when (not= 0 count-libraries) - (if (pos? (count references)) - [:* - [:div - (when (and (string? scd-msg) (not= scd-msg "")) - [:h3 scd-msg]) - [:ul.file-list - (for [[file-id file-name] references] - [:li.modal-item-element - {:key (dm/str file-id)} - [:span "- " file-name]])]] - (when (and (string? hint) (not= hint "")) - [:h3 hint])] - [:* - [:h3 no-files-msg]]))] + [:div {:class (stl/css :modal-content)} + (when (and (string? subtitle) (not= subtitle "")) + [:h3 {:class (stl/css :modal-subtitle)} subtitle]) + (when (not= 0 count-libraries) + (if (pos? (count references)) + [:* + [:div + (when (and (string? scd-msg) (not= scd-msg "")) + [:h3 {:class (stl/css :modal-scd-msg)} scd-msg]) + [:ul {:class (stl/css :element-list)} + (for [[file-id file-name] references] + [:li {:class (stl/css :list-item) + :key (dm/str file-id)} + [:span "- " file-name]])]] + (when (and (string? hint) (not= hint "")) + [:h3 {:class (stl/css :modal-hint)}hint])] + [:* + [:h3 {:class (stl/css :modal-msg)} no-files-msg]]))] - [:div.modal-footer - [:div.action-buttons - (when-not (= cancel-label :omit) - [:input.cancel-button - {:type "button" - :value cancel-label - :on-click cancel-fn}]) + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + (when-not (= cancel-label :omit) + [:input {:class (stl/css :cancel-button) + :type "button" + :value cancel-label + :on-click cancel-fn}]) - [:input.accept-button - {:class (dom/classnames - :danger (= accept-style :danger) - :primary (= accept-style :primary)) - :type "button" - :value accept-label - :on-click accept-fn}]]]]])) + [:input {:class (stl/css-case :accept-btn true + :danger (= accept-style :danger) + :primary (= accept-style :primary)) + :type "button" + :value accept-label + :on-click accept-fn}]]]]] + + + [:div.modal-overlay + [:div.modal-container.confirm-dialog + [:div.modal-header + [:div.modal-header-title + [:h2 title]] + [:div.modal-close-button + {:on-click cancel-fn} i/close]] + + [:div.modal-content.delete-shared + (when (and (string? subtitle) (not= subtitle "")) + [:h3 subtitle]) + (when (not= 0 count-libraries) + (if (pos? (count references)) + [:* + [:div + (when (and (string? scd-msg) (not= scd-msg "")) + [:h3 scd-msg]) + [:ul.file-list + (for [[file-id file-name] references] + [:li.modal-item-element + {:key (dm/str file-id)} + [:span "- " file-name]])]] + (when (and (string? hint) (not= hint "")) + [:h3 hint])] + [:* + [:h3 no-files-msg]]))] + + [:div.modal-footer + [:div.action-buttons + (when-not (= cancel-label :omit) + [:input.cancel-button + {:type "button" + :value cancel-label + :on-click cancel-fn}]) + + [:input.accept-button + {:class (dom/classnames + :danger (= accept-style :danger) + :primary (= accept-style :primary)) + :type "button" + :value accept-label + :on-click accept-fn}]]]]] + ) + + )) diff --git a/frontend/src/app/main/ui/delete_shared.scss b/frontend/src/app/main/ui/delete_shared.scss new file mode 100644 index 000000000..17c2531a7 --- /dev/null +++ b/frontend/src/app/main/ui/delete_shared.scss @@ -0,0 +1,59 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.modal-overlay { + @extend .modal-overlay-base; + &.transparent { + background-color: transparent; + } + .modal-container { + @extend .modal-container-base; + .modal-header { + margin-bottom: $s-24; + .modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); + } + .modal-close-btn { + @extend .modal-close-btn-base; + } + } + .modal-content { + @include titleTipography; + margin-bottom: $s-24; + .modal-msg, + .modal-scd-msg, + .modal-subtitle { + @include titleTipography; + } + .modal-hint { + @extend .modal-hint-base; + } + .element-list { + @include titleTipography; + .list-item { + @include titleTipography; + } + } + } + .modal-footer { + .action-buttons { + @extend .modal-action-btns; + .cancel-button { + @extend .modal-cancel-btn; + } + .accept-btn { + @extend .modal-accept-btn; + &.danger { + @extend .modal-danger-btn; + } + } + } + } + } +} diff --git a/frontend/src/app/main/ui/export.cljs b/frontend/src/app/main/ui/export.cljs index 7f164a983..710e5efc5 100644 --- a/frontend/src/app/main/ui/export.cljs +++ b/frontend/src/app/main/ui/export.cljs @@ -26,7 +26,8 @@ (mf/defc export-multiple-dialog [{:keys [exports title cmd no-selection]}] - (let [lstate (mf/deref refs/export) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + lstate (mf/deref refs/export) in-progress? (:in-progress lstate) exports (mf/use-state exports) @@ -61,91 +62,198 @@ (fn [_] (swap! exports (fn [exports] (mapv #(assoc % :enabled (not all-checked?)) exports))))] - [:div.modal-overlay - [:div.modal-container.export-multiple-dialog - {:class (when (empty? all-exports) "empty")} - [:div.modal-header - [:div.modal-header-title - [:h2 title]] + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div.modal-container.export-multiple-dialog + {:class (stl/css-case :modal-container true + :empty (empty? all-exports))} - [:div.modal-close-button - {:on-click cancel-fn} i/close]] + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} title] + [:button {:class (stl/css :modal-close-btn) + :on-click cancel-fn} + i/close-refactor]] - [:* - [:div.modal-content - (if (> (count all-exports) 0) - [:* - [:div.header - [:div.field.check {:on-click change-all} - (cond - all-checked? [:span.checked i/checkbox-checked] - all-unchecked? [:span.unchecked i/checkbox-unchecked] - :else [:span.intermediate i/checkbox-intermediate])] - [:div.field.title (tr "dashboard.export-multiple.selected" - (c (count enabled-exports)) - (c (count all-exports)))]] + [:* + [:div {:class (stl/css :modal-content)} + (if (> (count all-exports) 0) + [:* + [:div {:class (stl/css :selection-header)} + [:button {:class (stl/css :selection-btn) + :on-click change-all} + [:span {:class (stl/css :checkbox-wrapper)} + (cond + all-checked? [:span {:class (stl/css-case :checkbox-icon2 true + :global/checked true)} i/tick-refactor] + all-unchecked? [:span {:class (stl/css-case :checkbox-icon2 true + :global/uncheked true)}] + :else [:span {:class (stl/css-case :checkbox-icon2 true + :global/intermediate true)} i/remove-refactor])]] + [:div {:class (stl/css :selection-title)} (tr "dashboard.export-multiple.selected" + (c (count enabled-exports)) + (c (count all-exports)))]] + [:div {:class (stl/css :selection-wrapper)} + [:div {:class (stl/css-case :selection-list true + :selection-shadow (> (count all-exports) 8))} + (for [[index {:keys [shape suffix] :as export}] (d/enumerate @exports)] + (let [{:keys [x y width height]} (:selrect shape)] + [:div {:class (stl/css :selection-row)} + [:button {:class (stl/css :selection-btn) + :on-click #(on-toggle-enabled index)} + [:span {:class (stl/css :checkbox-wrapper)} + (if (:enabled export) + [:span {:class (stl/css-case :checkbox-icon2 true + :global/checked true)} i/tick-refactor] + [:span {:class (stl/css-case :checkbox-icon2 true + :global/uncheked true)}])] - [:div.body - (for [[index {:keys [shape suffix] :as export}] (d/enumerate @exports)] - (let [{:keys [x y width height]} (:selrect shape)] - [:div.row - [:div.field.check {:on-click #(on-toggle-enabled index)} - (if (:enabled export) - [:span.checked i/checkbox-checked] - [:span.unchecked i/checkbox-unchecked])] + [:div {:class (stl/css :image-wrapper)} + (if (some? (:thumbnail shape)) + [:img {:src (:thumbnail shape)}] + [:svg {:view-box (dm/str x " " y " " width " " height) + :width 24 + :height 20 + :version "1.1" + :xmlns "http://www.w3.org/2000/svg" + :xmlnsXlink "http://www.w3.org/1999/xlink" + ;; Fix Chromium bug about color of html texts + ;; https://bugs.chromium.org/p/chromium/issues/detail?id=1244560#c5 + :style {:-webkit-print-color-adjust :exact} + :fill "none"} - [:div.field.image - (if (some? (:thumbnail shape)) - [:img {:src (:thumbnail shape)}] - [:svg {:view-box (dm/str x " " y " " width " " height) - :width 24 - :height 20 - :version "1.1" - :xmlns "http://www.w3.org/2000/svg" - :xmlnsXlink "http://www.w3.org/1999/xlink" - ;; Fix Chromium bug about color of html texts - ;; https://bugs.chromium.org/p/chromium/issues/detail?id=1244560#c5 - :style {:-webkit-print-color-adjust :exact} - :fill "none"} + [:& shape-wrapper {:shape shape}]])] - [:& shape-wrapper {:shape shape}]])] + [:div {:class (stl/css :selection-name)} (cond-> (:name shape) suffix (str suffix))] + (when (:scale export) + [:div {:class (stl/css :selection-scale)} + (dm/str (ust/format-precision (* width (:scale export)) 2) "x" + (ust/format-precision (* height (:scale export)) 2) "px ")]) - [:div.field.name (cond-> (:name shape) suffix (str suffix))] - (when (:scale export) - [:div.field.scale (dm/str (ust/format-precision (* width (:scale export)) 2) "x" - (ust/format-precision (* height (:scale export)) 2) "px ")]) + (when (:type export) + [:div {:class (stl/css :selection-extension)} + (-> export :type d/name str/upper)])]]))]]] - (when (:type export) - [:div.field.extension (-> export :type d/name str/upper)])]))] + [:& no-selection])] - [:div.modal-footer - [:div.action-buttons - [:input.cancel-button - {:type "button" + (when (> (count all-exports) 0) + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + [:input + {:class (stl/css :cancel-button) + :type "button" :value (tr "labels.cancel") :on-click cancel-fn}] - [:input.accept-button.primary - {:class (dom/classnames - :btn-disabled (or in-progress? all-unchecked?)) - :disabled (or in-progress? all-unchecked?) - :type "button" - :value (if in-progress? - (tr "workspace.options.exporting-object") - (tr "labels.export")) - :on-click (when-not in-progress? accept-fn)}]]]] + [:input {:class (stl/css-case :accept-btn true + :btn-disabled (or in-progress? all-unchecked?)) + :disabled (or in-progress? all-unchecked?) + :type "button" + :value (if in-progress? + (tr "workspace.options.exporting-object") + (tr "labels.export")) + :on-click (when-not in-progress? accept-fn)}]]])]]] - [:& no-selection])]]]])) + + + [:div.modal-overlay + [:div.modal-container.export-multiple-dialog + {:class (when (empty? all-exports) "empty")} + + [:div.modal-header + [:div.modal-header-title + [:h2 title]] + + [:div.modal-close-button + {:on-click cancel-fn} i/close]] + + [:* + [:div.modal-content + (if (> (count all-exports) 0) + [:* + + + [:div.header + [:div.field.check {:on-click change-all} + (cond + all-checked? [:span.checked i/checkbox-checked] + all-unchecked? [:span.unchecked i/checkbox-unchecked] + :else [:span.intermediate i/checkbox-intermediate])] + [:div.field.title (tr "dashboard.export-multiple.selected" + (c (count enabled-exports)) + (c (count all-exports)))]] + + [:div.body + (for [[index {:keys [shape suffix] :as export}] (d/enumerate @exports)] + (let [{:keys [x y width height]} (:selrect shape)] + [:div.row + [:div.field.check {:on-click #(on-toggle-enabled index)} + (if (:enabled export) + [:span.checked i/checkbox-checked] + [:span.unchecked i/checkbox-unchecked])] + + [:div.field.image + (if (some? (:thumbnail shape)) + [:img {:src (:thumbnail shape)}] + [:svg {:view-box (dm/str x " " y " " width " " height) + :width 24 + :height 20 + :version "1.1" + :xmlns "http://www.w3.org/2000/svg" + :xmlnsXlink "http://www.w3.org/1999/xlink" + ;; Fix Chromium bug about color of html texts + ;; https://bugs.chromium.org/p/chromium/issues/detail?id=1244560#c5 + :style {:-webkit-print-color-adjust :exact} + :fill "none"} + + [:& shape-wrapper {:shape shape}]])] + + [:div.field.name (cond-> (:name shape) suffix (str suffix))] + (when (:scale export) + [:div.field.scale (dm/str (ust/format-precision (* width (:scale export)) 2) "x" + (ust/format-precision (* height (:scale export)) 2) "px ")]) + + (when (:type export) + [:div.field.extension (-> export :type d/name str/upper)])]))] + + [:div.modal-footer + [:div.action-buttons + [:input.cancel-button + {:type "button" + :value (tr "labels.cancel") + :on-click cancel-fn}] + + [:input.accept-button.primary + {:class (dom/classnames + :btn-disabled (or in-progress? all-unchecked?)) + :disabled (or in-progress? all-unchecked?) + :type "button" + :value (if in-progress? + (tr "workspace.options.exporting-object") + (tr "labels.export")) + :on-click (when-not in-progress? accept-fn)}]]]] + + [:& no-selection])]]]]))) (mf/defc shapes-no-selection [] - [:div.no-selection - [:img {:src "images/export-no-shapes.png" :border "0"}] - [:p (tr "dashboard.export-shapes.no-elements")] - [:p (tr "dashboard.export-shapes.how-to")] - [:p [:a {:target "_blank" + (let [new-css-system (mf/use-ctx ctx/new-css-system)] + (if new-css-system + [:div {:class (stl/css :no-selection)} + [:p {:class (stl/css :modal-msg)}(tr "dashboard.export-shapes.no-elements")] + [:p {:class (stl/css :modal-scd-msg)}(tr "dashboard.export-shapes.how-to")] + [:a {:target "_blank" + :class (stl/css :modal-link) :href "https://help.penpot.app/user-guide/exporting/ "} - (tr "dashboard.export-shapes.how-to-link")]]]) + (tr "dashboard.export-shapes.how-to-link")]] + + + [:div.no-selection + [:img {:src "images/export-no-shapes.png" :border "0"}] + [:p (tr "dashboard.export-shapes.no-elements")] + [:p (tr "dashboard.export-shapes.how-to")] + [:p [:a {:target "_blank" + :href "https://help.penpot.app/user-guide/exporting/ "} + (tr "dashboard.export-shapes.how-to-link")]]]))) (mf/defc export-shapes-dialog {::mf/register modal/components diff --git a/frontend/src/app/main/ui/export.scss b/frontend/src/app/main/ui/export.scss index e4aa1d2c8..785f42bde 100644 --- a/frontend/src/app/main/ui/export.scss +++ b/frontend/src/app/main/ui/export.scss @@ -38,14 +38,14 @@ @include titleTipography; padding: 0; margin: 0; - color: var(--modal-foreground-color); + color: var(--modal-title-foreground-color); padding-left: $s-4; } .progress { @include titleTipography; padding-left: $s-8; margin: 0; - color: var(--modal-foreground-color-secondary); + color: var(--modal-text-foreground-color); } .retry-btn { @extend .button-tertiary; @@ -66,3 +66,159 @@ } } } + +.modal-overlay { + @extend .modal-overlay-base; + &.transparent { + background-color: transparent; + } + .modal-container { + @extend .modal-container-base; + max-height: calc(10 * $s-80); + .modal-header { + margin-bottom: $s-24; + .modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); + } + .modal-close-btn { + @extend .modal-close-btn-base; + } + } + .modal-content, + .no-selection { + @include titleTipography; + margin-bottom: $s-24; + .modal-msg, + .modal-scd-msg, + .modal-hint, + .modal-subtitle { + @include titleTipography; + color: var(--modal-text-foreground-color); + } + .modal-link { + @include titleTipography; + text-decoration: none; + cursor: pointer; + color: var(--modal-link-foreground-color); + } + .selection-header { + @include flexRow; + height: $s-32; + margin-bottom: $s-4; + .selection-btn { + @include buttonStyle; + @extend .input-checkbox; + @include flexCenter; + height: $s-24; + width: $s-24; + padding: 0; + margin-left: $s-16; + span { + @extend .checkbox-icon; + } + } + .selection-title { + @include titleTipography; + } + } + .selection-wrapper { + position: relative; + width: 100%; + height: fit-content; + } + .selection-shadow { + width: 100%; + height: 100%; + &:after { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 50px; + background: linear-gradient(to top, rgba(24, 24, 26, 1) 0%, rgba(24, 24, 26, 0) 100%); + content: ""; + pointer-events: none; + } + } + .selection-list { + @include flexColumn; + max-height: $s-400; + overflow-y: auto; + padding-bottom: $s-12; + .selection-row { + @include flexRow; + background-color: var(--entry-background-color); + min-height: $s-40; + border-radius: $br-8; + .selection-btn { + @include buttonStyle; + @include flexRow; + width: 100%; + height: 10%; + gap: $s-8; + padding: 0 $s-16; + .checkbox-wrapper { + @extend .input-checkbox; + @include flexCenter; + height: $s-24; + width: $s-24; + padding: 0; + .checkbox-icon2 { + @extend .checkbox-icon; + } + } + .selection-name { + @include titleTipography; + @include textEllipsis; + flex-grow: 1; + color: var(--modal-text-foreground-color); + text-align: start; + } + .selection-scale { + @include titleTipography; + @include textEllipsis; + min-width: $s-108; + padding: $s-12; + color: var(--modal-text-foreground-color); + } + .selection-extension { + @include titleTipography; + @include textEllipsis; + min-width: $s-72; + padding: $s-12; + color: var(--modal-text-foreground-color); + } + } + .image-wrapper { + @include flexCenter; + height: 100%; + min-height: $s-32; + min-width: $s-32; + background-color: var(--white); + border-radius: $br-6; + margin: auto 0; + img, + svg { + object-fit: contain; + } + } + } + } + } + .modal-footer { + .action-buttons { + @extend .modal-action-btns; + .cancel-button { + @extend .modal-cancel-btn; + } + .accept-btn { + @extend .modal-accept-btn; + &.danger { + @extend .modal-danger-btn; + } + } + } + } + } +} diff --git a/frontend/src/app/main/ui/icons.cljs b/frontend/src/app/main/ui/icons.cljs index b24abe32d..20071dfd5 100644 --- a/frontend/src/app/main/ui/icons.cljs +++ b/frontend/src/app/main/ui/icons.cljs @@ -391,6 +391,10 @@ (def masked-refactor (icon-xref :masked-refactor)) (def menu-refactor (icon-xref :menu-refactor)) (def merge-nodes-refactor (icon-xref :merge-nodes-refactor)) +(def msg-error-refactor (icon-xref :msg-error-refactor)) +(def msg-neutral-refactor (icon-xref :msg-neutral-refactor)) +(def msg-success-refactor (icon-xref :msg-success-refactor)) +(def msg-warning-refactor (icon-xref :msg-warning-refactor)) (def move-refactor (icon-xref :move-refactor)) (def open-link-refactor (icon-xref :open-link-refactor)) (def open-refactor (icon-xref :open-refactor)) @@ -448,9 +452,11 @@ (def text-underlined-refactor (icon-xref :text-underlined-refactor)) (def text-uppercase-refactor (icon-xref :text-uppercase-refactor)) (def tick-refactor (icon-xref :tick-refactor)) +(def tree-refactor (icon-xref :tree-refactor)) (def to-corner-refactor (icon-xref :to-corner-refactor)) (def to-curve-refactor (icon-xref :to-curve-refactor)) (def unlock-refactor (icon-xref :unlock-refactor)) +(def user-refactor (icon-xref :user-refactor)) (def vertical-align-items-center-refactor (icon-xref :vertical-align-items-center-refactor)) (def vertical-align-items-end-refactor (icon-xref :vertical-align-items-end-refactor)) (def vertical-align-items-start-refactor (icon-xref :vertical-align-items-start-refactor)) diff --git a/frontend/src/app/main/ui/messages.cljs b/frontend/src/app/main/ui/messages.cljs index 36a9e94be..f04919fc6 100644 --- a/frontend/src/app/main/ui/messages.cljs +++ b/frontend/src/app/main/ui/messages.cljs @@ -5,6 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.messages + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dm] @@ -13,49 +14,96 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.link-button :as lb] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [rumext.v2 :as mf])) (mf/defc banner [{:keys [type position status controls content links actions on-close data-test role] :as props}] - [:div.banner {:class (dom/classnames - :warning (= type :warning) - :error (= type :error) - :success (= type :success) - :info (= type :info) - :fixed (= position :fixed) - :floating (= position :floating) - :inline (= position :inline) - :hide (= status :hide))} - [:div.wrapper - [:div.icon (case type - :warning i/msg-warning - :error i/msg-error - :success i/msg-success - :info i/msg-info - i/msg-error)] - [:div.content {:class (dom/classnames - :inline-actions (= controls :inline-actions) - :bottom-actions (= controls :bottom-actions)) - :data-test data-test - :role role} - [:span - content - (for [[index link] (d/enumerate links)] - [:* {:key (dm/str "link-" index)} - " " [:& lb/link-button {:class "link" - :on-click (:callback link) - :value (:label link)}]])] + (let [new-css-system (mf/use-ctx ctx/new-css-system)] + (if new-css-system + [:div {:class (stl/css-case :banner true + :warning (= type :warning) + :error (= type :error) + :success (= type :success) + :info (= type :info) + :fixed (= position :fixed) + :floating (= position :floating) + :inline (= position :inline) + :hide (= status :hide))} + [:div {:class (stl/css :wrapper)} + [:div {:class (stl/css :icon)} + (case type + :warning i/msg-warning-refactor + :error i/msg-error-refactor + :success i/msg-success-refactor + :info i/msg-neutral-refactor + i/msg-error-refactor)] - (when (or (= controls :bottom-actions) (= controls :inline-actions)) - [:div.actions - (for [action actions] - [:div.btn-secondary.btn-small {:key (uuid/next) - :on-click (:callback action)} - (:label action)])])] - (when (= controls :close) - [:div.btn-close {:on-click on-close} i/close])]]) + [:div {:class (stl/css-case :content true + :inline-actions (= controls :inline-actions) + :bottom-actions (= controls :bottom-actions)) + :data-test data-test + :role role} + [:span {:class (stl/css :text)} + content + (for [[index link] (d/enumerate links)] + [:* {:key (dm/str "link-" index)} + " " [:& lb/link-button {:class "link" + :on-click (:callback link) + :value (:label link)}]])] + + (when (or (= controls :bottom-actions) (= controls :inline-actions)) + [:div {:class (stl/css :actions)} + (for [action actions] + [:button {:key (uuid/next) + :class (stl/css :action-bnt) + :on-click (:callback action)} + (:label action)])])] + (when (= controls :close) + [:button {:class (stl/css :btn-close) + :on-click on-close} i/close-refactor])]] + + + + [:div.banner {:class (dom/classnames + :warning (= type :warning) + :error (= type :error) + :success (= type :success) + :info (= type :info) + :fixed (= position :fixed) + :floating (= position :floating) + :inline (= position :inline) + :hide (= status :hide))} + [:div.wrapper + [:div.icon (case type + :warning i/msg-warning + :error i/msg-error + :success i/msg-success + :info i/msg-info + i/msg-error)] + [:div.content {:class (dom/classnames + :inline-actions (= controls :inline-actions) + :bottom-actions (= controls :bottom-actions)) + :data-test data-test + :role role} + [:span + content + (for [[index link] (d/enumerate links)] + [:* {:key (dm/str "link-" index)} + " " [:& lb/link-button {:class "link" + :on-click (:callback link) + :value (:label link)}]])] + + (when (or (= controls :bottom-actions) (= controls :inline-actions)) + [:div.actions + (for [action actions] + [:div.btn-secondary.btn-small {:key (uuid/next) + :on-click (:callback action)} + (:label action)])])] + (when (= controls :close) + [:div.btn-close {:on-click on-close} i/close])]]))) (mf/defc notifications [] diff --git a/frontend/src/app/main/ui/messages.scss b/frontend/src/app/main/ui/messages.scss new file mode 100644 index 000000000..1479f2f14 --- /dev/null +++ b/frontend/src/app/main/ui/messages.scss @@ -0,0 +1,130 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.banner { + --bg-color: var(--alert-background-color-error); + --fg-color: var(--alert-foreground-color-error); + position: relative; + display: flex; + align-items: center; + border-radius: $br-8; + background-color: var(--bg-color); + color: var(--fg-color); +} +.warning { + --bg-color: var(--alert-background-color-warning); + --fg-color: var(--alert-foreground-color-warning); +} + +.success { + --bg-color: var(--alert-background-color-ok); + --fg-color: var(--alert-foreground-color-ok); +} + +.info { + --bg-color: var(--alert-background-color-neutral); + --fg-color: var(--alert-foreground-color-neutral-active); +} +.banner.info .icon { + --fg-color: var(--alert-foreground-color-neutral); +} +.banner.info:hover .icon { + --fg-color: var(--alert-foreground-color-neutral); +} + +.wrapper { + display: grid; + grid-template-columns: $s-16 1fr $s-40; + padding: $s-8 $s-8 $s-8 $s-16; + gap: $s-8; + height: 100%; + width: 100%; +} + +.icon { + @include flexCenter; + height: 100%; + width: $s-16; + svg { + @extend .button-icon; + stroke: var(--fg-color); + } +} + +.fixed { + @include alertShadow; + position: fixed; + top: $s-16; + right: $s-16; + display: flex; + align-items: center; + height: $s-48; + min-width: $s-500; + max-width: calc(10 * $s-100); + padding-left: $s-16; + z-index: $z-index-alert; +} + +.floating { + @include alertShadow; + position: absolute; + min-height: $s-32; + top: $s-72; + left: 0; + right: 0; + width: $s-640; + margin-left: auto; + margin-right: auto; + z-index: $z-index-modal; +} + +.inline { + min-height: $s-40; + width: 100%; +} + +.hide { + display: none; +} + +.content { + @include flexRow; +} + +.text { + @include titleTipography; +} + +.inline-actions { +} + +.bottom-actions { +} + +.actions { +} + +.action-btn { + @extend .button-tertiary; + height: $s-32; + width: $s-32; + color: black; + svg { + @extend .button-icon-small; + } +} + +.btn-close { + @extend .button-tertiary; + height: $s-32; + width: $s-32; + svg { + @extend .button-icon-small; + stroke: black; + } +} diff --git a/frontend/src/app/main/ui/modal.cljs b/frontend/src/app/main/ui/modal.cljs index 1562543aa..61b088b78 100644 --- a/frontend/src/app/main/ui/modal.cljs +++ b/frontend/src/app/main/ui/modal.cljs @@ -5,7 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.modal - (:require-macros [app.main.style :refer [css]]) + (:require-macros [app.main.style :as stl]) (:require [app.main.data.modal :as dm] [app.main.features :as features] @@ -78,8 +78,9 @@ (when-let [component (get components (:type data))] [:div {:ref wrapper-ref - :class (dom/classnames (css :modal-wrapper) new-css-system - :modal-wrapper (not new-css-system))} + :class (stl/css-case + :modal-wrapper new-css-system + :old-css/modal-wrapper (not new-css-system))} (mf/element component (:props data))]))) (def modal-ref diff --git a/frontend/src/app/main/ui/onboarding.cljs b/frontend/src/app/main/ui/onboarding.cljs index f84f91f3f..be1e60510 100644 --- a/frontend/src/app/main/ui/onboarding.cljs +++ b/frontend/src/app/main/ui/onboarding.cljs @@ -5,12 +5,15 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.onboarding + (:require-macros [app.main.style :as stl]) (:require + [app.common.data.macros :as dm] [app.config :as cf] [app.main.data.events :as ev] [app.main.data.modal :as modal] [app.main.data.users :as du] [app.main.store :as st] + [app.main.ui.context :as ctx] [app.main.ui.onboarding.newsletter] [app.main.ui.onboarding.questions] [app.main.ui.onboarding.team-choice] @@ -30,80 +33,178 @@ (mf/defc onboarding-welcome [{:keys [next] :as props}] - (let [go-next + (let [new-css-system (mf/use-ctx ctx/new-css-system) + go-next (fn [] (send-event "onboarding-step1-continue") (next))] - [:div.modal-container.onboarding.onboarding-v2 - [:div.modal-left.welcome - [:img {:src "images/onboarding-welcome.png" :border "0" :alt (tr "onboarding.welcome.alt")}]] - [:div.modal-right - [:div.release-container [:span.release "Version " (:main cf/version)]] - [:div.right-content - [:div.modal-title - [:h2 {:data-test "onboarding-welcome"} (tr "onboarding-v2.welcome.title")]] + (if new-css-system + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-left)} + [:img {:src "images/onboarding-welcome.png" + :border "0" + :alt (tr "onboarding.welcome.alt")}]] + [:div {:class (stl/css :modal-right)} + [:div {:class (stl/css :release)} + "Version " (:main cf/version)] + [:div {:class (stl/css :modal-content)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title) + :data-test "onboarding-welcome"} + (tr "onboarding-v2.welcome.title")]] - [:div.modal-content - [:p (tr "onboarding-v2.welcome.desc1")] - [:div.welcome-card - [:img {:src "images/community.svg" :border "0"}] - [:div - [:div.title [:a {:href "https://community.penpot.app/" :target "_blank" :on-click #(send-event "onboarding-community-link")} (tr "onboarding-v2.welcome.desc2.title")]] - [:div.description (tr "onboarding-v2.welcome.desc2")]]] + [:div {:class (stl/css :modal-info)} + [:p {:class (stl/css :modal-text)} + (tr "onboarding-v2.welcome.desc1")] + [:div {:class (stl/css :property-block)} + [:img {:src "images/community.svg" + :border "0"}] + [:div {:class (stl/css :text-wrapper)} + [:div {:class (stl/css :property-title)} + [:a {:href "https://community.penpot.app/" + :target "_blank" + :on-click #(send-event "onboarding-community-link")} + (tr "onboarding-v2.welcome.desc2.title")]] + [:div {:class (stl/css :property-description)} + (tr "onboarding-v2.welcome.desc2")]]] - [:div.welcome-card - [:img {:src "images/contributing.svg" :border "0"}] - [:div - [:div.title [:a {:href "https://help.penpot.app/contributing-guide/" :target "_blank" :on-click #(send-event "onboarding-contributing-link")} (tr "onboarding-v2.welcome.desc3.title")]] - [:div.description (tr "onboarding-v2.welcome.desc3")]]]]] - [:div.modal-navigation - [:button.btn-secondary {:on-click go-next :data-test "onboarding-next-btn"} (tr "labels.continue")]] - [:img.deco.square {:src "images/deco-square.svg" :border "0"}] - [:img.deco.circle {:src "images/deco-circle.svg" :border "0"}] - [:img.deco.line1 {:src "images/deco-line1.svg" :border "0"}] - [:img.deco.line2 {:src "images/deco-line2.svg" :border "0"}]]])) + [:div {:class (stl/css :property-block)} + [:img {:src "images/contributing.svg" + :border "0"}] + [:div {:class (stl/css :text-wrapper)} + [:div {:class (stl/css :property-title)} + [:a {:href "https://help.penpot.app/contributing-guide/" + :target "_blank" :on-click #(send-event "onboarding-contributing-link")} + (tr "onboarding-v2.welcome.desc3.title")]] + [:div {:class (stl/css :property-description)} + (tr "onboarding-v2.welcome.desc3")]]]]] + + [:div {:class (stl/css :modal-footer)} + [:button {:on-click go-next + :data-test "onboarding-next-btn"} + (tr "labels.continue")]]]] + + + [:div.modal-container.onboarding.onboarding-v2 + [:div.modal-left.welcome + [:img {:src "images/onboarding-welcome.png" :border "0" :alt (tr "onboarding.welcome.alt")}]] + [:div.modal-right + [:div.release-container [:span.release "Version " (:main cf/version)]] + [:div.right-content + [:div.modal-title + [:h2 {:data-test "onboarding-welcome"} (tr "onboarding-v2.welcome.title")]] + + [:div.modal-content + [:p (tr "onboarding-v2.welcome.desc1")] + [:div.welcome-card + [:img {:src "images/community.svg" :border "0"}] + [:div + [:div.title [:a {:href "https://community.penpot.app/" :target "_blank" :on-click #(send-event "onboarding-community-link")} (tr "onboarding-v2.welcome.desc2.title")]] + [:div.description (tr "onboarding-v2.welcome.desc2")]]] + + [:div.welcome-card + [:img {:src "images/contributing.svg" :border "0"}] + [:div + [:div.title [:a {:href "https://help.penpot.app/contributing-guide/" :target "_blank" :on-click #(send-event "onboarding-contributing-link")} (tr "onboarding-v2.welcome.desc3.title")]] + [:div.description (tr "onboarding-v2.welcome.desc3")]]]]] + [:div.modal-navigation + [:button.btn-secondary {:on-click go-next :data-test "onboarding-next-btn"} (tr "labels.continue")]] + [:img.deco.square {:src "images/deco-square.svg" :border "0"}] + [:img.deco.circle {:src "images/deco-circle.svg" :border "0"}] + [:img.deco.line1 {:src "images/deco-line1.svg" :border "0"}] + [:img.deco.line2 {:src "images/deco-line2.svg" :border "0"}]]]))) (mf/defc onboarding-before-start [{:keys [next] :as props}] - (let [go-next + (let [new-css-system (mf/use-ctx ctx/new-css-system) + go-next (fn [] (send-event "onboarding-step2-continue") (next))] - [:div.modal-container.onboarding.onboarding-v2 - [:div.modal-left.welcome - [:img {:src "images/onboarding-people.png" :border "0" :alt (tr "onboarding.welcome.alt")}]] - [:div.modal-right - [:div.release-container [:span.release "Version " (:main cf/version)]] - [:div.right-content - [:div.modal-title - [:h2 {:data-test "onboarding-welcome"} (tr "onboarding-v2.before-start.title")]] + (if new-css-system + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-left)} + [:img {:src "images/onboarding-people.png" + :border "0" + :alt (tr "onboarding.welcome.alt")}]] + [:div {:class (stl/css :modal-right)} + [:div {:class (stl/css :release)} + "Version " (:main cf/version)] + [:div {:class (stl/css :modal-content)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title) + :data-test "onboarding-welcome"} + (tr "onboarding-v2.before-start.title")]] - [:div.modal-content - [:p (tr "onboarding-v2.before-start.desc1")] - [:div.welcome-card - [:img {:src "images/user-guide.svg" :border "0"}] - [:div - [:div.title [:a {:href "https://help.penpot.app/user-guide/" :target "_blank" :on-click #(send-event "onboarding-user-guide-link")} (tr "onboarding-v2.before-start.desc2.title")]] - [:div.description (tr "onboarding-v2.before-start.desc2")]]] + [:div {:class (stl/css :modal-info)} + [:p {:class (stl/css :modal-text)} + (tr "onboarding-v2.before-start.desc1")] + [:div {:class (stl/css :property-block)} + [:img {:src "images/user-guide.svg" :border "0"}] + [:div {:class (stl/css :text-wrapper)} + [:div {:class (stl/css :property-title)} + [:a {:class (stl/css :modal-link) + :href "https://help.penpot.app/user-guide/" + :target "_blank" + :on-click #(send-event "onboarding-user-guide-link")} + (tr "onboarding-v2.before-start.desc2.title")]] + [:div {:class (stl/css :property-description)} + (tr "onboarding-v2.before-start.desc2")]]] - [:div.welcome-card - [:img {:src "images/video-tutorials.svg" :border "0"}] - [:div - [:div.title [:a {:href "https://www.youtube.com/c/Penpot" :target "_blank" :on-click #(send-event "onboarding-video-tutorials-link")} (tr "onboarding-v2.before-start.desc3.title")]] - [:div.description (tr "onboarding-v2.before-start.desc3")]]]]] - [:div.modal-navigation - [:button.btn-secondary {:on-click go-next :data-test "onboarding-next-btn"} (tr "labels.continue")]] - [:img.deco.square {:src "images/deco-square.svg" :border "0"}] - [:img.deco.circle {:src "images/deco-circle.svg" :border "0"}] - [:img.deco.line1 {:src "images/deco-line1.svg" :border "0"}] - [:img.deco.line2 {:src "images/deco-line2.svg" :border "0"}]]])) + [:div {:class (stl/css :property-block)} + [:img {:src "images/video-tutorials.svg" :border "0"}] + [:div {:class (stl/css :text-wrapper)} + [:div {:class (stl/css :property-title)} + [:a {:class (stl/css :modal-link) + :href "https://www.youtube.com/c/Penpot" + :target "_blank" + :on-click #(send-event "onboarding-video-tutorials-link")} + (tr "onboarding-v2.before-start.desc3.title")]] + [:div {:class (stl/css :property-description)} + (tr "onboarding-v2.before-start.desc3")]]]]] + + [:div {:class (stl/css :modal-footer)} + [:button {:on-click go-next + :data-test "onboarding-next-btn"} + (tr "labels.continue")]]]] + + + [:div.modal-container.onboarding.onboarding-v2 + [:div.modal-left.welcome + [:img {:src "images/onboarding-people.png" :border "0" :alt (tr "onboarding.welcome.alt")}]] + [:div.modal-right + [:div.release-container [:span.release "Version " (:main cf/version)]] + [:div.right-content + [:div.modal-title + [:h2 {:data-test "onboarding-welcome"} (tr "onboarding-v2.before-start.title")]] + + [:div.modal-content + [:p (tr "onboarding-v2.before-start.desc1")] + [:div.welcome-card + [:img {:src "images/user-guide.svg" :border "0"}] + [:div + [:div.title [:a {:href "https://help.penpot.app/user-guide/" :target "_blank" :on-click #(send-event "onboarding-user-guide-link")} (tr "onboarding-v2.before-start.desc2.title")]] + [:div.description (tr "onboarding-v2.before-start.desc2")]]] + + [:div.welcome-card + [:img {:src "images/video-tutorials.svg" :border "0"}] + [:div + [:div.title [:a {:href "https://www.youtube.com/c/Penpot" :target "_blank" :on-click #(send-event "onboarding-video-tutorials-link")} (tr "onboarding-v2.before-start.desc3.title")]] + [:div.description (tr "onboarding-v2.before-start.desc3")]]]]] + [:div.modal-navigation + [:button.btn-secondary {:on-click go-next :data-test "onboarding-next-btn"} (tr "labels.continue")]] + [:img.deco.square {:src "images/deco-square.svg" :border "0"}] + [:img.deco.circle {:src "images/deco-circle.svg" :border "0"}] + [:img.deco.line1 {:src "images/deco-line1.svg" :border "0"}] + [:img.deco.line2 {:src "images/deco-line2.svg" :border "0"}]]]))) (mf/defc onboarding-modal {::mf/register modal/components ::mf/register-as :onboarding} [_] - (let [slide (mf/use-state :start) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + slide (mf/use-state :start) klass (mf/use-state "fadeInDown") navigate @@ -124,9 +225,17 @@ (fn [] (reset! klass nil) (tm/dispose! sem)))) + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div.animated {:class(dm/str @klass " " (stl/css :animated))} + (case @slide + :start [:& onboarding-welcome {:next #(navigate :opensource)}] + :opensource [:& onboarding-before-start {:next skip}])]] - [:div.modal-overlay - [:div.animated {:class @klass} - (case @slide - :start [:& onboarding-welcome {:next #(navigate :opensource)}] - :opensource [:& onboarding-before-start {:next skip}])]])) + + + [:div.modal-overlay + [:div.animated {:class @klass} + (case @slide + :start [:& onboarding-welcome {:next #(navigate :opensource)}] + :opensource [:& onboarding-before-start {:next skip}])]]))) diff --git a/frontend/src/app/main/ui/onboarding.scss b/frontend/src/app/main/ui/onboarding.scss new file mode 100644 index 000000000..0ff0507a7 --- /dev/null +++ b/frontend/src/app/main/ui/onboarding.scss @@ -0,0 +1,106 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.modal-overlay { + @extend .modal-overlay-base; +} + +.modal-container { + @extend .modal-container-base; + position: relative; + display: flex; + padding: 0; + margin: 0; + min-width: $s-712; + border: $s-1 solid var(--modal-border-color); +} + +.modal-left { + width: $s-284; + img { + width: $s-284; + height: 100%; + border-radius: $br-8 0 0 $br-8; + } +} + +.modal-right { + @include flexColumn; + position: relative; + flex-grow: 1; + padding: $s-32; +} + +.release { + @include titleTipography; + position: absolute; + top: 0; + right: 0; + padding: $s-8; + color: var(--modal-text-foreground-color); +} + +.modal-content { + @include flexColumn; + @include titleTipography; + gap: $s-24; + flex-grow: 1; + margin-bottom: $s-24; +} + +.modal-title { + @include bigTitleTipography; + color: var(--modal-title-foreground-color); +} + +.modal-info { + @include flexColumn; +} + +.modal-text { + @include titleTipography; +} + +.property-block { + @include flexRow; + gap: $s-16; + margin-bottom: $s-24; + img { + @include flexCenter; + height: $s-40; + width: $s-40; + } +} + +.modal-link { + @include titleTipography; + color: var(--modal-link-foreground-color); + margin: 0; +} + +.text-wrapper { + @include flexColumn; +} + +.property-title a { + @include titleTipography; + color: var(--modal-title-foreground-color); +} + +.property-description { + @include titleTipography; + color: var(--modal-text-foreground-color); +} + +.modal-footer { + display: flex; + justify-content: flex-end; + button { + @extend .modal-accept-btn; + } +} diff --git a/frontend/src/app/main/ui/onboarding/newsletter.cljs b/frontend/src/app/main/ui/onboarding/newsletter.cljs index 33b465840..c147a7d5e 100644 --- a/frontend/src/app/main/ui/onboarding/newsletter.cljs +++ b/frontend/src/app/main/ui/onboarding/newsletter.cljs @@ -5,11 +5,14 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.onboarding.newsletter + (:require-macros [app.main.style :as stl]) (:require [app.main.data.messages :as dm] [app.main.data.modal :as modal] [app.main.data.users :as du] [app.main.store :as st] + [app.main.ui.context :as ctx] + [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) @@ -17,9 +20,10 @@ {::mf/register modal/components ::mf/register-as :onboarding-newsletter-modal} [] - (let [message (tr "onboarding.newsletter.acceptance-message") + (let [new-css-system (mf/use-ctx ctx/new-css-system) + message (tr "onboarding.newsletter.acceptance-message") newsletter-updates (mf/use-state false) - newsletter-news (mf/use-state false) + newsletter-news (mf/use-state false) toggle (mf/use-callback (fn [option] @@ -35,27 +39,78 @@ (modal/show {:type :onboarding-team}) (du/update-profile-props {:newsletter-updates @newsletter-updates :newsletter-news @newsletter-news}))))] - [:div.modal-overlay - [:div.modal-container.onboarding.newsletter.animated.fadeInDown - [:div.modal-top - [:h1.newsletter-title {:data-test "onboarding-newsletter-title"} (tr "onboarding.newsletter.title")] - [:p (tr "onboarding-v2.newsletter.desc")]] - [:div.modal-bottom - [:div.newsletter-options - [:div.input-checkbox.check-primary - [:input {:type "checkbox" - :id "newsletter-updates" - :on-change #(toggle newsletter-updates)}] - [:label {:for "newsletter-updates"} (tr "onboarding-v2.newsletter.updates")]] - [:div.input-checkbox.check-primary - [:input {:type "checkbox" - :id "newsletter-news" - :on-change #(toggle newsletter-news)}] - [:label {:for "newsletter-news"} (tr "onboarding-v2.newsletter.news")]]] - [:p (tr "onboarding-v2.newsletter.privacy1") [:a {:target "_blank" :href "https://penpot.app/privacy"} (tr "onboarding.newsletter.policy")]] - [:p (tr "onboarding-v2.newsletter.privacy2")]] - [:div.modal-footer - [:button.btn-primary {:on-click accept} (tr "labels.continue")]] - [:img.deco.top {:src "images/deco-newsletter.png" :border "0"}] - [:img.deco.newsletter-left {:src "images/deco-news-left.png" :border "0"}] - [:img.deco.newsletter-right {:src "images/deco-news-right.png" :border "0"}]]])) + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div.animated.fadeInDown {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title) + :data-test "onboarding-newsletter-title"} + (tr "onboarding.newsletter.title")] + [:p {:class (stl/css :modal-text)} + (tr "onboarding-v2.newsletter.desc")]] + [:div {:class (stl/css :modal-content)} + [:div {:class (stl/css :newsletter-options)} + [:div {:class (stl/css :input-wrapper)} + [:label {:for "newsletter-updates"} + [:span {:class (stl/css-case :global/checked @newsletter-updates)} + (when @newsletter-updates + i/status-tick-refactor)] + (tr "onboarding-v2.newsletter.updates") + [:input {:type "checkbox" + :id "newsletter-updates" + :on-change #(toggle newsletter-updates)}]]] + + [:div {:class (stl/css :input-wrapper)} + [:label {:for "newsletter-news"} + [:span {:class (stl/css-case :global/checked @newsletter-news)} + (when @newsletter-news + i/status-tick-refactor)] + (tr "onboarding-v2.newsletter.news") + [:input {:type "checkbox" + :id "newsletter-news" + :on-change #(toggle newsletter-news)}]]]] + + [:div {:class (stl/css :modal-info)} + [:p {:class (stl/css :modal-text)} + (tr "onboarding-v2.newsletter.privacy1") + [:a {:class (stl/css :modal-link) + :target "_blank" + :href "https://penpot.app/privacy"} + (tr "onboarding.newsletter.policy")]] + [:p {:class (stl/css :modal-text)} + (tr "onboarding-v2.newsletter.privacy2")]]] + + [:div {:class (stl/css :modal-footer)} + [:button {:on-click accept} (tr "labels.continue")]] + + [:img {:class (stl/css-case :deco true + :top true) + :src "images/deco-newsletter.png" :border "0"}]]] + + + + [:div.modal-overlay + [:div.modal-container.onboarding.newsletter.animated.fadeInDown + [:div.modal-top + [:h1.newsletter-title {:data-test "onboarding-newsletter-title"} (tr "onboarding.newsletter.title")] + [:p (tr "onboarding-v2.newsletter.desc")]] + [:div.modal-bottom + [:div.newsletter-options + [:div.input-checkbox.check-primary + [:input {:type "checkbox" + :id "newsletter-updates" + :on-change #(toggle newsletter-updates)}] + [:label {:for "newsletter-updates"} (tr "onboarding-v2.newsletter.updates")]] + [:div.input-checkbox.check-primary + [:input {:type "checkbox" + :id "newsletter-news" + :on-change #(toggle newsletter-news)}] + [:label {:for "newsletter-news"} (tr "onboarding-v2.newsletter.news")]]] + [:p (tr "onboarding-v2.newsletter.privacy1") [:a {:target "_blank" :href "https://penpot.app/privacy"} (tr "onboarding.newsletter.policy")]] + [:p (tr "onboarding-v2.newsletter.privacy2")]] + [:div.modal-footer + [:button.btn-primary {:on-click accept} (tr "labels.continue")]] + [:img.deco.top {:src "images/deco-newsletter.png" :border "0"}] + [:img.deco.newsletter-left {:src "images/deco-news-left.png" :border "0"}] + [:img.deco.newsletter-right {:src "images/deco-news-right.png" :border "0"}]]]) + )) diff --git a/frontend/src/app/main/ui/onboarding/newsletter.scss b/frontend/src/app/main/ui/onboarding/newsletter.scss new file mode 100644 index 000000000..a407dcfa8 --- /dev/null +++ b/frontend/src/app/main/ui/onboarding/newsletter.scss @@ -0,0 +1,82 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.modal-overlay { + @extend .modal-overlay-base; +} + +.modal-container { + @extend .modal-container-base; + position: relative; + min-width: $s-712; + border: $s-1 solid var(--modal-border-color); +} + +.modal-header { + display: flex; + flex-direction: column; + justify-content: center; + margin-top: $s-80; + margin-bottom: $s-32; +} + +.modal-title { + @include bigTitleTipography; + text-align: center; + color: var(--modal-title-foreground-color); + margin-bottom: $s-16; +} + +.modal-text { + @include titleTipography; + color: var(--modal-text-foreground-color); + margin: 0; +} + +.modal-content { + @include flexColumn; + @include titleTipography; + gap: $s-32; + margin-bottom: $s-24; +} + +.newsletter-options { + @include flexColumn; + gap: $s-16; +} + +.input-wrapper { + @extend .input-checkbox; +} + +.modal-info { + @include flexColumn; + gap: $s-16; + margin-bottom: $s-32; +} + +.modal-link { + @include titleTipography; + color: var(--modal-link-foreground-color); + margin: 0; +} + +.modal-footer { + display: flex; + justify-content: flex-end; + button { + @extend .modal-accept-btn; + } +} + +.deco { + position: absolute; + width: 183px; + top: -106px; + left: 261px; +} diff --git a/frontend/src/app/main/ui/onboarding/questions.cljs b/frontend/src/app/main/ui/onboarding/questions.cljs index ca5d1fb08..1f5d4b9f4 100644 --- a/frontend/src/app/main/ui/onboarding/questions.cljs +++ b/frontend/src/app/main/ui/onboarding/questions.cljs @@ -6,12 +6,14 @@ (ns app.main.ui.onboarding.questions "External form for onboarding questions." + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dm] [app.main.data.users :as du] [app.main.store :as st] [app.main.ui.components.forms :as fm] + [app.main.ui.context :as ctx] [app.util.i18n :as i18n :refer [tr]] [cljs.spec.alpha :as s] [cuerdas.core :as str] @@ -19,67 +21,140 @@ (mf/defc step-container [{:keys [form step on-next on-prev children] :as props}] - [:& fm/form {:form form :on-submit on-next} - [:div.step-header - [:div.step-number (str/ffmt "%/4" step)]] - children - [:div.buttons - [:div.step-next - [:> fm/submit-button* - {:label (if (< step 4) (tr "questions.next") (tr "questions.start")) - :class "step-next"}]] + (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (when on-prev - [:div.step-prev - [:button {:on-click on-prev} (tr "questions.previous")]])]]) + (if new-css-system + [:& fm/form {:form form :on-submit on-next} + [:div {:class (stl/css :paginator)} (str/ffmt "%/4" step)] + + children + + [:div {:class (stl/css :action-buttons)} + + (when on-prev + [:button {:class (stl/css :prev-button) + :on-click on-prev} (tr "questions.previous")]) + + [:> fm/submit-button* + {:label (if (< step 4) (tr "questions.next") (tr "questions.start")) + :class (stl/css :next-button)}]]] + + + + [:& fm/form {:form form :on-submit on-next} + [:div.step-header + [:div.step-number (str/ffmt "%/4" step)]] + + children + + [:div.buttons + [:div.step-next + [:> fm/submit-button* + {:label (if (< step 4) (tr "questions.next") (tr "questions.start")) + :class "step-next"}]] + + (when on-prev + [:div.step-prev + [:button {:on-click on-prev} (tr "questions.previous")]])]]))) (s/def ::questions-form-step-1 (s/keys :req-un [::planning])) (mf/defc step-1 [{:keys [on-next form] :as props}] - [:& step-container {:form form :step 1 :on-next on-next} - [:img.header-image {:src "images/form/use-for-1.png" :alt (tr "questions.lets-get-started")}] - [:h1 (tr "questions.lets-get-started")] - [:p.intro (tr "questions.your-feedback-will-help-us")] - [:h3 (tr "questions.questions-how-are-you-planning-to-use-penpot")] - [:& fm/select {:options [{:label (tr "questions.select-option") :value "" :key "questions-how-are-you-planning-to-use-penpot" :disabled true} - {:label (tr "questions.discover-more-about-penpot") :value "discover-more-about-penpot" :key "discover-more-about-penpot"} - {:label (tr "questions.test-penpot-to-see-if-its-a-fit-for-team") :value "test-penpot-to-see-if-its-a-fit-for-team" :key "test-penpot-to-see-if-its-a-fit-for-team"} - {:label (tr "questions.start-to-work-on-my-project") :value "start-to-work-on-my-project" :key "start-to-work-on-my-project"} - {:label (tr "questions.get-the-code-from-my-team-project") :value "get-the-code-from-my-team-project" :key "get-the-code-from-my-team-project"} - {:label (tr "questions.leave-feedback-for-my-team-project") :value "leave-feedback-for-my-team-project" :key "leave-feedback-for-my-team-project"} - {:label (tr "questions.work-in-concept-ideas") :value "work-in-concept-ideas" :key "work-in-concept-ideas"} - {:label (tr "questions.try-out-before-using-penpot-on-premise") :value "try-out-before-using-penpot-on-premise" :key "try-out-before-using-penpot-on-premise"}] - :default "" - :name :planning}]]) + (let [new-css-system (mf/use-ctx ctx/new-css-system)] + (if new-css-system + [:& step-container {:form form :step 1 :on-next on-next} + [:img {:class (stl/css :header-image) + :src "images/form/use-for-1.png" :alt (tr "questions.lets-get-started")}] + [:h1 {:class (stl/css :modal-title)} (tr "questions.lets-get-started")] + [:p {:class (stl/css :modal-text)} (tr "questions.your-feedback-will-help-us")] + [:h3 {:class (stl/css :modal-subtitle)} (tr "questions.questions-how-are-you-planning-to-use-penpot")] + [:& fm/select {:options [{:label (tr "questions.select-option") :value "" :key "questions-how-are-you-planning-to-use-penpot" :disabled true} + {:label (tr "questions.discover-more-about-penpot") :value "discover-more-about-penpot" :key "discover-more-about-penpot"} + {:label (tr "questions.test-penpot-to-see-if-its-a-fit-for-team") :value "test-penpot-to-see-if-its-a-fit-for-team" :key "test-penpot-to-see-if-its-a-fit-for-team"} + {:label (tr "questions.start-to-work-on-my-project") :value "start-to-work-on-my-project" :key "start-to-work-on-my-project"} + {:label (tr "questions.get-the-code-from-my-team-project") :value "get-the-code-from-my-team-project" :key "get-the-code-from-my-team-project"} + {:label (tr "questions.leave-feedback-for-my-team-project") :value "leave-feedback-for-my-team-project" :key "leave-feedback-for-my-team-project"} + {:label (tr "questions.work-in-concept-ideas") :value "work-in-concept-ideas" :key "work-in-concept-ideas"} + {:label (tr "questions.try-out-before-using-penpot-on-premise") :value "try-out-before-using-penpot-on-premise" :key "try-out-before-using-penpot-on-premise"}] + :default "" + :name :planning}]] + + + [:& step-container {:form form :step 1 :on-next on-next} + [:img.header-image {:src "images/form/use-for-1.png" :alt (tr "questions.lets-get-started")}] + [:h1 (tr "questions.lets-get-started")] + [:p.intro (tr "questions.your-feedback-will-help-us")] + [:h3 (tr "questions.questions-how-are-you-planning-to-use-penpot")] + [:& fm/select {:options [{:label (tr "questions.select-option") :value "" :key "questions-how-are-you-planning-to-use-penpot" :disabled true} + {:label (tr "questions.discover-more-about-penpot") :value "discover-more-about-penpot" :key "discover-more-about-penpot"} + {:label (tr "questions.test-penpot-to-see-if-its-a-fit-for-team") :value "test-penpot-to-see-if-its-a-fit-for-team" :key "test-penpot-to-see-if-its-a-fit-for-team"} + {:label (tr "questions.start-to-work-on-my-project") :value "start-to-work-on-my-project" :key "start-to-work-on-my-project"} + {:label (tr "questions.get-the-code-from-my-team-project") :value "get-the-code-from-my-team-project" :key "get-the-code-from-my-team-project"} + {:label (tr "questions.leave-feedback-for-my-team-project") :value "leave-feedback-for-my-team-project" :key "leave-feedback-for-my-team-project"} + {:label (tr "questions.work-in-concept-ideas") :value "work-in-concept-ideas" :key "work-in-concept-ideas"} + {:label (tr "questions.try-out-before-using-penpot-on-premise") :value "try-out-before-using-penpot-on-premise" :key "try-out-before-using-penpot-on-premise"}] + :default "" + :name :planning}]]))) (s/def ::questions-form-step-2 (s/keys :req-un [::experience-branding-illustrations-marketing-pieces ::experience-interface-design-visual-assets-design-systems ::experience-interface-wireframes-user-journeys-flows-navigation-trees])) (mf/defc step-2 [{:keys [on-next on-prev form] :as props}] - [:& step-container {:form form :step 2 :on-next on-next :on-prev on-prev} - [:h3 (tr "questions.describe-your-experience-working-on")] + (let [new-css-system (mf/use-ctx ctx/new-css-system)] + (if new-css-system + [:& step-container {:form form :step 2 :on-next on-next :on-prev on-prev} + [:h3 {:class (stl/css :modal-subtitle)} + (tr "questions.describe-your-experience-working-on")] - [:div.section (tr "branding-illustrations-marketing-pieces")] - [:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"} - {:label (tr "questions.some") :value "some"} - {:label (tr "questions.a-lot") :value "a-lot"}] - :name :experience-branding-illustrations-marketing-pieces}] + [:div {:class (stl/css :modal-question)} + [:div {:class (stl/css :modal-text)} + (tr "branding-illustrations-marketing-pieces")] + [:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"} + {:label (tr "questions.some") :value "some"} + {:label (tr "questions.a-lot") :value "a-lot"}] + :name :experience-branding-illustrations-marketing-pieces}]] - [:div.section (tr "questions.interface-design-visual-assets-design-systems")] - [:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"} - {:label (tr "questions.some") :value "some"} - {:label (tr "questions.a-lot") :value "a-lot"}] - :name :experience-interface-design-visual-assets-design-systems}] + [:div {:class (stl/css :modal-question)} + [:div {:class (stl/css :modal-text)} + (tr "questions.interface-design-visual-assets-design-systems")] + [:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"} + {:label (tr "questions.some") :value "some"} + {:label (tr "questions.a-lot") :value "a-lot"}] + :name :experience-interface-design-visual-assets-design-systems}]] - [:div.section (tr "questions.wireframes-user-journeys-flows-navigation-trees")] - [:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"} - {:label (tr "questions.some") :value "some"} - {:label (tr "questions.a-lot") :value "a-lot"}] - :name :experience-interface-wireframes-user-journeys-flows-navigation-trees}]]) + [:div {:class (stl/css :modal-question)} + [:div {:class (stl/css :modal-text)} + (tr "questions.wireframes-user-journeys-flows-navigation-trees")] + [:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"} + {:label (tr "questions.some") :value "some"} + {:label (tr "questions.a-lot") :value "a-lot"}] + :name :experience-interface-wireframes-user-journeys-flows-navigation-trees}]]] + + + [:& step-container {:form form :step 2 :on-next on-next :on-prev on-prev} + [:h3 (tr "questions.describe-your-experience-working-on")] + + [:div.section (tr "branding-illustrations-marketing-pieces")] + [:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"} + {:label (tr "questions.some") :value "some"} + {:label (tr "questions.a-lot") :value "a-lot"}] + :name :experience-branding-illustrations-marketing-pieces}] + + [:div.section (tr "questions.interface-design-visual-assets-design-systems")] + [:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"} + {:label (tr "questions.some") :value "some"} + {:label (tr "questions.a-lot") :value "a-lot"}] + :name :experience-interface-design-visual-assets-design-systems}] + + [:div.section (tr "questions.wireframes-user-journeys-flows-navigation-trees")] + [:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"} + {:label (tr "questions.some") :value "some"} + {:label (tr "questions.a-lot") :value "a-lot"}] + :name :experience-interface-wireframes-user-journeys-flows-navigation-trees}]]))) (s/def ::questions-form-step-3 (s/keys :req-un [::experience-design-tool] @@ -95,7 +170,8 @@ (mf/defc step-3 [{:keys [on-next on-prev form] :as props}] - (let [experience-design-tool (dm/get-in @form [:clean-data :experience-design-tool]) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + experience-design-tool (dm/get-in @form [:clean-data :experience-design-tool]) on-design-tool-change (fn [_ _] (let [experience-design-tool (dm/get-in @form [:clean-data :experience-design-tool])] @@ -104,20 +180,40 @@ (swap! form d/dissoc-in [:data :experience-design-tool-other]) (swap! form d/dissoc-in [:errors :experience-design-tool-other])))))] - [:& step-container {:form form :step 3 :on-next on-next :on-prev on-prev} - [:h3 (tr "question.design-tool-more-experienced-with")] - [:& fm/radio-buttons {:options [{:label (tr "questions.figma") :value "figma" :image "images/form/figma.png"} - {:label (tr "questions.sketch") :value "sketch" :image "images/form/sketch.png"} - {:label (tr "questions.adobe-xd") :value "adobe-xd" :image "images/form/adobe-xd.png"} - {:label (tr "questions.canva") :value "canva" :image "images/form/canva.png"} - {:label (tr "questions.invision") :value "invision" :image "images/form/invision.png"} - {:label (tr "questions.never-used-a-tool") :value "never-used-a-tool" :image "images/form/never-used.png"} - {:label (tr "questions.other") :value "other"}] - :name :experience-design-tool - :on-change on-design-tool-change}] - [:div.other - [:label (tr "questions.other")] - [:& fm/input {:name :experience-design-tool-other :label (tr "questions.other") :disabled (not= experience-design-tool "other")}]]])) + (if new-css-system + [:& step-container {:form form :step 3 :on-next on-next :on-prev on-prev} + [:h3 {:class (stl/css :modal-subtitle)} + (tr "question.design-tool-more-experienced-with")] + [:& fm/radio-buttons {:options [{:label (tr "questions.figma") :value "figma" :image "images/form/figma.png"} + {:label (tr "questions.sketch") :value "sketch" :image "images/form/sketch.png"} + {:label (tr "questions.adobe-xd") :value "adobe-xd" :image "images/form/adobe-xd.png"} + {:label (tr "questions.canva") :value "canva" :image "images/form/canva.png"} + {:label (tr "questions.invision") :value "invision" :image "images/form/invision.png"} + {:label (tr "questions.never-used-a-tool") :value "never-used-a-tool" :image "images/form/never-used.png"} + {:label (tr "questions.other") :value "other"}] + :name :experience-design-tool + :on-change on-design-tool-change}] + + [:& fm/input {:name :experience-design-tool-other + :placeholder (tr "questions.other") + :label "" + :disabled (not= experience-design-tool "other")}]] + + + [:& step-container {:form form :step 3 :on-next on-next :on-prev on-prev} + [:h3 (tr "question.design-tool-more-experienced-with")] + [:& fm/radio-buttons {:options [{:label (tr "questions.figma") :value "figma" :image "images/form/figma.png"} + {:label (tr "questions.sketch") :value "sketch" :image "images/form/sketch.png"} + {:label (tr "questions.adobe-xd") :value "adobe-xd" :image "images/form/adobe-xd.png"} + {:label (tr "questions.canva") :value "canva" :image "images/form/canva.png"} + {:label (tr "questions.invision") :value "invision" :image "images/form/invision.png"} + {:label (tr "questions.never-used-a-tool") :value "never-used-a-tool" :image "images/form/never-used.png"} + {:label (tr "questions.other") :value "other"}] + :name :experience-design-tool + :on-change on-design-tool-change}] + [:div.other + [:label (tr "questions.other")] + [:& fm/input {:name :experience-design-tool-other :label (tr "questions.other") :disabled (not= experience-design-tool "other")}]]]))) (s/def ::questions-form-step-4 (s/keys :req-un [::team-size ::role] @@ -133,7 +229,8 @@ (mf/defc step-4 [{:keys [on-next on-prev form] :as props}] - (let [role (dm/get-in @form [:data :role]) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + role (dm/get-in @form [:data :role]) on-role-change (fn [_ _] (let [experience-design-tool (dm/get-in @form [:clean-data :experience-design-tool])] @@ -142,83 +239,122 @@ (swap! form d/dissoc-in [:data :role-other]) (swap! form d/dissoc-in [:errors :role-other])))))] - [:& step-container {:form form :step 4 :on-next on-next :on-prev on-prev} - [:h3 (tr "questions.role")] - [:& fm/radio-buttons {:options [{:label (tr "questions.designer") :value "designer"} - {:label (tr "questions.developer") :value "developer"} - {:label (tr "questions.manager") :value "manager"} - {:label (tr "questions.founder") :value "founder"} - {:label (tr "questions.marketing") :value "marketing"} - {:label (tr "questions.student-teacher") :value "student-teacher"} - {:label (tr "questions.other") :value "other"}] - :name :role - :on-change on-role-change}] - [:div.other - [:label (tr "questions.other")] - [:& fm/input {:name :role-other :label (tr "questions.other") :disabled (not= role "other")}]] + (if new-css-system + [:& step-container {:form form :step 4 :on-next on-next :on-prev on-prev} + [:h3 {:class (stl/css :modal-subtitle)} (tr "questions.role")] + [:& fm/radio-buttons {:options [{:label (tr "questions.designer") :value "designer"} + {:label (tr "questions.developer") :value "developer"} + {:label (tr "questions.manager") :value "manager"} + {:label (tr "questions.founder") :value "founder"} + {:label (tr "questions.marketing") :value "marketing"} + {:label (tr "questions.student-teacher") :value "student-teacher"} + {:label (tr "questions.other") :value "other"}] + :name :role + :on-change on-role-change}] + [:& fm/input {:name :role-other :label "" :placeholder (tr "questions.other") :disabled (not= role "other")}] - [:h3 (tr "questions.team-size")] - [:& fm/select {:options [{:label (tr "questions.select-option") :value "" :key "team-size" :disabled true} - {:label (tr "questions.more-than-50") :value "more-than-50" :key "more-than-50"} - {:label (tr "questions.31-50") :value "31-50" :key "31-50"} - {:label (tr "questions.11-30") :value "11-30" :key "11-30"} - {:label (tr "questions.2-10") :value "2-10" :key "2-10"} - {:label (tr "questions.freelancer") :value "freelancer" :key "freelancer"} - {:label (tr "questions.personal-project") :value "personal-project" :key "personal-project"}] - :default "" - :name :team-size}]])) + [:div {:class (stl/css :modal-question)} + [:h3 {:class (stl/css :modal-subtitle)} (tr "questions.team-size")] + [:& fm/select {:options [{:label (tr "questions.select-option") :value "" :key "team-size" :disabled true} + {:label (tr "questions.more-than-50") :value "more-than-50" :key "more-than-50"} + {:label (tr "questions.31-50") :value "31-50" :key "31-50"} + {:label (tr "questions.11-30") :value "11-30" :key "11-30"} + {:label (tr "questions.2-10") :value "2-10" :key "2-10"} + {:label (tr "questions.freelancer") :value "freelancer" :key "freelancer"} + {:label (tr "questions.personal-project") :value "personal-project" :key "personal-project"}] + :default "" + :name :team-size}]]] + + + [:& step-container {:form form :step 4 :on-next on-next :on-prev on-prev} + [:h3 (tr "questions.role")] + [:& fm/radio-buttons {:options [{:label (tr "questions.designer") :value "designer"} + {:label (tr "questions.developer") :value "developer"} + {:label (tr "questions.manager") :value "manager"} + {:label (tr "questions.founder") :value "founder"} + {:label (tr "questions.marketing") :value "marketing"} + {:label (tr "questions.student-teacher") :value "student-teacher"} + {:label (tr "questions.other") :value "other"}] + :name :role + :on-change on-role-change}] + [:div.other + [:label (tr "questions.other")] + [:& fm/input {:name :role-other :label (tr "questions.other") :disabled (not= role "other")}]] + + [:h3 (tr "questions.team-size")] + [:& fm/select {:options [{:label (tr "questions.select-option") :value "" :key "team-size" :disabled true} + {:label (tr "questions.more-than-50") :value "more-than-50" :key "more-than-50"} + {:label (tr "questions.31-50") :value "31-50" :key "31-50"} + {:label (tr "questions.11-30") :value "11-30" :key "11-30"} + {:label (tr "questions.2-10") :value "2-10" :key "2-10"} + {:label (tr "questions.freelancer") :value "freelancer" :key "freelancer"} + {:label (tr "questions.personal-project") :value "personal-project" :key "personal-project"}] + :default "" + :name :team-size}]]))) (mf/defc questions [{:keys []}] - (let [container (mf/use-ref) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + container (mf/use-ref) step (mf/use-state 1) clean-data (mf/use-state {}) ;; Forms are initialized here because we can go back and forth between the steps ;; and we want to keep the filled info step-1-form (fm/use-form - :initial {} - :spec ::questions-form-step-1) + :initial {} + :spec ::questions-form-step-1) step-2-form (fm/use-form - :initial {} - :spec ::questions-form-step-2) + :initial {} + :spec ::questions-form-step-2) step-3-form (fm/use-form - :initial {} - :validators [step-3-form-validator] - :spec ::questions-form-step-3) + :initial {} + :validators [step-3-form-validator] + :spec ::questions-form-step-3) step-4-form (fm/use-form - :initial {} - :validators [step-4-form-validator] - :spec ::questions-form-step-4) + :initial {} + :validators [step-4-form-validator] + :spec ::questions-form-step-4) on-next (mf/use-fn - (fn [form] - (swap! step inc) - (swap! clean-data merge (:clean-data @form)))) + (fn [form] + (swap! step inc) + (swap! clean-data merge (:clean-data @form)))) on-prev (mf/use-fn - (fn [] - (swap! step dec))) + (fn [] + (swap! step dec))) on-submit (mf/use-fn - (mf/deps @clean-data) - (fn [form] - (let [questionnaire (merge @clean-data (:clean-data @form))] - (reset! clean-data questionnaire) - (st/emit! (du/mark-questions-as-answered questionnaire)))))] + (mf/deps @clean-data) + (fn [form] + (let [questionnaire (merge @clean-data (:clean-data @form))] + (reset! clean-data questionnaire) + (st/emit! (du/mark-questions-as-answered questionnaire)))))] - [:div.modal-wrapper.questions-form - [:div.modal-overlay - [:div.modal-container.onboarding.onboarding-v2 {:ref container} - [:img.deco.left {:src "images/deco-left.png" :border 0}] - [:img.deco.right {:src "images/deco-right.png" :border 0}] - [:div.signup-questions + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container) + :ref container} (case @step 1 [:& step-1 {:on-next on-next :on-prev on-prev :form step-1-form}] 2 [:& step-2 {:on-next on-next :on-prev on-prev :form step-2-form}] 3 [:& step-3 {:on-next on-next :on-prev on-prev :form step-3-form}] - 4 [:& step-4 {:on-next on-submit :on-prev on-prev :form step-4-form}])]]]])) + 4 [:& step-4 {:on-next on-submit :on-prev on-prev :form step-4-form}])]] + + + [:div.modal-wrapper.questions-form + [:div.modal-overlay + [:div.modal-container.onboarding.onboarding-v2 {:ref container} + [:img.deco.left {:src "images/deco-left.png" :border 0}] + [:img.deco.right {:src "images/deco-right.png" :border 0}] + [:div.signup-questions + (case @step + 1 [:& step-1 {:on-next on-next :on-prev on-prev :form step-1-form}] + 2 [:& step-2 {:on-next on-next :on-prev on-prev :form step-2-form}] + 3 [:& step-3 {:on-next on-next :on-prev on-prev :form step-3-form}] + 4 [:& step-4 {:on-next on-submit :on-prev on-prev :form step-4-form}])]]]]))) diff --git a/frontend/src/app/main/ui/onboarding/questions.scss b/frontend/src/app/main/ui/onboarding/questions.scss new file mode 100644 index 000000000..e2024f7dd --- /dev/null +++ b/frontend/src/app/main/ui/onboarding/questions.scss @@ -0,0 +1,72 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.modal-overlay { + @extend .modal-overlay-base; +} + +.modal-container { + @extend .modal-container-base; + min-width: $s-512; + position: relative; + border: $s-1 solid var(--modal-border-color); +} + +// STEP CONTAINER +.paginator { + @include titleTipography; + position: absolute; + top: $s-8; + right: $s-8; + padding: $s-4; + border-radius: $br-6; + color: var(--color-foreground-secondary); +} + +.action-buttons { + @extend .modal-action-btns; + margin-top: $s-32; +} +.next-button { + @extend .modal-accept-btn; +} + +.prev-button { + @extend .modal-cancel-btn; +} + +// STEP 1 + +.header-image { + height: auto; + width: $s-200; +} + +.modal-title { + @include bigTitleTipography; + color: var(--modal-title-foreground-color); + margin: $s-32 0 $s-8 0; +} + +.modal-subtitle { + @include titleTipography; + color: var(--modal-title-foreground-color); +} + +// STEP-2 + +.modal-text { + @include titleTipography; + color: var(--modal-text-foreground-color); + margin: 0; +} + +.modal-question { + @include flexColumn; + margin-top: $s-32; +} diff --git a/frontend/src/app/main/ui/onboarding/team_choice.cljs b/frontend/src/app/main/ui/onboarding/team_choice.cljs index 06785c02a..c71c62410 100644 --- a/frontend/src/app/main/ui/onboarding/team_choice.cljs +++ b/frontend/src/app/main/ui/onboarding/team_choice.cljs @@ -5,6 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.onboarding.team-choice + (:require-macros [app.main.style :as stl]) (:require [app.common.spec :as us] [app.main.data.dashboard :as dd] @@ -14,6 +15,7 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.forms :as fm] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [tr]] [app.util.router :as rt] @@ -28,31 +30,64 @@ (mf/defc team-modal-right [] - [:div.team-right - [:h2.subtitle (tr "onboarding.team-modal.create-team")] - [:p.info (tr "onboarding.team-modal.create-team-desc")] - [:ul.team-features - [:li.feature - [:span.icon i/file-html] - [:p.feature-txt (tr "onboarding.team-modal.create-team-feature-1")]] - [:li.feature - [:span.icon i/pointer-inner] - [:p.feature-txt (tr "onboarding.team-modal.create-team-feature-2")]] - [:li.feature - [:span.icon i/tree] - [:p.feature-txt (tr "onboarding.team-modal.create-team-feature-3")]] - [:li.feature - [:span.icon i/user] - [:p.feature-txt (tr "onboarding.team-modal.create-team-feature-4")]] - [:li.feature - [:span.icon i/tick] - [:p.feature-txt (tr "onboarding.team-modal.create-team-feature-5")]]]]) + (let [new-css-system (mf/use-ctx ctx/new-css-system)] + (if new-css-system + + [:div {:class (stl/css :modal-right)} + [:h2 {:class (stl/css :modal-subtitle)} + (tr "onboarding.team-modal.create-team")] + [:p {:class (stl/css :modal-text)} + (tr "onboarding.team-modal.create-team-desc")] + [:ul {:class (stl/css :team-features)} + [:li {:class (stl/css :feature)} + [:span {:class (stl/css :icon)} i/document-refactor] + [:p {:class (stl/css :modal-text)} + (tr "onboarding.team-modal.create-team-feature-1")]] + [:li {:class (stl/css :feature)} + [:span {:class (stl/css :icon)} i/move-refactor] + [:p {:class (stl/css :modal-text)} + (tr "onboarding.team-modal.create-team-feature-2")]] + [:li {:class (stl/css :feature)} + [:span {:class (stl/css :icon)} i/tree-refactor] + [:p {:class (stl/css :modal-text)} + (tr "onboarding.team-modal.create-team-feature-3")]] + [:li {:class (stl/css :feature)} + [:span {:class (stl/css :icon)} i/user-refactor] + [:p {:class (stl/css :modal-text)} + (tr "onboarding.team-modal.create-team-feature-4")]] + [:li {:class (stl/css :feature)} + [:span {:class (stl/css :icon)} i/tick-refactor] + [:p {:class (stl/css :modal-text)} + (tr "onboarding.team-modal.create-team-feature-5")]]]] + + + + [:div.team-right + [:h2.subtitle (tr "onboarding.team-modal.create-team")] + [:p.info (tr "onboarding.team-modal.create-team-desc")] + [:ul.team-features + [:li.feature + [:span.icon i/file-html] + [:p.feature-txt (tr "onboarding.team-modal.create-team-feature-1")]] + [:li.feature + [:span.icon i/pointer-inner] + [:p.feature-txt (tr "onboarding.team-modal.create-team-feature-2")]] + [:li.feature + [:span.icon i/tree] + [:p.feature-txt (tr "onboarding.team-modal.create-team-feature-3")]] + [:li.feature + [:span.icon i/user] + [:p.feature-txt (tr "onboarding.team-modal.create-team-feature-4")]] + [:li.feature + [:span.icon i/tick] + [:p.feature-txt (tr "onboarding.team-modal.create-team-feature-5")]]]]))) (mf/defc onboarding-team-modal {::mf/register modal/components ::mf/register-as :onboarding-team} [] - (let [form (fm/use-form :spec ::team-form + (let [new-css-system (mf/use-ctx ctx/new-css-system) + form (fm/use-form :spec ::team-form :initial {} :validators [(fm/validate-not-empty :name (tr "auth.name.not-all-space")) (fm/validate-length :name fm/max-length-allowed (tr "auth.name.too-long"))]) @@ -73,36 +108,77 @@ :step 1})))) teams (mf/deref refs/teams)] - (if (< (count teams) 2) - [:div.modal-overlay - [:div.modal-container.onboarding-team.animated.fadeIn - [:div.team-left - [:h2.title (tr "onboarding.team-modal.create-team")] - [:p.info (tr "onboarding.choice.team-up.create-team-desc")] - [:& fm/form {:form form - :on-submit on-submit} - [:& fm/input {:type "text" - :name :name - :label (tr "onboarding.choice.team-up.create-team-placeholder")}] + (if new-css-system + (if (< (count teams) 2) + [:div {:class (stl/css :modal-overlay)} + [:div.animated.fadeIn {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-left)} + [:div {:class (stl/css :first-block)} + [:h2 {:class (stl/css :modal-title)} + (tr "onboarding.team-modal.create-team")] + [:p {:class (stl/css :modal-text)} + (tr "onboarding.choice.team-up.create-team-desc")] + [:& fm/form {:form form + :class (stl/css :modal-form) + :on-submit on-submit} - [:& fm/submit-button* - {:label (tr "onboarding.choice.team-up.continue-creating-team")}]] + [:& fm/input {:type "text" + :class (stl/css :team-name-input) + :name :name + :placeholder "Team name" + :label (tr "onboarding.choice.team-up.create-team-placeholder")}] - [:h2.title (tr "onboarding.choice.team-up.start-without-a-team")] - [:p.info (tr "onboarding.choice.team-up.start-without-a-team-description")] + [:div {:class (stl/css :action-buttons)} + [:& fm/submit-button* + {:className (stl/css :accept-button) + :label (tr "onboarding.choice.team-up.continue-creating-team")}]]]] + [:div {:class (stl/css :second-block)} + [:h2 {:class (stl/css :modal-title)} + (tr "onboarding.choice.team-up.start-without-a-team")] + [:p {:class (stl/css :modal-text)} + (tr "onboarding.choice.team-up.start-without-a-team-description")] - [:div - [:button.btn-primary.btn-large {:on-click on-skip} (tr "onboarding.choice.team-up.continue-without-a-team")]]] - [:& team-modal-right] - [:div.paginator "1/2"] + [:div {:class (stl/css :action-buttons)} + [:button {:class (stl/css :accept-button) + :on-click on-skip} + (tr "onboarding.choice.team-up.continue-without-a-team")]]]] + [:& team-modal-right] + [:div {:class (stl/css :paginator)} "1/2"]]] - [:img.deco.square {:src "images/deco-square.svg" :border "0"}] - [:img.deco.circle {:src "images/deco-circle.svg" :border "0"}] - [:img.deco.line1 {:src "images/deco-line1.svg" :border "0"}] - [:img.deco.line2 {:src "images/deco-line2.svg" :border "0"}]]] + (st/emit! (modal/hide))) - (st/emit! (modal/hide))))) + + (if (< (count teams) 2) + + [:div.modal-overlay + [:div.modal-container.onboarding-team.animated.fadeIn + [:div.team-left + [:h2.title (tr "onboarding.team-modal.create-team")] + [:p.info (tr "onboarding.choice.team-up.create-team-desc")] + [:& fm/form {:form form + :on-submit on-submit} + [:& fm/input {:type "text" + :name :name + :label (tr "onboarding.choice.team-up.create-team-placeholder")}] + + [:& fm/submit-button* + {:label (tr "onboarding.choice.team-up.continue-creating-team")}]] + + [:h2.title (tr "onboarding.choice.team-up.start-without-a-team")] + [:p.info (tr "onboarding.choice.team-up.start-without-a-team-description")] + + [:div + [:button.btn-primary.btn-large {:on-click on-skip} (tr "onboarding.choice.team-up.continue-without-a-team")]]] + [:& team-modal-right] + [:div.paginator "1/2"] + + [:img.deco.square {:src "images/deco-square.svg" :border "0"}] + [:img.deco.circle {:src "images/deco-circle.svg" :border "0"}] + [:img.deco.line1 {:src "images/deco-line1.svg" :border "0"}] + [:img.deco.line2 {:src "images/deco-line2.svg" :border "0"}]]] + + (st/emit! (modal/hide)))))) (defn get-available-roles [] @@ -121,14 +197,15 @@ {::mf/register modal/components ::mf/register-as :onboarding-team-invitations} [{:keys [name] :as props}] - (let [initial (mf/use-memo (constantly + (let [new-css-system (mf/use-ctx ctx/new-css-system) + initial (mf/use-memo (constantly {:role "editor" :name name})) form (fm/use-form :spec ::invite-form :initial initial) params (:clean-data @form) emails (:emails params) - + roles (mf/use-memo #(get-available-roles)) on-success @@ -167,7 +244,7 @@ :on-error (partial on-error form)} params (:clean-data @form) emails (:emails params)] - + (st/emit! (if (> (count emails) 0) ;; If the user is only inviting to itself we don't call to create-team-with-invitations (dd/create-team-with-invitations (with-meta params mdata)) @@ -178,7 +255,7 @@ :role (:role params) :name name :step 2}))))) - + on-submit (mf/use-callback (fn [_] @@ -188,49 +265,96 @@ (on-invite-now form) (on-invite-later form)))))] - [:div.modal-overlay - [:div.modal-container.onboarding-team-members.animated.fadeIn - [:div.team-left - [:h2.title (tr "onboarding.choice.team-up.invite-members")] - [:p.info (tr "onboarding.choice.team-up.invite-members-info")] + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div.animated.fadeIn {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-left)} + [:h2 {:class (stl/css :modal-title)} (tr "onboarding.choice.team-up.invite-members")] + [:p {:class (stl/css :modal-text)} (tr "onboarding.choice.team-up.invite-members-info")] - [:& fm/form {:form form - :on-submit on-submit} - [:div.invite-row - [:div.role-wrapper - [:span.rol (tr "onboarding.choice.team-up.roles")] - [:& fm/select {:name :role :options roles}]] + [:div {:class (stl/css :modal-form)} + [:& fm/form {:form form + :on-submit on-submit} + [:div {:class (stl/css :role-select)} + [:p {:class (stl/css :role-title)} (tr "onboarding.choice.team-up.roles")] + [:& fm/select {:name :role :options roles}]] - [:& fm/multi-input {:type "email" - :name :emails - :auto-focus? true - :trim true - :valid-item-fn us/parse-email - :caution-item-fn #{} - :on-submit on-submit - :label (tr "modals.invite-member.emails")}]] + [:div {:class (stl/css :invitation-row)} + [:& fm/multi-input {:type "email" + :name :emails + :auto-focus? true + :trim true + :valid-item-fn us/parse-email + :caution-item-fn #{} + :label (tr "modals.invite-member.emails") + :on-submit on-submit}]]] - [:div.buttons - [:button.btn-secondary.btn-large - {:on-click #(st/emit! (modal/show {:type :onboarding-team}) - (ptk/event ::ev/event {::ev/name "invite-members-back" - ::ev/origin "onboarding" - :name name - :step 2}))} - (tr "labels.back")] - [:& fm/submit-button* - {:label - (if (> (count emails) 0) - (tr "onboarding.choice.team-up.create-team-and-send-invites") - (tr "onboarding.choice.team-up.create-team-without-inviting"))}]] - [:div.skip-action - (tr "onboarding.choice.team-up.create-team-and-send-invites-description")]]] - [:& team-modal-right] - [:div.paginator "2/2"] + [:div {:class (stl/css :action-buttons)} + [:button {:class (stl/css :back-button) + :on-click #(st/emit! (modal/show {:type :onboarding-team}) + (ptk/event ::ev/event {::ev/name "invite-members-back" + ::ev/origin "onboarding" + :name name + :step 2}))} + (tr "labels.back")] - [:img.deco.square {:src "images/deco-square.svg" :border "0"}] - [:img.deco.circle {:src "images/deco-circle.svg" :border "0"}] - [:img.deco.line1 {:src "images/deco-line1.svg" :border "0"}] - [:img.deco.line2 {:src "images/deco-line2.svg" :border "0"}]]])) + [:& fm/submit-button* + {:className (stl/css :accept-button) + :label + (if (> (count emails) 0) + (tr "onboarding.choice.team-up.create-team-and-invite") + (tr "onboarding.choice.team-up.create-team-without-invite"))}]] + [:div {:class (stl/css :modal-hint)} + (tr "onboarding.choice.team-up.create-team-and-send-invites-description")]]] + + [:& team-modal-right] + [:div {:class (stl/css :paginator)} "2/2"]]] + + + + [:div.modal-overlay + [:div.modal-container.onboarding-team-members.animated.fadeIn + [:div.team-left + [:h2.title (tr "onboarding.choice.team-up.invite-members")] + [:p.info (tr "onboarding.choice.team-up.invite-members-info")] + + [:& fm/form {:form form + :on-submit on-submit} + [:div.invite-row + [:div.role-wrapper + [:span.rol (tr "onboarding.choice.team-up.roles")] + [:& fm/select {:name :role :options roles}]] + + [:& fm/multi-input {:type "email" + :name :emails + :auto-focus? true + :trim true + :valid-item-fn us/parse-email + :caution-item-fn #{} + :on-submit on-submit + :label (tr "modals.invite-member.emails")}]] + + [:div.buttons + [:button.btn-secondary.btn-large + {:on-click #(st/emit! (modal/show {:type :onboarding-team}) + (ptk/event ::ev/event {::ev/name "invite-members-back" + ::ev/origin "onboarding" + :name name + :step 2}))} + (tr "labels.back")] + [:& fm/submit-button* + {:label + (if (> (count emails) 0) + (tr "onboarding.choice.team-up.create-team-and-send-invites") + (tr "onboarding.choice.team-up.create-team-without-inviting"))}]] + [:div.skip-action + (tr "onboarding.choice.team-up.create-team-and-send-invites-description")]]] + [:& team-modal-right] + [:div.paginator "2/2"] + + [:img.deco.square {:src "images/deco-square.svg" :border "0"}] + [:img.deco.circle {:src "images/deco-circle.svg" :border "0"}] + [:img.deco.line1 {:src "images/deco-line1.svg" :border "0"}] + [:img.deco.line2 {:src "images/deco-line2.svg" :border "0"}]]]))) diff --git a/frontend/src/app/main/ui/onboarding/team_choice.scss b/frontend/src/app/main/ui/onboarding/team_choice.scss new file mode 100644 index 000000000..1ebf06798 --- /dev/null +++ b/frontend/src/app/main/ui/onboarding/team_choice.scss @@ -0,0 +1,145 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.modal-overlay { + @extend .modal-overlay-base; +} + +.modal-container { + @extend .modal-container-base; + padding: 0; + display: flex; + position: relative; + min-width: $s-712; + border: $s-1 solid var(--modal-border-color); +} + +.modal-left { + width: $s-356; + padding: $s-48 $s-28 $s-48 $s-48; +} + +.first-block, +.second-block { + @include flexColumn; + width: 100%; +} + +.first-block { + margin-bottom: $s-72; +} + +.modal-right { + width: $s-356; + padding: $s-48; + background-color: var(--color-background-tertiary); +} + +.modal-title { + @include bigTitleTipography; + color: var(--modal-title-foreground-color); + margin-bottom: $s-8; +} + +.modal-subtitle { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); + margin-bottom: $s-8; +} + +.modal-text, +.modal-hint { + @include titleTipography; + color: var(--modal-text-foreground-color); + margin: 0; +} +.modal-hint { + margin-top: $s-24; +} +.modal-form { + margin: $s-24 0; +} + +.team-name-input { + @extend .input-element-label; + label { + @include flexColumn; + @include titleTipography; + align-items: flex-start; + width: 100%; + border: none; + background-color: transparent; + height: 100%; + + input { + @include titleTipography; + margin-top: $s-8; + } + } +} + +.role-select { + @include flexColumn; + .role-title { + @include titleTipography; + margin: 0; + color: var(--modal-title-foreground-color); + } +} + +.invitation-row { + margin-top: $s-8; + margin-bottom: $s-24; +} + +.paginator { + @include titleTipography; + position: absolute; + top: $s-8; + right: $s-8; + padding: $s-4; + border-radius: $br-6; + color: var(--color-foreground-secondary); +} + +.action-buttons { + @extend .modal-action-btns; + justify-content: flex-start; + margin-top: $s-24; +} + +.accept-button { + @extend .modal-accept-btn; +} + +.back-button { + @extend .modal-cancel-btn; +} + +.team-features { + @include flexColumn; + gap: $s-24; + margin-top: $s-24; +} + +.feature { + @include flexRow; + gap: $s-8; +} + +.icon { + @include flexCenter; + height: $s-32; + width: $s-32; + border-radius: $br-circle; + background-color: var(--color-accent-primary); + svg { + @extend .button-icon; + stroke: var(--color-background-tertiary); + } +} diff --git a/frontend/src/app/main/ui/onboarding/templates.cljs b/frontend/src/app/main/ui/onboarding/templates.cljs index 01ed35345..2ce17a7d8 100644 --- a/frontend/src/app/main/ui/onboarding/templates.cljs +++ b/frontend/src/app/main/ui/onboarding/templates.cljs @@ -44,8 +44,7 @@ (fn [error] (js/console.log "error" error)) (fn [] - (reset! downloading? false))))) - ] + (reset! downloading? false)))))] [:div.template-item [:div.template-item-content diff --git a/frontend/src/app/main/ui/releases.scss b/frontend/src/app/main/ui/releases.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/main/ui/settings/access_tokens.cljs b/frontend/src/app/main/ui/settings/access_tokens.cljs index 3e4947ce9..617b76e60 100644 --- a/frontend/src/app/main/ui/settings/access_tokens.cljs +++ b/frontend/src/app/main/ui/settings/access_tokens.cljs @@ -5,6 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.settings.access-tokens + (:require-macros [app.main.style :as stl]) (:require [app.common.spec :as us] [app.main.data.messages :as dm] @@ -13,6 +14,7 @@ [app.main.store :as st] [app.main.ui.components.context-menu-a11y :refer [context-menu-a11y]] [app.main.ui.components.forms :as fm] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -49,7 +51,8 @@ {::mf/register modal/components ::mf/register-as :access-token} [] - (let [form (fm/use-form + (let [new-css-system (mf/use-ctx ctx/new-css-system) + form (fm/use-form :initial initial-data :spec ::access-token-form :validators [name-validator @@ -61,38 +64,38 @@ on-success (mf/use-fn - (mf/deps created) - (fn [_] - (let [message (tr "dashboard.access-tokens.create.success")] - (st/emit! (du/fetch-access-tokens) - (dm/success message) - (reset! created? true))))) + (mf/deps created) + (fn [_] + (let [message (tr "dashboard.access-tokens.create.success")] + (st/emit! (du/fetch-access-tokens) + (dm/success message) + (reset! created? true))))) on-close (mf/use-fn - (mf/deps created) - (fn [_] - (reset! created? false) - (st/emit! (modal/hide)))) + (mf/deps created) + (fn [_] + (reset! created? false) + (st/emit! (modal/hide)))) on-error (mf/use-fn - (fn [_] - (st/emit! (dm/error (tr "errors.generic")) - (modal/hide)))) + (fn [_] + (st/emit! (dm/error (tr "errors.generic")) + (modal/hide)))) on-submit (mf/use-fn - (fn [form] - (let [cdata (:clean-data @form) - mdata {:on-success (partial on-success form) - :on-error (partial on-error form)} - expiration (:expiration-date cdata) - params (cond-> {:name (:name cdata) - :perms (:perms cdata)} - (not= "never" expiration) (assoc :expiration expiration))] - (st/emit! (du/create-access-token - (with-meta params mdata)))))) + (fn [form] + (let [cdata (:clean-data @form) + mdata {:on-success (partial on-success form) + :on-error (partial on-error form)} + expiration (:expiration-date cdata) + params (cond-> {:name (:name cdata) + :perms (:perms cdata)} + (not= "never" expiration) (assoc :expiration expiration))] + (st/emit! (du/create-access-token + (with-meta params mdata)))))) copy-token (mf/use-fn @@ -104,70 +107,143 @@ :content (tr "dashboard.access-tokens.copied-success") :timeout 1000}))))] - [:div.modal-overlay - [:div.modal-container.access-tokens-modal - [:& fm/form {:form form :on-submit on-submit} + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:& fm/form {:form form :on-submit on-submit} - [:div.modal-header - [:div.modal-header-title - [:h2 (tr "modals.create-access-token.title")]] + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} (tr "modals.create-access-token.title")] - [:div.modal-close-button - {:on-click on-close} i/close]] + [:button {:class (stl/css :modal-close-btn) + :on-click on-close} i/close-refactor]] - [:div.modal-content.generic-form - [:div.fields-container - [:div.fields-row - [:& fm/input {:type "text" - :auto-focus? true - :form form - :name :name - :disabled @created? - :label (tr "modals.create-access-token.name.label") - :placeholder (tr "modals.create-access-token.name.placeholder")}]] - - [:div.fields-row - [:& fm/select {:options [{:label (tr "dashboard.access-tokens.expiration-never") :value "never" :key "never"} - {:label (tr "dashboard.access-tokens.expiration-30-days") :value "720h" :key "720h"} - {:label (tr "dashboard.access-tokens.expiration-60-days") :value "1440h" :key "1440h"} - {:label (tr "dashboard.access-tokens.expiration-90-days") :value "2160h" :key "2160h"} - {:label (tr "dashboard.access-tokens.expiration-180-days") :value "4320h" :key "4320h"}] - :label (tr "modals.create-access-token.expiration-date.label") - :default "never" + [:div {:class (stl/css :modal-content)} + [:div {:class (stl/css :fields-row)} + [:& fm/input {:type "text" + :auto-focus? true + :form form + :name :name :disabled @created? - :name :expiration-date}] - (when @created? - [:span.token-created-info - (if (:expires-at created) - (tr "dashboard.access-tokens.token-will-expire" (dt/format-date-locale (:expires-at created) {:locale locale})) - (tr "dashboard.access-tokens.token-will-not-expire"))])] + :label (tr "modals.create-access-token.name.label") + :placeholder (tr "modals.create-access-token.name.placeholder")}]] - [:div.fields-row.access-token-created - (when @created? - [:div.custom-input.with-icon - [:input {:type "text" - :value (:token created "") - :placeholder (tr "modals.create-access-token.token") - :read-only true}] - [:button.help-icon {:title (tr "modals.create-access-token.copy-token") - :on-click copy-token} + [:div {:class (stl/css :fields-row)} + [:div {:class (stl/css :select-title)} (tr "modals.create-access-token.expiration-date.label")] + [:& fm/select {:options [{:label (tr "dashboard.access-tokens.expiration-never") :value "never" :key "never"} + {:label (tr "dashboard.access-tokens.expiration-30-days") :value "720h" :key "720h"} + {:label (tr "dashboard.access-tokens.expiration-60-days") :value "1440h" :key "1440h"} + {:label (tr "dashboard.access-tokens.expiration-90-days") :value "2160h" :key "2160h"} + {:label (tr "dashboard.access-tokens.expiration-180-days") :value "4320h" :key "4320h"}] + :default "never" + :disabled @created? + :name :expiration-date}] + (when @created? + [:span.token-created-info + (if (:expires-at created) + (tr "dashboard.access-tokens.token-will-expire" (dt/format-date-locale (:expires-at created) {:locale locale})) + (tr "dashboard.access-tokens.token-will-not-expire"))])] - i/copy]])]]] + [:div {:class (stl/css :fields-row)} + (when @created? + [:div {:class (stl/css :custon-input-wrapper)} + [:input {:type "text" + :value (:token created "") + :class (stl/css :custom-input-token) + :placeholder (tr "modals.create-access-token.token") + :read-only true}] + [:button {:title (tr "modals.create-access-token.copy-token") + :class (stl/css :copy-btn) + :on-click copy-token} + i/clipboard-refactor]]) + #_(when @created? + [:button {:class (stl/css :copy-btn) + :title (tr "modals.create-access-token.copy-token") + :on-click copy-token} + [:span {:class (stl/css :token-value)}(:token created "")] + [:span {:class (stl/css :icon)} + i/clipboard-refactor]])]] - [:div.modal-footer - [:div.action-buttons - (if @created? - [:input.cancel-button - {:type "button" - :value (tr "labels.close") - :on-click #(modal/hide!)}] - [:* - [:input.cancel-button - {:type "button" - :value (tr "labels.cancel") - :on-click #(modal/hide!)}] - [:> fm/submit-button* - {:label (tr "modals.create-access-token.submit-label")}]])]]]]])) + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + + (if @created? + [:input {:class (stl/css :cancel-button) + :type "button" + :value (tr "labels.close") + :on-click #(modal/hide!)}] + [:* + [:input {:class (stl/css :cancel-button) + :type "button" + :value (tr "labels.cancel") + :on-click #(modal/hide!)}] + [:> fm/submit-button* + {:label (tr "modals.create-access-token.submit-label")}]])]]]]] + + + [:div.modal-overlay + [:div.modal-container.access-tokens-modal + [:& fm/form {:form form :on-submit on-submit} + + [:div.modal-header + [:div.modal-header-title + [:h2 (tr "modals.create-access-token.title")]] + + [:div.modal-close-button + {:on-click on-close} i/close]] + + [:div.modal-content.generic-form + [:div.fields-container + [:div.fields-row + [:& fm/input {:type "text" + :auto-focus? true + :form form + :name :name + :disabled @created? + :label (tr "modals.create-access-token.name.label") + :placeholder (tr "modals.create-access-token.name.placeholder")}]] + + [:div.fields-row + [:& fm/select {:options [{:label (tr "dashboard.access-tokens.expiration-never") :value "never" :key "never"} + {:label (tr "dashboard.access-tokens.expiration-30-days") :value "720h" :key "720h"} + {:label (tr "dashboard.access-tokens.expiration-60-days") :value "1440h" :key "1440h"} + {:label (tr "dashboard.access-tokens.expiration-90-days") :value "2160h" :key "2160h"} + {:label (tr "dashboard.access-tokens.expiration-180-days") :value "4320h" :key "4320h"}] + :label (tr "modals.create-access-token.expiration-date.label") + :default "never" + :disabled @created? + :name :expiration-date}] + (when @created? + [:span.token-created-info + (if (:expires-at created) + (tr "dashboard.access-tokens.token-will-expire" (dt/format-date-locale (:expires-at created) {:locale locale})) + (tr "dashboard.access-tokens.token-will-not-expire"))])] + + [:div.fields-row.access-token-created + (when @created? + [:div.custom-input.with-icon + [:input {:type "text" + :value (:token created "") + :placeholder (tr "modals.create-access-token.token") + :read-only true}] + [:button.help-icon {:title (tr "modals.create-access-token.copy-token") + :on-click copy-token} + i/copy]])]]] + + [:div.modal-footer + [:div.action-buttons + (if @created? + [:input.cancel-button + {:type "button" + :value (tr "labels.close") + :on-click #(modal/hide!)}] + [:* + [:input.cancel-button + {:type "button" + :value (tr "labels.cancel") + :on-click #(modal/hide!)}] + [:> fm/submit-button* + {:label (tr "modals.create-access-token.submit-label")}]])]]]]]))) (mf/defc access-tokens-hero [] diff --git a/frontend/src/app/main/ui/settings/access_tokens.scss b/frontend/src/app/main/ui/settings/access_tokens.scss new file mode 100644 index 000000000..a9d63c3de --- /dev/null +++ b/frontend/src/app/main/ui/settings/access_tokens.scss @@ -0,0 +1,82 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.modal-overlay { + @extend .modal-overlay-base; + .modal-container { + @extend .modal-container-base; + min-width: $s-408; + border: $s-1 solid var(--modal-border-color); + .modal-header { + margin-bottom: $s-24; + .modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); + } + .modal-close-btn { + @extend .modal-close-btn-base; + } + } + + .modal-content { + @include flexColumn; + gap: $s-24; + @include titleTipography; + margin-bottom: $s-24; + + .fields-row { + @include flexColumn; + .select-title { + @include titleTipography; + color: var(--modal-title-foreground-color); + } + .custon-input-wrapper { + @include flexRow; + border-radius: $br-8; + height: $s-32; + background-color: var(--input-background-color); + } + .custom-input-token { + @extend .input-element; + margin: 0; + flex-grow: 1; + &:focus { + outline: none; + border: $s-1 solid var(--input-border-color-active); + } + } + .token-value { + @include textEllipsis; + @include titleTipography; + flex-grow: 1; + } + .copy-btn { + @include flexCenter; + @extend .button-secondary; + height: $s-28; + width: $s-28; + svg { + @extend .button-icon-small; + } + } + } + } + + .modal-footer { + .action-buttons { + @extend .modal-action-btns; + button { + @extend .modal-accept-btn; + } + .cancel-button { + @extend .modal-cancel-btn; + } + } + } + } +} diff --git a/frontend/src/app/main/ui/settings/change_email.cljs b/frontend/src/app/main/ui/settings/change_email.cljs index 996d4668c..391fa4475 100644 --- a/frontend/src/app/main/ui/settings/change_email.cljs +++ b/frontend/src/app/main/ui/settings/change_email.cljs @@ -5,6 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.settings.change-email + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dma] @@ -15,6 +16,7 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.forms :as fm] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.ui.messages :as msgs] [app.util.i18n :as i18n :refer [tr]] @@ -74,7 +76,8 @@ {::mf/register modal/components ::mf/register-as :change-email} [] - (let [profile (mf/deref refs/profile) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + profile (mf/deref refs/profile) form (fm/use-form :spec ::email-change-form :validators [email-equality] :initial profile) @@ -85,7 +88,7 @@ (mf/use-callback (mf/deps profile) (partial on-submit profile)) - + on-email-change (mf/use-callback (fn [_ _] @@ -96,41 +99,81 @@ (when (and different-emails-error? (= email-1 email-2)) (swap! form d/dissoc-in [:errors :email-2])))))] - [:div.modal-overlay - [:div.modal-container.change-email-modal.form-container - [:& fm/form {:form form - :on-submit on-submit} + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:& fm/form {:form form + :on-submit on-submit} - [:div.modal-header - [:div.modal-header-title - [:h2 {:data-test "change-email-title"} - (tr "modals.change-email.title")]] - [:div.modal-close-button - {:on-click on-close} i/close]] + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title) + :data-test "change-email-title"} + (tr "modals.change-email.title")] + [:button {:class (stl/css :modal-close-btn) + :on-click on-close} i/close-refactor]] - [:div.modal-content - [:& msgs/inline-banner - {:type :info - :content (tr "modals.change-email.info" (:email profile))}] + [:div {:class (stl/css :modal-content)} + [:& msgs/inline-banner + {:type :info + :content (tr "modals.change-email.info" (:email profile))}] - [:div.fields-container - [:div.fields-row - [:& fm/input {:type "email" - :name :email-1 - :label (tr "modals.change-email.new-email") - :trim true - :on-change-value on-email-change}]] - [:div.fields-row - [:& fm/input {:type "email" - :name :email-2 - :label (tr "modals.change-email.confirm-email") - :trim true - :on-change-value on-email-change}]]]] + [:div {:class (stl/css :fields-row)} + [:& fm/input {:type "email" + :name :email-1 + :label (tr "modals.change-email.new-email") + :trim true + :on-change-value on-email-change}]] - [:div.modal-footer - [:div.action-buttons {:data-test "change-email-submit"} - [:> fm/submit-button* - {:label (tr "modals.change-email.submit")}]]]]]])) + [:div {:class (stl/css :fields-row)} + [:& fm/input {:type "email" + :name :email-2 + :label (tr "modals.change-email.confirm-email") + :trim true + :on-change-value on-email-change}]]] + + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons) + :data-test "change-email-submit"} + [:> fm/submit-button* + {:label (tr "modals.change-email.submit")}]]]]]] + + + [:div.modal-overlay + [:div.modal-container.change-email-modal.form-container + [:& fm/form {:form form + :on-submit on-submit} + + [:div.modal-header + [:div.modal-header-title + [:h2 {:data-test "change-email-title"} + (tr "modals.change-email.title")]] + [:div.modal-close-button + {:on-click on-close} i/close]] + + [:div.modal-content + [:& msgs/inline-banner + {:type :info + :content (tr "modals.change-email.info" (:email profile))}] + + [:div.fields-container + [:div.fields-row + [:& fm/input {:type "email" + :name :email-1 + :label (tr "modals.change-email.new-email") + :trim true + :on-change-value on-email-change}]] + [:div.fields-row + [:& fm/input {:type "email" + :name :email-2 + :label (tr "modals.change-email.confirm-email") + :trim true + :on-change-value on-email-change}]]]] + + [:div.modal-footer + [:div.action-buttons {:data-test "change-email-submit"} + [:> fm/submit-button* + {:label (tr "modals.change-email.submit")}]]]]]]) + )) diff --git a/frontend/src/app/main/ui/settings/change_email.scss b/frontend/src/app/main/ui/settings/change_email.scss new file mode 100644 index 000000000..6b6b760c9 --- /dev/null +++ b/frontend/src/app/main/ui/settings/change_email.scss @@ -0,0 +1,53 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.modal-overlay { + @extend .modal-overlay-base; + .modal-container { + @extend .modal-container-base; + min-width: $s-408; + border: $s-1 solid var(--modal-border-color); + .modal-header { + margin-bottom: $s-24; + .modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); + } + .modal-close-btn { + @extend .modal-close-btn-base; + } + } + + .modal-content { + @include flexColumn; + @include titleTipography; + gap: $s-24; + margin-bottom: $s-24; + + .fields-row { + @include flexColumn; + .select-title { + @include titleTipography; + color: var(--modal-title-foreground-color); + } + } + } + + .modal-footer { + .action-buttons { + @extend .modal-action-btns; + button { + @extend .modal-accept-btn; + } + .cancel-button { + @extend .modal-cancel-btn; + } + } + } + } +} diff --git a/frontend/src/app/main/ui/settings/delete_account.cljs b/frontend/src/app/main/ui/settings/delete_account.cljs index 9a1961f1a..5f13bcf85 100644 --- a/frontend/src/app/main/ui/settings/delete_account.cljs +++ b/frontend/src/app/main/ui/settings/delete_account.cljs @@ -5,11 +5,13 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.settings.delete-account + (:require-macros [app.main.style :as stl]) (:require [app.main.data.messages :as dm] [app.main.data.modal :as modal] [app.main.data.users :as du] [app.main.store :as st] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.ui.messages :as msgs] [app.util.i18n :as i18n :refer [tr]] @@ -27,33 +29,62 @@ {::mf/register modal/components ::mf/register-as :delete-account} [] - (let [on-close + (let [new-css-system (mf/use-ctx ctx/new-css-system) + on-close (mf/use-callback #(st/emit! (modal/hide))) on-accept (mf/use-callback #(st/emit! (modal/hide) (du/request-account-deletion - (with-meta {} {:on-error on-error}))))] + (with-meta {} {:on-error on-error}))))] - [:div.modal-overlay - [:div.modal-container.change-email-modal - [:div.modal-header - [:div.modal-header-title - [:h2 (tr "modals.delete-account.title")]] - [:div.modal-close-button - {:on-click on-close} i/close]] + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} - [:div.modal-content - [:& msgs/inline-banner - {:type :warning - :content (tr "modals.delete-account.info")}]] + [:div {:class (stl/css :modal-header)} - [:div.modal-footer - [:div.action-buttons - [:button.btn-danger.btn-large {:on-click on-accept - :data-test "delete-account-btn"} - (tr "modals.delete-account.confirm")] - [:button.btn-secondary.btn-large {:on-click on-close} - (tr "modals.delete-account.cancel")]]]]])) + [:h2 {:class (stl/css :modal-title)} (tr "modals.delete-account.title")] + [:button {:class (stl/css :modal-close-btn) + :on-click on-close} i/close-refactor]] + + [:div {:class (stl/css :modal-content)} + [:& msgs/inline-banner + {:type :warning + :content (tr "modals.delete-account.info")}]] + + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + [:button {:class (stl/css :cancel-button) + :on-click on-close} + (tr "modals.delete-account.cancel")] + [:button {:class (stl/css-case :accept-button true + :danger true) + :on-click on-accept + :data-test "delete-account-btn"} + (tr "modals.delete-account.confirm")]]]]] + + + + [:div.modal-overlay + [:div.modal-container.change-email-modal + [:div.modal-header + [:div.modal-header-title + [:h2 (tr "modals.delete-account.title")]] + [:div.modal-close-button + {:on-click on-close} i/close]] + + [:div.modal-content + [:& msgs/inline-banner + {:type :warning + :content (tr "modals.delete-account.info")}]] + + [:div.modal-footer + [:div.action-buttons + [:button.btn-danger.btn-large {:on-click on-accept + :data-test "delete-account-btn"} + (tr "modals.delete-account.confirm")] + [:button.btn-secondary.btn-large {:on-click on-close} + (tr "modals.delete-account.cancel")]]]]]))) diff --git a/frontend/src/app/main/ui/settings/delete_account.scss b/frontend/src/app/main/ui/settings/delete_account.scss new file mode 100644 index 000000000..e12ff29a8 --- /dev/null +++ b/frontend/src/app/main/ui/settings/delete_account.scss @@ -0,0 +1,57 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.modal-overlay { + @extend .modal-overlay-base; + .modal-container { + @extend .modal-container-base; + min-width: $s-408; + border: $s-1 solid var(--modal-border-color); + .modal-header { + margin-bottom: $s-24; + .modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); + } + .modal-close-btn { + @extend .modal-close-btn-base; + } + } + + .modal-content { + @include flexColumn; + @include titleTipography; + gap: $s-24; + margin-bottom: $s-24; + + .fields-row { + @include flexColumn; + .select-title { + @include titleTipography; + color: var(--modal-title-foreground-color); + } + } + } + + .modal-footer { + .action-buttons { + @extend .modal-action-btns; + + .cancel-button { + @extend .modal-cancel-btn; + } + .accept-button { + @extend .modal-accept-btn; + &.danger { + @extend .modal-danger-btn; + } + } + } + } + } +} diff --git a/frontend/src/app/main/ui/viewer/login.cljs b/frontend/src/app/main/ui/viewer/login.cljs index 7a130a7a4..46054f8f8 100644 --- a/frontend/src/app/main/ui/viewer/login.cljs +++ b/frontend/src/app/main/ui/viewer/login.cljs @@ -51,7 +51,6 @@ (set-current-section :register-validate))] (mf/with-effect [] (swap! storage assoc :redirect-url uri)) - [:div.modal-overlay [:div.modal-container.login-register [:div.title diff --git a/frontend/src/app/main/ui/workspace/left_header.scss b/frontend/src/app/main/ui/workspace/left_header.scss index 665b7c779..b89a2c0e2 100644 --- a/frontend/src/app/main/ui/workspace/left_header.scss +++ b/frontend/src/app/main/ui/workspace/left_header.scss @@ -37,7 +37,7 @@ cursor: pointer; } .file-name { - @include titleBigTipography; + @include medTitleTipography; text-transform: none; color: var(--title-foreground-color-hover); } diff --git a/frontend/src/app/main/ui/workspace/libraries.scss b/frontend/src/app/main/ui/workspace/libraries.scss index 2db3097f9..b1efd730e 100644 --- a/frontend/src/app/main/ui/workspace/libraries.scss +++ b/frontend/src/app/main/ui/workspace/libraries.scss @@ -175,7 +175,7 @@ grid-column: span 3; grid-template-columns: repeat(auto-fill, minmax($s-160, 1fr)); grid-gap: $s-24; - font-size: $fs12; + font-size: $fs-12; margin-top: $s-16; .libraries-updates-item { diff --git a/frontend/src/app/main/ui/workspace/nudge.cljs b/frontend/src/app/main/ui/workspace/nudge.cljs index 0d6e079e1..6e59f4618 100644 --- a/frontend/src/app/main/ui/workspace/nudge.cljs +++ b/frontend/src/app/main/ui/workspace/nudge.cljs @@ -5,12 +5,14 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.workspace.nudge + (:require-macros [app.main.style :as stl]) (:require [app.main.data.modal :as modal] [app.main.data.workspace :as dw] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.numeric-input :refer [numeric-input*]] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -30,7 +32,8 @@ {::mf/register modal/components ::mf/register-as :nudge-option} [] - (let [profile (mf/deref refs/profile) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + profile (mf/deref refs/profile) nudge (or (get-in profile [:props :nudge]) {:big 10 :small 1}) update-big (mf/use-fn #(st/emit! (dw/update-nudge {:big %}))) update-small (mf/use-fn #(st/emit! (dw/update-nudge {:small %}))) @@ -40,21 +43,45 @@ (->> (events/listen js/document EventType.KEYDOWN on-keydown) (partial events/unlistenByKey))) - [:div.nudge-modal-overlay - [:div.nudge-modal-container - [:div.nudge-modal-header - [:p.nudge-modal-title (tr "modals.nudge-title")] - [:button.modal-close-button {:on-click on-close} i/close]] - [:div.nudge-modal-body - [:div.input-wrapper - [:span - [:p.nudge-subtitle (tr "modals.small-nudge")] - [:> numeric-input* {:min 0.01 - :value (:small nudge) - :on-change update-small}]]] - [:div.input-wrapper - [:span - [:p.nudge-subtitle (tr "modals.big-nudge")] - [:> numeric-input* {:min 0.01 - :value (:big nudge) - :on-change update-big}]]]]]])) + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} (tr "modals.nudge-title")] + [:button {:class (stl/css :modal-close-btn) + :on-click on-close} i/close-refactor]] + [:div {:class (stl/css :modal-content)} + [:div {:class (stl/css :input-wrapper)} + [:label {:class (stl/css :modal-msg) + :for "nudge-small"} (tr "modals.small-nudge")] + [:> numeric-input* {:min 0.01 + :id "nudge-small" + :value (:small nudge) + :on-change update-small}]] + [:div {:class (stl/css :input-wrapper)} + [:label {:class (stl/css :modal-msg) + :for "nudge-big"} (tr "modals.big-nudge")] + [:> numeric-input* {:min 0.01 + :id "nudge-big" + :value (:big nudge) + :on-change update-big}]]]]] + + + [:div.nudge-modal-overlay + [:div.nudge-modal-container + [:div.nudge-modal-header + [:p.nudge-modal-title (tr "modals.nudge-title")] + [:button.modal-close-button {:on-click on-close} i/close]] + [:div.nudge-modal-body + [:div.input-wrapper + [:span + [:p.nudge-subtitle (tr "modals.small-nudge")] + [:> numeric-input* {:min 0.01 + :value (:small nudge) + :on-change update-small}]]] + [:div.input-wrapper + [:span + [:p.nudge-subtitle (tr "modals.big-nudge")] + [:> numeric-input* {:min 0.01 + :value (:big nudge) + :on-change update-big}]]]]]]))) diff --git a/frontend/src/app/main/ui/workspace/nudge.scss b/frontend/src/app/main/ui/workspace/nudge.scss new file mode 100644 index 000000000..042f64c2f --- /dev/null +++ b/frontend/src/app/main/ui/workspace/nudge.scss @@ -0,0 +1,42 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.modal-overlay { + @extend .modal-overlay-base; + .modal-container { + @extend .modal-container-base; + min-width: $s-408; + border: $s-1 solid var(--modal-border-color); + .modal-header { + margin-bottom: $s-24; + .modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); + } + .modal-close-btn { + @extend .modal-close-btn-base; + } + } + + .modal-content { + @include flexColumn; + gap: $s-24; + @include titleTipography; + margin-bottom: $s-24; + .modal-msg { + @include titleTipography; + } + .input-wrapper { + @extend .input-with-label; + label { + text-transform: none; + } + } + } + } +} diff --git a/frontend/src/app/main/ui/workspace/right_header.scss b/frontend/src/app/main/ui/workspace/right_header.scss index b16d32f45..65c28c98d 100644 --- a/frontend/src/app/main/ui/workspace/right_header.scss +++ b/frontend/src/app/main/ui/workspace/right_header.scss @@ -97,7 +97,7 @@ min-width: $s-68; padding: 0 $s-8; margin: 0; - color: var(--modal-foreground-color); + color: var(--modal-title-foreground-color); } } .reset-btn { diff --git a/frontend/src/app/main/ui/workspace/sidebar.cljs b/frontend/src/app/main/ui/workspace/sidebar.cljs index ac618f9e8..9a67ca682 100644 --- a/frontend/src/app/main/ui/workspace/sidebar.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar.cljs @@ -66,8 +66,8 @@ :id "left-sidebar-aside" :data-size size :class (stl/css-case new-css-system - :old-css/settings-bar true - :old-css/settings-bar-left true + :global/settings-bar true + :global/settings-bar-left true :left-settings-bar true :global/two-row (<= size 300) :global/three-row (and (> size 300) (<= size 400)) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/groups.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/groups.cljs index 546975055..096fa42eb 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/groups.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/groups.cljs @@ -5,7 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.workspace.sidebar.assets.groups - (:require-macros [app.main.style :refer [css]]) + (:require-macros [app.main.style :as stl]) (:require [app.common.pages.helpers :as cph] [app.common.spec :as us] @@ -47,17 +47,17 @@ on-close-menu (mf/use-fn #(swap! menu-state cmm/close-context-menu))] (if new-css-system - [:div {:class (dom/classnames (css :group-title) true) + [:div {:class (stl/css :group-title) :on-context-menu on-context-menu} [:& title-bar {:collapsable? true :collapsed? (not group-open?) :clickable-all? true :on-collapsed on-fold-group :title (mf/html [:* (when-not (empty? other-path) - [:span {:class (dom/classnames (css :pre-path) true) + [:span {:class (stl/css :pre-path) :title (when truncated path)} other-path "\u00A0\u2022\u00A0"]) - [:span {:class (dom/classnames (css :path) true) + [:span {:class (stl/css :path) :title (when truncated path)} last-path]])}] [:& cmm/assets-context-menu @@ -120,7 +120,8 @@ ::mf/register-as :name-group-dialog} [{:keys [path last-path accept] :as ctx :or {path "" last-path ""}}] - (let [initial (mf/use-memo + (let [new-css-system (mf/use-ctx ctx/new-css-system) + initial (mf/use-memo (mf/deps last-path) (constantly {:asset-name last-path})) form (fm/use-form :spec ::name-group-form @@ -141,34 +142,69 @@ (accept asset-name) (accept path asset-name)) (modal/hide!))))] + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} + (if create? + (tr "workspace.assets.create-group") + (tr "workspace.assets.rename-group"))] + [:button {:class (stl/css :modal-close-btn) + :on-click on-close} i/close-refactor]] - [:div.modal-overlay - [:div.modal-container.confirm-dialog - [:div.modal-header - [:div.modal-header-title - [:h2 (if create? - (tr "workspace.assets.create-group") - (tr "workspace.assets.rename-group"))]] - [:div.modal-close-button - {:on-click on-close} i/close]] + [:div {:class (stl/css :modal-content)} + [:& fm/form {:form form :on-submit on-accept} + [:& fm/input {:name :asset-name + :class (stl/css :input-wrapper) + :auto-focus? true + :label (tr "workspace.assets.group-name") + :hint (tr "workspace.assets.create-group-hint")}]]] - [:div.modal-content.generic-form - [:& fm/form {:form form :on-submit on-accept} - [:& fm/input {:name :asset-name - :auto-focus? true - :label (tr "workspace.assets.group-name") - :hint (tr "workspace.assets.create-group-hint")}]]] + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + [:input + {:class (stl/css :cancel-button) + :type "button" + :value (tr "labels.cancel") + :on-click on-close}] - [:div.modal-footer - [:div.action-buttons - [:input.cancel-button - {:type "button" - :value (tr "labels.cancel") - :on-click on-close}] + [:input + {:type "button" + :class (stl/css-case :accept-btn true + :global/disabled (not (:valid @form) )) + :disabled (not (:valid @form)) + :value (if create? (tr "labels.create") (tr "labels.rename")) + :on-click on-accept}]]]]] - [:input.accept-button.primary - {:type "button" - :class (when-not (:valid @form) "btn-disabled") - :disabled (not (:valid @form)) - :value (if create? (tr "labels.create") (tr "labels.rename")) - :on-click on-accept}]]]]])) + + [:div.modal-overlay + [:div.modal-container.confirm-dialog + [:div.modal-header + [:div.modal-header-title + [:h2 (if create? + (tr "workspace.assets.create-group") + (tr "workspace.assets.rename-group"))]] + [:div.modal-close-button + {:on-click on-close} i/close]] + + [:div.modal-content.generic-form + [:& fm/form {:form form :on-submit on-accept} + [:& fm/input {:name :asset-name + :auto-focus? true + :label (tr "workspace.assets.group-name") + :hint (tr "workspace.assets.create-group-hint")}]]] + + [:div.modal-footer + [:div.action-buttons + [:input.cancel-button + {:type "button" + :value (tr "labels.cancel") + :on-click on-close}] + + [:input.accept-button.primary + {:type "button" + :class (when-not (:valid @form) "btn-disabled") + :disabled (not (:valid @form)) + :value (if create? (tr "labels.create") (tr "labels.rename")) + :on-click on-accept}]]]]]))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/groups.scss b/frontend/src/app/main/ui/workspace/sidebar/assets/groups.scss index 88eb0e6cd..93235d3ba 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/groups.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/groups.scss @@ -19,3 +19,41 @@ color: var(--title-foreground-color-hover); } } + +.modal-overlay { + @extend .modal-overlay-base; + .modal-container { + @extend .modal-container-base; + .modal-header { + margin-bottom: $s-24; + .modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); + } + .modal-close-btn { + @extend .modal-close-btn-base; + } + } + .modal-content { + @include titleTipography; + margin-bottom: $s-24; + .input-wrapper { + @extend .input-with-label; + } + } + .modal-footer { + .action-buttons { + @extend .modal-action-btns; + .cancel-button { + @extend .modal-cancel-btn; + } + .accept-btn { + @extend .modal-accept-btn; + &.danger { + @extend .modal-danger-btn; + } + } + } + } + } +} diff --git a/frontend/translations/en.po b/frontend/translations/en.po index cda611ae5..8ff5e68f4 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -2247,6 +2247,12 @@ msgstr "Create team and send invites" msgid "onboarding.choice.team-up.create-team-without-inviting" msgstr "Create team without inviting" +msgid "onboarding.choice.team-up.create-team-and-invite" +msgstr "Create team & invite" + +msgid "onboarding.choice.team-up.create-team-without-invite" +msgstr "Create team" + msgid "onboarding.choice.team-up.create-team-and-send-invites-description" msgstr "You'll be able to invite later" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index c547166cb..046bb34a3 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -2300,6 +2300,12 @@ msgstr "Crear equipo y enviar invitaciones" msgid "onboarding.choice.team-up.create-team-without-inviting" msgstr "Crear equipo sin invitar" +msgid "onboarding.choice.team-up.create-team-and-invite" +msgstr "Crear equipo e invitar" + +msgid "onboarding.choice.team-up.create-team-without-invite" +msgstr "Crear equipo" + msgid "onboarding.choice.team-up.create-team-and-send-invites-description" msgstr "Podrás enviar invitaciones después"