From fe8f13ed57e8e0bc316fd8268575d92175ac8fca Mon Sep 17 00:00:00 2001 From: Eva Date: Thu, 27 Apr 2023 12:44:11 +0200 Subject: [PATCH] :sparkles: Add new palette UI --- common/src/app/common/pages/helpers.cljc | 24 +- .../images/icons/detach-refactor.svg | 3 + .../images/icons/library-refactor.svg | 2 +- .../images/icons/text-Autoheight-refactor.svg | 3 + .../images/icons/text-LTR-refactor.svg | 3 + .../images/icons/text-RTL-refactor.svg | 3 + .../images/icons/text-autowidth-refactor.svg | 3 + .../images/icons/text-bottom-refactor.svg | 3 + .../images/icons/text-fixed-refactor.svg | 3 + .../images/icons/text-justify-refactor.svg | 3 + .../icons/text-letterspacing-refactor.svg | 3 + .../images/icons/text-lineheight-refactor.svg | 3 + .../images/icons/text-lowercase-refactor.svg | 3 + .../images/icons/text-middle-refactor.svg | 3 + .../images/icons/text-mixed-refactor.svg | 3 + .../images/icons/text-stroked-refactor.svg | 3 + .../images/icons/text-top-refactor.svg | 3 + .../images/icons/text-underlined-refactor.svg | 3 + .../images/icons/text-uppercase-refactor.svg | 3 + .../images/icons/view-as-list-refactor.svg | 3 + .../styles/common/refactor/basic-rules.scss | 100 +- .../styles/common/refactor/design-tokens.scss | 69 +- .../styles/common/refactor/fonts.scss | 1 + .../styles/common/refactor/mixins.scss | 5 + .../styles/common/refactor/spacing.scss | 12 +- .../common/refactor/themes/default-theme.scss | 1 + .../styles/common/refactor/z-index.scss | 1 + frontend/src/app/main/ui.cljs | 7 +- .../ui/components/color_bullet_new.css.json | 2 +- .../main/ui/components/color_bullet_new.scss | 7 +- .../main/ui/components/context_menu_a11y.cljs | 8 +- .../ui/components/context_menu_a11y.css.json | 2 +- .../main/ui/components/context_menu_a11y.scss | 15 + .../main/ui/components/editable_label.cljs | 8 +- .../ui/components/editable_label.css.json | 1 + .../main/ui/components/editable_label.scss | 21 + .../app/main/ui/components/numeric_input.cljs | 3 +- .../app/main/ui/components/radio_buttons.cljs | 78 + .../main/ui/components/radio_buttons.css.json | 1 + .../app/main/ui/components/radio_buttons.scss | 42 + .../app/main/ui/components/search_bar.cljs | 60 + .../main/ui/components/search_bar.css.json | 1 + .../app/main/ui/components/search_bar.scss | 66 + .../src/app/main/ui/components/select.cljs | 65 +- .../app/main/ui/components/select.css.json | 1 + .../src/app/main/ui/components/select.scss | 84 + .../app/main/ui/components/tab_container.cljs | 52 +- .../main/ui/components/tab_container.css.json | 2 +- .../app/main/ui/components/tab_container.scss | 13 +- .../src/app/main/ui/components/title_bar.cljs | 42 + .../app/main/ui/components/title_bar.css.json | 1 + .../src/app/main/ui/components/title_bar.scss | 78 + .../app/main/ui/debug/components_preview.cljs | 179 +- .../main/ui/debug/components_preview.css.json | 2 +- .../app/main/ui/debug/components_preview.scss | 28 +- frontend/src/app/main/ui/icons.cljs | 18 +- frontend/src/app/main/ui/modal.cljs | 2 +- frontend/src/app/main/ui/workspace.cljs | 6 +- frontend/src/app/main/ui/workspace.css.json | 2 +- .../main/ui/workspace/color_palette.css.json | 2 +- .../workspace/color_palette_ctx_menu.css.json | 2 +- .../main/ui/workspace/context_menu.css.json | 2 +- .../app/main/ui/workspace/left_toolbar.cljs | 4 +- .../src/app/main/ui/workspace/libraries.cljs | 461 ++- .../app/main/ui/workspace/libraries.css.json | 1 + .../src/app/main/ui/workspace/libraries.scss | 139 + .../app/main/ui/workspace/palette.css.json | 2 +- .../src/app/main/ui/workspace/palette.scss | 4 +- .../src/app/main/ui/workspace/sidebar.cljs | 55 +- .../app/main/ui/workspace/sidebar.css.json | 2 +- .../src/app/main/ui/workspace/sidebar.scss | 17 +- .../app/main/ui/workspace/sidebar/assets.cljs | 2531 +---------------- .../main/ui/workspace/sidebar/assets.css.json | 1 + .../app/main/ui/workspace/sidebar/assets.scss | 119 + .../ui/workspace/sidebar/assets/colors.cljs | 621 ++++ .../workspace/sidebar/assets/colors.css.json | 1 + .../ui/workspace/sidebar/assets/colors.scss | 102 + .../ui/workspace/sidebar/assets/common.cljs | 257 ++ .../workspace/sidebar/assets/common.css.json | 1 + .../ui/workspace/sidebar/assets/common.scss | 40 + .../workspace/sidebar/assets/components.cljs | 662 +++++ .../sidebar/assets/components.css.json | 1 + .../workspace/sidebar/assets/components.scss | 249 ++ .../sidebar/assets/file_library.cljs | 420 +++ .../sidebar/assets/file_library.css.json | 1 + .../sidebar/assets/file_library.scss | 51 + .../ui/workspace/sidebar/assets/graphics.cljs | 553 ++++ .../sidebar/assets/graphics.css.json | 1 + .../ui/workspace/sidebar/assets/graphics.scss | 190 ++ .../ui/workspace/sidebar/assets/groups.cljs | 171 ++ .../workspace/sidebar/assets/groups.css.json | 1 + .../ui/workspace/sidebar/assets/groups.scss | 19 + .../sidebar/assets/typographies.cljs | 553 ++++ .../sidebar/assets/typographies.css.json | 1 + .../sidebar/assets/typographies.scss | 51 + .../workspace/sidebar/collapsable_button.cljs | 4 +- .../sidebar/collapsable_button.css.json | 2 +- .../ui/workspace/sidebar/layer_item.css.json | 2 +- .../ui/workspace/sidebar/layer_name.css.json | 2 +- .../main/ui/workspace/sidebar/layer_name.scss | 3 +- .../app/main/ui/workspace/sidebar/layers.cljs | 100 +- .../main/ui/workspace/sidebar/layers.css.json | 2 +- .../app/main/ui/workspace/sidebar/layers.scss | 109 +- .../sidebar/options/menus/typography.cljs | 785 +++-- .../sidebar/options/menus/typography.css.json | 1 + .../sidebar/options/menus/typography.scss | 304 ++ .../main/ui/workspace/sidebar/shortcuts.cljs | 38 +- .../ui/workspace/sidebar/shortcuts.css.json | 2 +- .../main/ui/workspace/sidebar/shortcuts.scss | 12 +- .../main/ui/workspace/sidebar/sitemap.cljs | 21 +- .../ui/workspace/sidebar/sitemap.css.json | 2 +- .../main/ui/workspace/sidebar/sitemap.scss | 79 +- .../main/ui/workspace/text_palette.css.json | 2 +- .../workspace/text_palette_ctx_menu.css.json | 2 +- 114 files changed, 6754 insertions(+), 3172 deletions(-) create mode 100644 frontend/resources/images/icons/detach-refactor.svg create mode 100644 frontend/resources/images/icons/text-Autoheight-refactor.svg create mode 100644 frontend/resources/images/icons/text-LTR-refactor.svg create mode 100644 frontend/resources/images/icons/text-RTL-refactor.svg create mode 100644 frontend/resources/images/icons/text-autowidth-refactor.svg create mode 100644 frontend/resources/images/icons/text-bottom-refactor.svg create mode 100644 frontend/resources/images/icons/text-fixed-refactor.svg create mode 100644 frontend/resources/images/icons/text-justify-refactor.svg create mode 100644 frontend/resources/images/icons/text-letterspacing-refactor.svg create mode 100644 frontend/resources/images/icons/text-lineheight-refactor.svg create mode 100644 frontend/resources/images/icons/text-lowercase-refactor.svg create mode 100644 frontend/resources/images/icons/text-middle-refactor.svg create mode 100644 frontend/resources/images/icons/text-mixed-refactor.svg create mode 100644 frontend/resources/images/icons/text-stroked-refactor.svg create mode 100644 frontend/resources/images/icons/text-top-refactor.svg create mode 100644 frontend/resources/images/icons/text-underlined-refactor.svg create mode 100644 frontend/resources/images/icons/text-uppercase-refactor.svg create mode 100644 frontend/resources/images/icons/view-as-list-refactor.svg create mode 100644 frontend/src/app/main/ui/components/editable_label.css.json create mode 100644 frontend/src/app/main/ui/components/editable_label.scss create mode 100644 frontend/src/app/main/ui/components/radio_buttons.cljs create mode 100644 frontend/src/app/main/ui/components/radio_buttons.css.json create mode 100644 frontend/src/app/main/ui/components/radio_buttons.scss create mode 100644 frontend/src/app/main/ui/components/search_bar.cljs create mode 100644 frontend/src/app/main/ui/components/search_bar.css.json create mode 100644 frontend/src/app/main/ui/components/search_bar.scss create mode 100644 frontend/src/app/main/ui/components/select.css.json create mode 100644 frontend/src/app/main/ui/components/select.scss create mode 100644 frontend/src/app/main/ui/components/title_bar.cljs create mode 100644 frontend/src/app/main/ui/components/title_bar.css.json create mode 100644 frontend/src/app/main/ui/components/title_bar.scss create mode 100644 frontend/src/app/main/ui/workspace/libraries.css.json create mode 100644 frontend/src/app/main/ui/workspace/libraries.scss create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets.css.json create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets.scss create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/colors.cljs create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/colors.css.json create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/colors.scss create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/common.css.json create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/common.scss create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/components.css.json create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/components.scss create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/file_library.css.json create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/file_library.scss create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/graphics.cljs create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/graphics.css.json create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/graphics.scss create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/groups.cljs create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/groups.css.json create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/groups.scss create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/typographies.cljs create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/typographies.css.json create mode 100644 frontend/src/app/main/ui/workspace/sidebar/assets/typographies.scss create mode 100644 frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.css.json create mode 100644 frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss diff --git a/common/src/app/common/pages/helpers.cljc b/common/src/app/common/pages/helpers.cljc index f9bb170b90..529d3bf2f6 100644 --- a/common/src/app/common/pages/helpers.cljc +++ b/common/src/app/common/pages/helpers.cljc @@ -448,25 +448,37 @@ path) name)) +(defn merge-path-item-with-dot + "Put the item at the end of the path." + [path name] + (if-not (empty? path) + (if-not (empty? name) + (str path "\u00A0\u2022\u00A0" name) + path) + name)) + (defn compact-path "Separate last item of the path, and truncate the others if too long: 'one' -> ['' 'one' false] 'one / two / three' -> ['one / two' 'three' false] 'one / two / three / four' -> ['one / two / ...' 'four' true] 'one-item-but-very-long / two' -> ['...' 'two' true] " - [path max-length] + [path max-length dot?] (let [path-split (split-path path) - last-item (last path-split)] + last-item (last path-split) + merge-path (if dot? + merge-path-item-with-dot + merge-path-item)] (loop [other-items (seq (butlast path-split)) other-path ""] (if-let [item (first other-items)] (let [full-path (-> other-path - (merge-path-item item) - (merge-path-item last-item))] + (merge-path item) + (merge-path last-item))] (if (> (count full-path) max-length) - [(merge-path-item other-path "...") last-item true] + [(merge-path other-path "...") last-item true] (recur (next other-items) - (merge-path-item other-path item)))) + (merge-path other-path item)))) [other-path last-item false])))) (defn compact-name diff --git a/frontend/resources/images/icons/detach-refactor.svg b/frontend/resources/images/icons/detach-refactor.svg new file mode 100644 index 0000000000..3f349eb189 --- /dev/null +++ b/frontend/resources/images/icons/detach-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/library-refactor.svg b/frontend/resources/images/icons/library-refactor.svg index a961bb3e32..ea1a7b8084 100644 --- a/frontend/resources/images/icons/library-refactor.svg +++ b/frontend/resources/images/icons/library-refactor.svg @@ -1,3 +1,3 @@ - + diff --git a/frontend/resources/images/icons/text-Autoheight-refactor.svg b/frontend/resources/images/icons/text-Autoheight-refactor.svg new file mode 100644 index 0000000000..e7fe975604 --- /dev/null +++ b/frontend/resources/images/icons/text-Autoheight-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/text-LTR-refactor.svg b/frontend/resources/images/icons/text-LTR-refactor.svg new file mode 100644 index 0000000000..8acf7188bb --- /dev/null +++ b/frontend/resources/images/icons/text-LTR-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/text-RTL-refactor.svg b/frontend/resources/images/icons/text-RTL-refactor.svg new file mode 100644 index 0000000000..217b18c121 --- /dev/null +++ b/frontend/resources/images/icons/text-RTL-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/text-autowidth-refactor.svg b/frontend/resources/images/icons/text-autowidth-refactor.svg new file mode 100644 index 0000000000..e7fe975604 --- /dev/null +++ b/frontend/resources/images/icons/text-autowidth-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/text-bottom-refactor.svg b/frontend/resources/images/icons/text-bottom-refactor.svg new file mode 100644 index 0000000000..89a0861e81 --- /dev/null +++ b/frontend/resources/images/icons/text-bottom-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/text-fixed-refactor.svg b/frontend/resources/images/icons/text-fixed-refactor.svg new file mode 100644 index 0000000000..3dc0d5fe22 --- /dev/null +++ b/frontend/resources/images/icons/text-fixed-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/text-justify-refactor.svg b/frontend/resources/images/icons/text-justify-refactor.svg new file mode 100644 index 0000000000..01b8392a82 --- /dev/null +++ b/frontend/resources/images/icons/text-justify-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/text-letterspacing-refactor.svg b/frontend/resources/images/icons/text-letterspacing-refactor.svg new file mode 100644 index 0000000000..ce8ff42bde --- /dev/null +++ b/frontend/resources/images/icons/text-letterspacing-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/text-lineheight-refactor.svg b/frontend/resources/images/icons/text-lineheight-refactor.svg new file mode 100644 index 0000000000..eebb1c05b2 --- /dev/null +++ b/frontend/resources/images/icons/text-lineheight-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/text-lowercase-refactor.svg b/frontend/resources/images/icons/text-lowercase-refactor.svg new file mode 100644 index 0000000000..e003bafa78 --- /dev/null +++ b/frontend/resources/images/icons/text-lowercase-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/text-middle-refactor.svg b/frontend/resources/images/icons/text-middle-refactor.svg new file mode 100644 index 0000000000..50bf1347d5 --- /dev/null +++ b/frontend/resources/images/icons/text-middle-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/text-mixed-refactor.svg b/frontend/resources/images/icons/text-mixed-refactor.svg new file mode 100644 index 0000000000..c2856aea76 --- /dev/null +++ b/frontend/resources/images/icons/text-mixed-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/text-stroked-refactor.svg b/frontend/resources/images/icons/text-stroked-refactor.svg new file mode 100644 index 0000000000..e63a1ae955 --- /dev/null +++ b/frontend/resources/images/icons/text-stroked-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/text-top-refactor.svg b/frontend/resources/images/icons/text-top-refactor.svg new file mode 100644 index 0000000000..ada2e6129b --- /dev/null +++ b/frontend/resources/images/icons/text-top-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/text-underlined-refactor.svg b/frontend/resources/images/icons/text-underlined-refactor.svg new file mode 100644 index 0000000000..ad91abf795 --- /dev/null +++ b/frontend/resources/images/icons/text-underlined-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/text-uppercase-refactor.svg b/frontend/resources/images/icons/text-uppercase-refactor.svg new file mode 100644 index 0000000000..01bf59429c --- /dev/null +++ b/frontend/resources/images/icons/text-uppercase-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/view-as-list-refactor.svg b/frontend/resources/images/icons/view-as-list-refactor.svg new file mode 100644 index 0000000000..edc5e0b881 --- /dev/null +++ b/frontend/resources/images/icons/view-as-list-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 e0d30f8cc6..cb4da27ec4 100644 --- a/frontend/resources/styles/common/refactor/basic-rules.scss +++ b/frontend/resources/styles/common/refactor/basic-rules.scss @@ -7,18 +7,24 @@ .button-primary { @include buttonStyle; @include flexCenter; + background-color: var(--button-primary-background-color-rest); + border: $s-1 solid var(--button-primary-border-color-rest); + color: var(--button-primary-foreground-color-rest); &:hover { - background-color: var(--button-background-hover); + background-color: var(--button-primary-background-color-hover); + border: $s-1 solid var(--button-primary-border-color-hover); + color: var(--button-primary-foreground-color-hover); svg { - stroke: var(--button-foreground-hover); + stroke: var(--button-primary-foreground-color-hover); } } &:focus { outline: none; - border: 1px solid var(--button-border-focus); - background-color: var(--button-background-focus); + background-color: var(--button-primary-background-color-focus); + border: $s-1 solid var(--button-primary-boder-color-focus); + color: var(--button-primary-foreground-color-focus); svg { - stroke: var(--button-foreground-focus); + stroke: var(--button-primary-foreground-color-focus); } } &:active { @@ -32,6 +38,73 @@ } .button-secondary { + @include buttonStyle; + @include flexCenter; + background-color: var(--button-secondary-background-color-rest); + border: $s-1 solid var(--button-secondary-border-color-rest); + color: var(--button-secondary-foreground-color-rest); + &:hover { + background-color: var(--button-secondary-background-color-hover); + border: $s-1 solid var(--button-secondary-border-color-hover); + color: var(--button-secondary-foreground-color-hover); + svg, + span svg { + stroke: var(--button-secondary-foreground-color-hover); + } + } + &:focus { + outline: none; + background-color: var(--button-secondary-background-color-focus); + border: $s-1 solid var(--button-secondary-boder-color-focus); + color: var(--button-secondary-foreground-color-focus); + svg { + stroke: var(--button-secondary-foreground-color-focus); + } + } + &:active { + border: none; + background-color: transparent; + } + &:focus-visible { + border: none; + outline: none; + } +} + +.button-tertiary { + @include buttonStyle; + @include flexCenter; + color: var(--button-tertiary-foreground-color-rest); + svg { + stroke: var(--button-tertiary-foreground-color-rest); + } + &:hover { + background-color: var(--button-tertiary-background-color-hover); + color: var(--button-tertiary-foreground-color-hover); + svg { + stroke: var(--button-tertiary-foreground-color-hover); + } + } + &:focus { + outline: none; + border: $s-1 solid var(--button-tertiary-border-color-focus); + background-color: var(--button-tertiary-background-color-focus); + color: var(--button-tertiary-foreground-color-focus); + svg { + stroke: var(--button-tertiary-foreground-color-focus); + } + } + &:active { + border: $s-1 solid var(--button-tertiary-border-color-focus); + outline: none; + } + &:focus-visible { + border: none; + outline: none; + } +} + +.button-tag { @include buttonStyle; @include flexCenter; &:hover { @@ -73,3 +146,20 @@ width: $s-12; stroke-width: 1.33px; } + +.asset-element { + @include titleTipography; + display: flex; + align-items: center; + height: $s-32; + border-radius: $br-8; + margin-bottom: $s-4; + padding: $s-8 $s-12; + background-color: var(--assets-item-background-color); + color: var(--assets-item-name-foreground-color); + &:hover, + &:focus-within { + background-color: var(--assets-item-background-color-hover); + color: var(--assets-item-name-foreground-color-hover); + } +} diff --git a/frontend/resources/styles/common/refactor/design-tokens.scss b/frontend/resources/styles/common/refactor/design-tokens.scss index 1f21439317..ccd8ea3281 100644 --- a/frontend/resources/styles/common/refactor/design-tokens.scss +++ b/frontend/resources/styles/common/refactor/design-tokens.scss @@ -8,15 +8,53 @@ .light, .default { --scrollbar-background-color: var(--color-foreground-secondary); - --button-background-active: var(--color-background-primary); + --button-background-hover: var(--color-background-quaternary); --button-foreground-hover: var(--color-accent-primary); --button-foreground-active: var(--color-foreground-primary); --button-background-focus: var(--color-background-secondary); --button-foreground-focus: var(--color-foreground-primary); --button-border-focus: var(--color-accent-primary); + --button-border: var(--color-background-tertiary); --button-foreground-color-disabled: var(--color-background-quaternary); + --button-primary-background-color-rest: var(--color-accent-primary); + --button-primary-border-color-rest: var(--color-accent-primary); + --button-primary-foreground-color-rest: var(--color-background-secondary); + --button-primary-background-color-hover: var(--color-accent-tertiary); + --button-primary-border-color-hover: var(--color-accent-tertiary); + --button-primary-foreground-color-hover: var(--color-background-secondary); + --button-primary-background-color-selected: var(--color-background-secondary); + --button-primary-border-color-selected: var(--color-background-secondary); + --button-primary-foreground-color-selected: var(--color-accent-primary); + --button-primary-background-color-focus: var(--color-background-tertiary); + --button-primary-border-color-focus: var(--color-accent-primary); + --button-primary-foreground-color-focus: var(--color-foreground-secondary); + + --button-secondary-background-color-rest: var(--color-background-tertiary); + --button-secondary-border-color-rest: var(--color-background-quaternary); + --button-secondary-foreground-color-rest: var(--color-foreground-secondary); + --button-secondary-background-color-hover: var(--color-background-quaternary); + --button-secondary-border-color-hover: var(--color-background-quaternary); + --button-secondary-foreground-color-hover: var(--color-accent-primary); + --button-secondary-background-color-selected: var(--color-background-secondary); + --button-secondary-border-color-selected: var(--color-background-quaternary); + --button-secondary-foreground-color-selected: var(--color-accent-primary); + --button-secondary-background-color-focus: var(--color-background-tertiary); + --button-secondary-border-color-focus: var(--color-accent-primary); + --button-secondary-foreground-color-focus: var(--color-foreground-secondary); + + --button-tertiary-foreground-color-rest: var(--color-foreground-secondary); + --button-tertiary-background-color-hover: var(--color-background-quaternary); + --button-tertiary-border-color-hover: var(--color-background-quaternary); + --button-tertiary-foreground-color-hover: var(--color-accent-primary); + --button-tertiary-background-color-selected: var(--color-background-secondary); + --button-tertiary-border-color-selected: var(--color-background-quaternary); + --button-tertiary-foreground-color-selected: var(--color-accent-primary); + --button-tertiary-background-color-focus: var(--color-background-tertiary); + --button-tertiary-border-color-focus: var(--color-accent-primary); + --button-tertiary-foreground-color-focus: var(--color-foreground-primary); + --icon-foreground: var(--color-foreground-secondary); --icon-foreground-hover: var(--color-foreground-primary); @@ -29,6 +67,7 @@ --title-background-color: var(--color-background-secondary); --title-foreground-color: var(--color-foreground-secondary); --title-foreground-color-hover: var(--color-foreground-primary); + --title-background-color: var(--color-background-primary); --layer-row-background-color: var(--color-background-primary); --layer-row-background-color-hover: var(--color-background-secondary); @@ -44,13 +83,17 @@ --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); + --input-background-color: var(--color-background-tertiary); --input-background-color-active: var(--color-background-primary); --input-background-color-hover: var(--color-background-quaternary); --input-background-color-focus: var(--color-background-tertiary); --input-background-color-disabled: var(--color-background-primary); --input-placeholder-color: var(--color-foreground-secondary); - --input-foreground-color: var(--color-foreground-primary); + --input-foreground-color: var(--color-foreground-secondary); --input-foreground-color-active: var(--color-foreground-primary); --input-border-color-active: var(--color-accent-primary); --input-border-outline-color-active: var(--color-accent-primary-muted); @@ -95,4 +138,26 @@ --color-bullet-background-color: var(--white); // We don't want this color to change with palette --color-bullet-border-color: var(--color-background-quaternary); --palette-handler-background-color: var(--color-background-quaternary); + + --assets-item-background-color: var(--color-background-tertiary); + --assets-item-background-color-hover: var(--color-background-quaternary); + --assets-item-name-foreground-color: var(--color-foreground-secondary); + --assets-item-name-foreground-color-hover: var(--color-foreground-primary); + --assets-item-border-color: var(--color-accent-primary); + --assets-item-background-color-drag: var(--color-accent-primary-muted); + --assets-item-border-color-drag: var(--color-accent-primary); + --assets-component-background-color: var(--white); // We don't want this color to change with palette + + --radio-btns-background-color: var(--color-background-tertiary); + --radio-btn-background-color-selected: var(--color-background-primary); + --radio-btn-foreground-color: var(--color-foreground-secondary); + --radio-btn-foreground-color-selected: var(--color-accent-primary); + --radio-btn-border-color-selected: var(--color-background-quaternary); + + --modal-background-color: var(--color-background-primary); + + --library-name-foreground-color: var(--color-foreground-primary); + --library-content-foreground-color: var(--color-foreground-secondary); + + --dropdown-background-color: var(--color-background-tertiary); } diff --git a/frontend/resources/styles/common/refactor/fonts.scss b/frontend/resources/styles/common/refactor/fonts.scss index d60499e2d4..ebc5b5e150 100644 --- a/frontend/resources/styles/common/refactor/fonts.scss +++ b/frontend/resources/styles/common/refactor/fonts.scss @@ -20,6 +20,7 @@ $fs-9: math.div(9, $fs-base) + rem; $fs-10: math.div(10, $fs-base) + rem; $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; diff --git a/frontend/resources/styles/common/refactor/mixins.scss b/frontend/resources/styles/common/refactor/mixins.scss index 9aa19c9bae..3c48959eab 100644 --- a/frontend/resources/styles/common/refactor/mixins.scss +++ b/frontend/resources/styles/common/refactor/mixins.scss @@ -15,6 +15,11 @@ background: none; cursor: pointer; } +@mixin removeInputStyle { + border: none; + background: none; + outline: none; +} @mixin tabTitleTipography { font-family: "worksans", sans-serif; diff --git a/frontend/resources/styles/common/refactor/spacing.scss b/frontend/resources/styles/common/refactor/spacing.scss index 81180d27df..bcf9ed6288 100644 --- a/frontend/resources/styles/common/refactor/spacing.scss +++ b/frontend/resources/styles/common/refactor/spacing.scss @@ -7,6 +7,7 @@ @use "sass:math"; $s-0: 0px; +$s-1: 1px; $s-2: math.div(0.25rem, 2); $s-4: var(--s-4); $s-6: calc($s-2 + $s-4); @@ -29,13 +30,22 @@ $s-68: calc(var(--s-4) * 17); $s-72: calc(var(--s-4) * 18); $s-76: calc(var(--s-4) * 19); $s-80: calc(var(--s-4) * 20); +$s-84: calc(var(--s-4) * 21); +$s-92: calc(var(--s-4) * 23); $s-96: calc(var(--s-4) * 24); $s-104: calc(var(--s-4) * 25); $s-120: calc(var(--s-4) * 30); $s-136: calc(var(--s-4) * 34); $s-152: calc(var(--s-4) * 38); $s-192: calc(var(--s-4) * 48); +$s-196: calc(var(--s-4) * 49); +$s-200: calc(var(--s-4) * 50); +$s-216: calc(var(--s-4) * 54); $s-240: calc(var(--s-4) * 60); -$s-248: calc(var(--s-4) * 61); +$s-248: calc(var(--s-4) * 62); +$s-256: calc(var(--s-4) * 64); +$s-348: calc(var(--s-4) * 87); +$s-400: calc(var(--s-4) * 100); $s-480: calc(var(--s-4) * 120); +$s-664: calc(var(--s-4) * 166); $s-736: calc(var(--s-4) * 184); diff --git a/frontend/resources/styles/common/refactor/themes/default-theme.scss b/frontend/resources/styles/common/refactor/themes/default-theme.scss index 2b6e09f98d..f116aa0933 100644 --- a/frontend/resources/styles/common/refactor/themes/default-theme.scss +++ b/frontend/resources/styles/common/refactor/themes/default-theme.scss @@ -16,4 +16,5 @@ --color-accent-primary-muted: var(--green-30); --color-accent-secondary: var(--lilac); --color-accent-tertiary: var(--strong-green); + --overlay-color: rgba(0, 0, 0, 0.4); } diff --git a/frontend/resources/styles/common/refactor/z-index.scss b/frontend/resources/styles/common/refactor/z-index.scss index 876b035d62..c159be3e9d 100644 --- a/frontend/resources/styles/common/refactor/z-index.scss +++ b/frontend/resources/styles/common/refactor/z-index.scss @@ -9,3 +9,4 @@ $z-index-2: 2; $z-index-4: 4; $z-index-10: 10; $z-index-20: 20; +$z-index-modal: 30; // When refactor finish we can reduce this number, diff --git a/frontend/src/app/main/ui.cljs b/frontend/src/app/main/ui.cljs index 7528fdcd3a..baf740baa6 100644 --- a/frontend/src/app/main/ui.cljs +++ b/frontend/src/app/main/ui.cljs @@ -7,6 +7,7 @@ (ns app.main.ui (:require [app.config :as cf] + [app.main.features :as features] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.auth :refer [auth]] @@ -37,8 +38,10 @@ (mf/defc main-page {::mf/wrap [#(mf/catch % {:fallback on-main-error})]} [{:keys [route profile]}] - (let [{:keys [data params]} route] + (let [{:keys [data params]} route + new-css-system (features/use-feature :new-css-system)] [:& (mf/provider ctx/current-route) {:value route} + [:& (mf/provider ctx/new-css-system) {:value new-css-system} (case (:name data) (:auth-login :auth-register @@ -131,7 +134,7 @@ :page-id page-id :layout-name layout :key file-id}]) - nil)])) + nil)]])) (mf/defc app [] diff --git a/frontend/src/app/main/ui/components/color_bullet_new.css.json b/frontend/src/app/main/ui/components/color_bullet_new.css.json index 04e955d8e4..c8e23cac52 100644 --- a/frontend/src/app/main/ui/components/color_bullet_new.css.json +++ b/frontend/src/app/main/ui/components/color_bullet_new.css.json @@ -1 +1 @@ -{"button-primary":"components_color_bullet_new_button-primary_pDkQg","button-secondary":"components_color_bullet_new_button-secondary_y3A8V","button-icon":"components_color_bullet_new_button-icon_uAC1e","button-icon-small":"components_color_bullet_new_button-icon-small_rz5pc","color-bullet":"components_color_bullet_new_color-bullet_b1w8U","mini":"components_color_bullet_new_mini_B261Z","is-not-library-color":"components_color_bullet_new_is-not-library-color_PSveA","color-bullet-wrapper":"components_color_bullet_new_color-bullet-wrapper_clt4r","is-gradient":"components_color_bullet_new_is-gradient_6RdV2","is-transparent":"components_color_bullet_new_is-transparent_g0iwn","color-text":"components_color_bullet_new_color-text_HM6mp","small-text":"components_color_bullet_new_small-text_Y4OeK","no-text":"components_color_bullet_new_no-text_pbTQf"} \ No newline at end of file +{"button-primary":"components_color_bullet_new_button-primary_pDkQg","button-secondary":"components_color_bullet_new_button-secondary_y3A8V","button-tertiary":"components_color_bullet_new_button-tertiary_zPQ8t","button-tag":"components_color_bullet_new_button-tag_2Ur4i","button-icon":"components_color_bullet_new_button-icon_uAC1e","button-icon-small":"components_color_bullet_new_button-icon-small_rz5pc","asset-element":"components_color_bullet_new_asset-element_s3Yqx","color-bullet":"components_color_bullet_new_color-bullet_b1w8U","mini":"components_color_bullet_new_mini_B261Z","is-not-library-color":"components_color_bullet_new_is-not-library-color_PSveA","color-bullet-wrapper":"components_color_bullet_new_color-bullet-wrapper_clt4r","is-gradient":"components_color_bullet_new_is-gradient_6RdV2","is-transparent":"components_color_bullet_new_is-transparent_g0iwn","color-text":"components_color_bullet_new_color-text_HM6mp","small-text":"components_color_bullet_new_small-text_Y4OeK","no-text":"components_color_bullet_new_no-text_pbTQf"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/components/color_bullet_new.scss b/frontend/src/app/main/ui/components/color_bullet_new.scss index 66e21258a5..ea0276cb0d 100644 --- a/frontend/src/app/main/ui/components/color_bullet_new.scss +++ b/frontend/src/app/main/ui/components/color_bullet_new.scss @@ -10,12 +10,15 @@ position: relative; display: flex; flex-direction: row; - width: var(--bullet-size); - height: var(--bullet-size); + width: var(--bullet-size, $s-24); + height: var(--bullet-size, $s-24); margin-top: $s-4; border: $s-2 solid var(--color-bullet-border-color); border-radius: $br-circle; &.mini { + width: var(--bullet-size, $s-16); + height: var(--bullet-size, $s-16); + margin-top: 0; border: 1px solid var(--color-bullet-border-color); } diff --git a/frontend/src/app/main/ui/components/context_menu_a11y.cljs b/frontend/src/app/main/ui/components/context_menu_a11y.cljs index 7bdd05ff57..8cd5536d2f 100644 --- a/frontend/src/app/main/ui/components/context_menu_a11y.cljs +++ b/frontend/src/app/main/ui/components/context_menu_a11y.cljs @@ -258,6 +258,7 @@ :key id :class (if (and new-css-system workspace?) (dom/classnames (css :is-selected) (and selected (= option-name selected)) + (css :selected) (and selected (= data-test selected)) (css :context-menu-item) true) (dom/classnames :is-selected (and selected (= option-name selected)))) :key-index (dm/str "context-item-" index) @@ -274,7 +275,11 @@ :data-test data-test} (if (and in-dashboard? (= option-name "Default")) (tr "dashboard.default-team-name") - option-name)] + option-name) + + (when (and new-css-system selected (= data-test selected)) + [:span {:class (dom/classnames (css :selected-icon) true)} + i/tick-refactor])] [:a {:class (if (and new-css-system workspace?) (dom/classnames (css :context-menu-action) true (css :submenu) true) @@ -289,7 +294,6 @@ i/arrow-refactor i/arrow-slide)]])]))))])])]))) - (mf/defc context-menu-a11y {::mf/wrap-props false} [props] diff --git a/frontend/src/app/main/ui/components/context_menu_a11y.css.json b/frontend/src/app/main/ui/components/context_menu_a11y.css.json index 480fdf318b..98dcbca412 100644 --- a/frontend/src/app/main/ui/components/context_menu_a11y.css.json +++ b/frontend/src/app/main/ui/components/context_menu_a11y.css.json @@ -1 +1 @@ -{"button-primary":"components_context_menu_a11y_button-primary_FTrG6","button-secondary":"components_context_menu_a11y_button-secondary_tIeiM","button-icon":"components_context_menu_a11y_button-icon_eOLGl","button-icon-small":"components_context_menu_a11y_button-icon-small_bQvvB","context-menu":"components_context_menu_a11y_context-menu_bS2vM","context-menu-items":"components_context_menu_a11y_context-menu-items_lQC7H","context-menu-item":"components_context_menu_a11y_context-menu-item_E2GpJ","context-menu-action":"components_context_menu_a11y_context-menu-action_E53yg","submenu-back":"components_context_menu_a11y_submenu-back_AboXg","submenu-icon-back":"components_context_menu_a11y_submenu-icon-back_gy-B6","submenu":"components_context_menu_a11y_submenu_MuyM8","submenu-icon":"components_context_menu_a11y_submenu-icon_tWTVU","is-open":"components_context_menu_a11y_is-open_FbqIp","fixed":"components_context_menu_a11y_fixed_iJxPr","separator":"components_context_menu_a11y_separator_DrZoB","min-width":"components_context_menu_a11y_min-width_w-ron","is-selected":"components_context_menu_a11y_is-selected_UPMXx","is-selectable":"components_context_menu_a11y_is-selectable_n7sdb"} \ No newline at end of file +{"button-primary":"components_context_menu_a11y_button-primary_FTrG6","button-secondary":"components_context_menu_a11y_button-secondary_tIeiM","button-tertiary":"components_context_menu_a11y_button-tertiary_0A2mW","button-tag":"components_context_menu_a11y_button-tag_iLpM-","context-menu":"components_context_menu_a11y_context-menu_bS2vM","context-menu-items":"components_context_menu_a11y_context-menu-items_lQC7H","context-menu-item":"components_context_menu_a11y_context-menu-item_E2GpJ","selected":"components_context_menu_a11y_selected_on-en","selected-icon":"components_context_menu_a11y_selected-icon_H2S7W","button-icon":"components_context_menu_a11y_button-icon_eOLGl","button-icon-small":"components_context_menu_a11y_button-icon-small_bQvvB","context-menu-action":"components_context_menu_a11y_context-menu-action_E53yg","submenu-back":"components_context_menu_a11y_submenu-back_AboXg","submenu-icon-back":"components_context_menu_a11y_submenu-icon-back_gy-B6","submenu":"components_context_menu_a11y_submenu_MuyM8","submenu-icon":"components_context_menu_a11y_submenu-icon_tWTVU","asset-element":"components_context_menu_a11y_asset-element_r3q1-","is-open":"components_context_menu_a11y_is-open_FbqIp","fixed":"components_context_menu_a11y_fixed_iJxPr","separator":"components_context_menu_a11y_separator_DrZoB","min-width":"components_context_menu_a11y_min-width_w-ron","is-selected":"components_context_menu_a11y_is-selected_UPMXx","is-selectable":"components_context_menu_a11y_is-selectable_n7sdb"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/components/context_menu_a11y.scss b/frontend/src/app/main/ui/components/context_menu_a11y.scss index f62c2f3dc5..6eb30fce94 100644 --- a/frontend/src/app/main/ui/components/context_menu_a11y.scss +++ b/frontend/src/app/main/ui/components/context_menu_a11y.scss @@ -109,6 +109,21 @@ } } } + &.selected { + .context-menu-action { + justify-content: space-between; + color: var(--menu-foreground-color-focus); + } + .selected-icon { + @extend .button-tag; + border-radius: $br-8; + height: 100%; + svg { + @extend .button-icon-small; + stroke: var(--menu-foreground-color-focus); + } + } + } } .is-selected .context-menu-action { padding-left: $s-28; diff --git a/frontend/src/app/main/ui/components/editable_label.cljs b/frontend/src/app/main/ui/components/editable_label.cljs index de24d8848c..5100482393 100644 --- a/frontend/src/app/main/ui/components/editable_label.cljs +++ b/frontend/src/app/main/ui/components/editable_label.cljs @@ -6,6 +6,7 @@ (ns app.main.ui.components.editable-label (:require + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.keyboard :as kbd] @@ -18,6 +19,7 @@ tooltip (get props :tooltip) input (mf/use-ref nil) state (mf/use-state (:editing false)) + new-css-system (mf/use-ctx ctx/new-css-system) is-editing (:editing @state) start-editing (fn [] (swap! state assoc :editing true) @@ -52,7 +54,11 @@ :default-value value :on-key-up on-key-up :on-blur cancel-editing}] - [:span.editable-label-close {:on-click cancel-editing} i/close]] + + [:span.editable-label-close {:on-click cancel-editing} + (if new-css-system + i/delete-text-refactor + i/close)]] [:span.editable-label {:class class-name :title tooltip :on-double-click on-dbl-click} display-value]))) diff --git a/frontend/src/app/main/ui/components/editable_label.css.json b/frontend/src/app/main/ui/components/editable_label.css.json new file mode 100644 index 0000000000..fce3a08610 --- /dev/null +++ b/frontend/src/app/main/ui/components/editable_label.css.json @@ -0,0 +1 @@ +{"button-primary":"components_editable_label_button-primary_fp-ma","button-secondary":"components_editable_label_button-secondary_QPaT-","button-tertiary":"components_editable_label_button-tertiary_wOORv","button-tag":"components_editable_label_button-tag_pwEqY","button-icon":"components_editable_label_button-icon_acX7H","button-icon-small":"components_editable_label_button-icon-small_tSz5O","asset-element":"components_editable_label_asset-element_Bs5bh","editable-label-input":"components_editable_label_editable-label-input_q2Puk"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/components/editable_label.scss b/frontend/src/app/main/ui/components/editable_label.scss new file mode 100644 index 0000000000..04b36e4825 --- /dev/null +++ b/frontend/src/app/main/ui/components/editable_label.scss @@ -0,0 +1,21 @@ +// 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"; + +.editable-label-input { + @include textEllipsis; + @include titleTipography; + @include removeInputStyle; + flex-grow: 1; + height: $s-28; + max-width: calc(var(--parent-size) - (var(--depth) * var(--layer-indentation-size))); + margin: 0; + padding-left: $s-6; + border-radius: $br-8; + border: $s-1 solid var(--input-border-color-focus); + color: var(--layer-row-foreground-color); +} diff --git a/frontend/src/app/main/ui/components/numeric_input.cljs b/frontend/src/app/main/ui/components/numeric_input.cljs index cdc4ad970e..93125b5e12 100644 --- a/frontend/src/app/main/ui/components/numeric_input.cljs +++ b/frontend/src/app/main/ui/components/numeric_input.cljs @@ -35,6 +35,7 @@ default-val (obj/get props "default") nillable (obj/get props "nillable") select-on-focus? (obj/get props "data-select-on-focus" true) + class (obj/get props "klass") ;; We need a ref pointing to the input dom element, but the user ;; of this component may provide one (that is forwarded here). @@ -218,7 +219,7 @@ props (-> props (obj/without ["value" "onChange" "nillable" "onFocus"]) - (obj/set! "className" "input-text") + (obj/set! "className" (or class "input-text")) (obj/set! "type" "text") (obj/set! "ref" ref) (obj/set! "defaultValue" (fmt/format-number value)) diff --git a/frontend/src/app/main/ui/components/radio_buttons.cljs b/frontend/src/app/main/ui/components/radio_buttons.cljs new file mode 100644 index 0000000000..40784e6232 --- /dev/null +++ b/frontend/src/app/main/ui/components/radio_buttons.cljs @@ -0,0 +1,78 @@ +;; 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 + +(ns app.main.ui.components.radio-buttons + (:require-macros [app.main.style :refer [css]]) + (:require + [app.common.math :as math] + [app.main.ui.formats :as fmt] + [app.util.dom :as dom] + [rumext.v2 :as mf])) + +(def ctx-radio-button (mf/create-context nil)) + +(mf/defc radio-button + {::mf/wrap-props false} + [props] + (let [ctx (mf/use-ctx ctx-radio-button) + icon (unchecked-get props "icon") + id (unchecked-get props "id") + on-change (:on-change ctx) + selected (:selected ctx) + value (unchecked-get props "value") + checked? (= selected value) + name (:name ctx)] + [:label {:for id + :class (dom/classnames (css :radio-icon) true + (css :checked) checked?)} + icon + [:input {:id id + :on-change on-change + :type "radio" + :name name + :value value + :checked checked?}]])) + +(mf/defc nilable-option + {::mf/wrap-props false} + [props] + (let [ctx (mf/use-ctx ctx-radio-button) + icon (unchecked-get props "icon") + id (unchecked-get props "id") + on-change (:on-change ctx) + selected (:selected ctx) + value (unchecked-get props "value") + checked? (= selected value) + name (:name ctx)] + [:label {:for id + :class (dom/classnames (css :radio-icon) true + (css :checked) checked?)} + icon + [:input {:id id + :on-change on-change + :type "checkbox" + :name name + :value value + :checked checked?}]])) + +(mf/defc radio-buttons + {::mf/wrap-props false} + [props] + (let [children (unchecked-get props "children") + on-change (unchecked-get props "on-change") + selected (unchecked-get props "selected") + name (unchecked-get props "name") + calculate-width (fmt/format-pixels (+ (math/pow 2 (count children)) (* 28 (count children)))) + handle-change + (mf/use-fn + (mf/deps on-change) + (fn [event] + (let [value (dom/get-target-val event)] + (on-change value event))))] + [:& (mf/provider ctx-radio-button) {:value {:selected selected :on-change handle-change :name name}} + [:div {:class (css :radio-btn-wrapper) + :style {:width calculate-width}} + children]])) diff --git a/frontend/src/app/main/ui/components/radio_buttons.css.json b/frontend/src/app/main/ui/components/radio_buttons.css.json new file mode 100644 index 0000000000..f806818d33 --- /dev/null +++ b/frontend/src/app/main/ui/components/radio_buttons.css.json @@ -0,0 +1 @@ +{"button-primary":"components_radio_buttons_button-primary_-XZNO","button-secondary":"components_radio_buttons_button-secondary_yj3Oe","button-tertiary":"components_radio_buttons_button-tertiary_s2RvI","radio-icon":"components_radio_buttons_radio-icon_1OnG1","button-tag":"components_radio_buttons_button-tag_4VTp-","button-icon":"components_radio_buttons_button-icon_jP0XC","button-icon-small":"components_radio_buttons_button-icon-small_3AO-R","asset-element":"components_radio_buttons_asset-element_l2wMX","radio-btn-wrapper":"components_radio_buttons_radio-btn-wrapper_mH6QX","checked":"components_radio_buttons_checked_sjVzy"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/components/radio_buttons.scss b/frontend/src/app/main/ui/components/radio_buttons.scss new file mode 100644 index 0000000000..6d37280b1f --- /dev/null +++ b/frontend/src/app/main/ui/components/radio_buttons.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"; + +.radio-btn-wrapper { + @include flexCenter; + border-radius: $br-8; + height: $s-32; + background-color: var(--radio-btns-background-color); +} +.radio-icon { + @extend .button-tertiary; + height: $s-28; + width: 100%; + border-radius: $s-6; + border: solid $s-2 transparent; + box-sizing: content-box; + input { + display: none; + } + svg { + @extend .button-icon; + stroke: var(--radio-btn-foreground-color); + } + &:hover { + border: solid $s-2 var(--radio-btns-background-color); + } + &.checked { + background-color: var(--radio-btn-background-color-selected); + border: $s-2 solid var(--radio-btn-border-color-selected); + svg { + stroke: var(--radio-btn-foreground-color-selected); + } + &:hover { + border: $s-2 solid var(--radio-btn-border-color-selected); + } + } +} diff --git a/frontend/src/app/main/ui/components/search_bar.cljs b/frontend/src/app/main/ui/components/search_bar.cljs new file mode 100644 index 0000000000..607a2f4604 --- /dev/null +++ b/frontend/src/app/main/ui/components/search_bar.cljs @@ -0,0 +1,60 @@ +;; 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 + +(ns app.main.ui.components.search-bar + (:require-macros [app.main.style :refer [css]]) + (:require + [app.main.ui.icons :as i] + [app.util.dom :as dom] + [app.util.keyboard :as kbd] + [rumext.v2 :as mf])) + +(mf/defc search-bar + {::mf/wrap-props false} + [props] + (let [children (unchecked-get props "children") + on-change (unchecked-get props "on-change") + value (unchecked-get props "value") + on-clear (unchecked-get props "clear-action") + placeholder (unchecked-get props "placeholder") + icon (unchecked-get props "icon") + + handle-change + (mf/use-fn + (mf/deps on-change) + (fn [event] + (let [value (dom/get-target-val event)] + (on-change value event)))) + + handle-clear + (mf/use-fn + (mf/deps on-clear on-change) + (fn [event] + (if on-clear + (on-clear event) + (on-change "" event)))) + + handle-key-down + (mf/use-fn + (fn [event] + (let [enter? (kbd/enter? event) + esc? (kbd/esc? event) + node (dom/event->target event)] + (when ^boolean enter? (dom/blur! node)) + (when ^boolean esc? (dom/blur! node)))))] + [:span {:class (dom/classnames (css :search-box) true + (css :has-children) (some? children))} + children + [:div {:class (dom/classnames (css :search-input-wrapper) true)} + icon + [:input {:on-change handle-change + :value value + :placeholder placeholder + :on-key-down handle-key-down}] + (when (not= "" value) + [:button {:class (dom/classnames (css :clear) true) + :on-click handle-clear} + i/delete-text-refactor])]])) \ No newline at end of file diff --git a/frontend/src/app/main/ui/components/search_bar.css.json b/frontend/src/app/main/ui/components/search_bar.css.json new file mode 100644 index 0000000000..e329b342a2 --- /dev/null +++ b/frontend/src/app/main/ui/components/search_bar.css.json @@ -0,0 +1 @@ +{"button-primary":"components_search_bar_button-primary_-9D1J","button-secondary":"components_search_bar_button-secondary_GbDgI","button-tertiary":"components_search_bar_button-tertiary_VTCfX","button-tag":"components_search_bar_button-tag_dKink","search-box":"components_search_bar_search-box_AFEzz","search-input-wrapper":"components_search_bar_search-input-wrapper_Djsml","clear":"components_search_bar_clear_B6lfz","button-icon":"components_search_bar_button-icon_CdwNa","button-icon-small":"components_search_bar_button-icon-small_gSOsT","asset-element":"components_search_bar_asset-element_rH-5k","has-children":"components_search_bar_has-children_u-VSq"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/components/search_bar.scss b/frontend/src/app/main/ui/components/search_bar.scss new file mode 100644 index 0000000000..671ec54b91 --- /dev/null +++ b/frontend/src/app/main/ui/components/search_bar.scss @@ -0,0 +1,66 @@ +// 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"; + +.search-box { + display: flex; + gap: $s-2; + height: $s-32; + width: 100%; + border-radius: $br-8; + background-color: var(--search-bar-background-color); + + .search-input-wrapper { + @include flexCenter; + height: $s-32; + width: 100%; + border: $s-1 solid var(--search-bar-input-border-color); + border-radius: $br-8; + background-color: var(--search-bar-input-background-color); + input { + width: 100%; + height: 100%; + margin: 0 $s-8 0 $s-4; + border: 0; + background-color: var(--input-background-color); + font-size: $fs-12; + color: var(--input-foreground-color); + &:focus { + outline: none; + } + } + &:hover { + border: $s-1 solid var(--input-background-color-hover); + background-color: var(--input-background-color-hover); + input { + background-color: var(--input-background-color-hover); + } + } + &:focus-within { + background-color: var(--input-background-color-active); + color: var(--input-foreground-color-active); + border: $s-1 solid var(--input-border-color-focus); + input { + background-color: var(--input-background-color-active); + } + } + + .clear { + @extend .button-tag; + border-radius: $br-8; + height: 100%; + svg { + @extend .button-icon-small; + color: transparent; + } + } + } + &.has-children .search-input-wrapper { + border-radius: $br-2 $br-8 $br-8 $br-2; + margin-left: 0; + } +} diff --git a/frontend/src/app/main/ui/components/select.cljs b/frontend/src/app/main/ui/components/select.cljs index cdb88fd037..e00aa2edb0 100644 --- a/frontend/src/app/main/ui/components/select.cljs +++ b/frontend/src/app/main/ui/components/select.cljs @@ -5,11 +5,13 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.components.select + (:require-macros [app.main.style :refer [css]]) (:require [app.common.data :as d] [app.common.data.macros :as dm] [app.common.uuid :as uuid] [app.main.ui.components.dropdown :refer [dropdown]] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [rumext.v2 :as mf])) @@ -22,7 +24,8 @@ (mf/defc select [{:keys [default-value options class is-open? on-change on-pointer-enter-option on-pointer-leave-option]}] - (let [label-index (mf/with-memo [options] + (let [new-css-system (mf/use-ctx ctx/new-css-system) + label-index (mf/with-memo [options] (into {} (map as-key-value) options)) state* (mf/use-state @@ -73,22 +76,46 @@ (mf/with-effect [default-value] (swap! state* assoc :current-value default-value)) + (if new-css-system + [:div {:on-click open-dropdown :class (dom/classnames (css class) true + (css :custom-select) true)} + [:span {:class (css :current-label)} current-label] + [:span {:class (css :dropdown-button)} i/arrow-refactor] + [:& dropdown {:show is-open? :on-close close-dropdown} + [:ul {:class (css :custom-select-dropdown)} + (for [[index item] (d/enumerate options)] + (if (= :separator item) + [:hr {:key (dm/str current-id "-" index)}] + (let [[value label] (as-key-value item)] + [:li + {:key (dm/str current-id "-" index) + :class (dom/classnames + (css :checked-element) true + (css :is-selected) (= value current-value)) + :data-value (pr-str value) + :on-pointer-enter highlight-item + :on-pointer-leave unhighlight-item + :on-click select-item} + [:span {:class (css :label)} label] + [:span {:class (css :check-icon)} i/tick-refactor]])))]]] + + - [:div.custom-select {:on-click open-dropdown :class class} - [:span current-label] - [:span.dropdown-button i/arrow-down] - [:& dropdown {:show is-open? :on-close close-dropdown} - [:ul.custom-select-dropdown - (for [[index item] (d/enumerate options)] - (if (= :separator item) - [:hr {:key (dm/str current-id "-" index)}] - (let [[value label] (as-key-value item)] - [:li.checked-element - {:key (dm/str current-id "-" index) - :class (when (= value current-value) "is-selected") - :data-value (pr-str value) - :on-pointer-enter highlight-item - :on-pointer-leave unhighlight-item - :on-click select-item} - [:span.check-icon i/tick] - [:span label]])))]]])) + [:div.custom-select {:on-click open-dropdown :class class} + [:span current-label] + [:span.dropdown-button i/arrow-down] + [:& dropdown {:show is-open? :on-close close-dropdown} + [:ul.custom-select-dropdown + (for [[index item] (d/enumerate options)] + (if (= :separator item) + [:hr {:key (dm/str current-id "-" index)}] + (let [[value label] (as-key-value item)] + [:li.checked-element + {:key (dm/str current-id "-" index) + :class (when (= value current-value) "is-selected") + :data-value (pr-str value) + :on-pointer-enter highlight-item + :on-pointer-leave unhighlight-item + :on-click select-item} + [:span.check-icon i/tick] + [:span label]])))]]]))) diff --git a/frontend/src/app/main/ui/components/select.css.json b/frontend/src/app/main/ui/components/select.css.json new file mode 100644 index 0000000000..f9a8982329 --- /dev/null +++ b/frontend/src/app/main/ui/components/select.css.json @@ -0,0 +1 @@ +{"button-primary":"components_select_button-primary_peYzv","button-secondary":"components_select_button-secondary_Kse6w","button-tertiary":"components_select_button-tertiary_srwoV","button-tag":"components_select_button-tag_AJXtX","button-icon":"components_select_button-icon_86LWm","custom-select":"components_select_custom-select_OM8-6","dropdown-button":"components_select_dropdown-button_IcpuR","button-icon-small":"components_select_button-icon-small_H0Bue","checked-element":"components_select_checked-element_c5-i4","check-icon":"components_select_check-icon_9x082","asset-element":"components_select_asset-element_5vxj7","current-label":"components_select_current-label_CUaQs","custom-select-dropdown":"components_select_custom-select-dropdown_2yZj9","label":"components_select_label_kTY8t","is-selected":"components_select_is-selected_nTUGr"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/components/select.scss b/frontend/src/app/main/ui/components/select.scss new file mode 100644 index 0000000000..0bc68cfa0a --- /dev/null +++ b/frontend/src/app/main/ui/components/select.scss @@ -0,0 +1,84 @@ +// 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"; + +.custom-select { + position: relative; + display: flex; + width: 100%; + padding: $s-8; + border-radius: $br-8; + cursor: pointer; + .current-label { + width: 100%; + flex-grow: 1; + } + .dropdown-button { + @include flexCenter; + svg { + @extend .button-icon; + transform: rotate(90deg); + } + } + .custom-select-dropdown { + position: absolute; + top: $s-32; + left: 0; + width: 100%; + padding: $s-2; + margin: 0; + margin-top: $s-4; + border-radius: $br-8; + z-index: $z-index-10; + overflow-y: auto; + background-color: var(--menu-background-color); + box-shadow: 0px 0px $s-12 0px var(--menu-shadow-color); + } + .checked-element { + display: flex; + align-items: center; + height: $s-32; + padding: $s-8; + border-radius: $br-6; + color: var(--menu-foreground-color); + .label { + flex-grow: 1; + width: 100%; + } + + .check-icon { + @include flexCenter; + svg { + @extend .button-icon-small; + visibility: hidden; + } + } + + &.is-selected { + .check-icon svg { + stroke: var(--menu-foreground-color); + visibility: visible; + } + } + &:hover { + background-color: var(--menu-background-color-hover); + color: var(--menu-foreground-color-hover); + .check-icon svg { + stroke: var(--menu-foreground-color-hover); + } + } + } + + &:hover { + .dropdown-button { + color: var(--menu-foreground-color-hover); + svg { + stroke: var(--menu-foreground-color-hover); + } + } + } +} diff --git a/frontend/src/app/main/ui/components/tab_container.cljs b/frontend/src/app/main/ui/components/tab_container.cljs index 5bd44abecd..ea37a89f8b 100644 --- a/frontend/src/app/main/ui/components/tab_container.cljs +++ b/frontend/src/app/main/ui/components/tab_container.cljs @@ -35,10 +35,10 @@ on-change (unchecked-get props "on-change-tab") collapsable? (unchecked-get props "collapsable?") handle-collapse (unchecked-get props "handle-collapse") + klass (unchecked-get props "klass") state (mf/use-state #(or selected (-> children first .-props .-id))) selected (or selected @state) - new-css-system (mf/use-ctx ctx/new-css-system) select-fn (mf/use-fn @@ -48,42 +48,26 @@ (reset! state id) (when (fn? on-change) (on-change id)))))] - [:div {:class (if new-css-system - (dom/classnames (css :tab-container) true) - (dom/classnames :tab-container true))} - [:div {:class (if new-css-system - (dom/classnames (css :tab-container-tabs) true) - (dom/classnames :tab-container-tabs true))} - (when (and new-css-system collapsable?) + [:div {:class (dom/classnames (css :tab-container) true)} + [:div {:class (dom/classnames (css :tab-container-tabs) true + klass true)} + (when collapsable? [:button {:on-click handle-collapse :class (dom/classnames (css :collapse-sidebar) true) :aria-label (tr "workspace.sidebar.collapse")} i/arrow-refactor]) - (if new-css-system - [:div {:class (dom/classnames (css :tab-container-tab-wrapper) new-css-system)} - (for [tab children] - (let [props (.-props tab) - id (.-id props) - title (.-title props)] - [:div - {:key (str/concat "tab-" (d/name id)) - :data-id (pr-str id) - :on-click select-fn - :class (dom/classnames (css :tab-container-tab-title) true - (css :current) (= selected id))} - title]))] - (for [tab children] - (let [props (.-props tab) - id (.-id props) - title (.-title props)] - [:div.tab-container-tab-title - {:key (str/concat "tab-" (d/name id)) - :data-id (pr-str id) - :on-click select-fn - :class (when (= selected id) "current")} - title])))] - [:div {:class (if new-css-system - (dom/classnames (css :tab-container-content) true) - (dom/classnames :tab-container-content true))} + [:div {:class (dom/classnames (css :tab-container-tab-wrapper) true)} + (for [tab children] + (let [props (.-props tab) + id (.-id props) + title (.-title props)] + [:div + {:key (str/concat "tab-" (d/name id)) + :data-id (pr-str id) + :on-click select-fn + :class (dom/classnames (css :tab-container-tab-title) true + (css :current) (= selected id))} + title]))]] + [:div {:class (dom/classnames (css :tab-container-content) true)} (d/seek #(= selected (-> % .-props .-id)) children)]])) diff --git a/frontend/src/app/main/ui/components/tab_container.css.json b/frontend/src/app/main/ui/components/tab_container.css.json index 6a5fd88cd8..9d8e6e6bfd 100644 --- a/frontend/src/app/main/ui/components/tab_container.css.json +++ b/frontend/src/app/main/ui/components/tab_container.css.json @@ -1 +1 @@ -{"button-primary":"components_tab_container_button-primary_ibiAz","button-secondary":"components_tab_container_button-secondary_wZR80","button-icon":"components_tab_container_button-icon_2NhVr","button-icon-small":"components_tab_container_button-icon-small_yU7na","tab-container":"components_tab_container_tab-container_P6HRr","tab-container-content":"components_tab_container_tab-container-content_yfM9F","tab-element":"components_tab_container_tab-element_gBIwV","tab-container-tabs":"components_tab_container_tab-container-tabs_6gXOY","tab-container-tab-wrapper":"components_tab_container_tab-container-tab-wrapper_-ngrN","tab-container-tab-title":"components_tab_container_tab-container-tab-title_IN1Dx","current":"components_tab_container_current_jrovp","collapse-sidebar":"components_tab_container_collapse-sidebar_e5hFv","collapsed":"components_tab_container_collapsed_lfkjK"} \ No newline at end of file +{"button-primary":"components_tab_container_button-primary_ibiAz","button-secondary":"components_tab_container_button-secondary_wZR80","button-tertiary":"components_tab_container_button-tertiary_JHJAx","button-tag":"components_tab_container_button-tag_NnL8y","button-icon":"components_tab_container_button-icon_2NhVr","button-icon-small":"components_tab_container_button-icon-small_yU7na","asset-element":"components_tab_container_asset-element_1-YWa","tab-container":"components_tab_container_tab-container_P6HRr","tab-container-content":"components_tab_container_tab-container-content_yfM9F","tab-element":"components_tab_container_tab-element_gBIwV","tab-container-tabs":"components_tab_container_tab-container-tabs_6gXOY","tab-container-tab-wrapper":"components_tab_container_tab-container-tab-wrapper_-ngrN","tab-container-tab-title":"components_tab_container_tab-container-tab-title_IN1Dx","current":"components_tab_container_current_jrovp","collapse-sidebar":"components_tab_container_collapse-sidebar_e5hFv","collapsed":"components_tab_container_collapsed_lfkjK"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/components/tab_container.scss b/frontend/src/app/main/ui/components/tab_container.scss index 23e941e3a2..d5263e34f6 100644 --- a/frontend/src/app/main/ui/components/tab_container.scss +++ b/frontend/src/app/main/ui/components/tab_container.scss @@ -26,8 +26,7 @@ flex-direction: row; gap: $s-2; height: $s-32; - margin: $s-4 $s-4 0 $s-4; - padding: $s-2 $s-2 $s-2 0; + padding: $s-2 $s-2 $s-2 $s-2; border-radius: $br-8; background: var(--color-background-secondary); cursor: pointer; @@ -43,10 +42,12 @@ @include tabTitleTipography; height: $s-28; width: 100%; + padding: 0 $s-8; margin: 0; border-radius: $br-5; background-color: transparent; color: var(--tab-foreground-color); + white-space: nowrap; &.current, &.current:hover { @@ -63,12 +64,13 @@ @include buttonStyle; height: 100%; width: $s-24; - padding: 0; + min-width: $s-24; + padding: 0 $s-6 0 0; border-radius: $br-5; svg { @include flexCenter; - height: 12px; - width: 16px; + height: $s-16; + width: $s-16; stroke: var(--icon-foreground); transform: rotate(180deg); fill: none; @@ -83,6 +85,7 @@ &.collapsed { svg { transform: rotate(0deg); + padding: 0 0 0 $s-6; } } } diff --git a/frontend/src/app/main/ui/components/title_bar.cljs b/frontend/src/app/main/ui/components/title_bar.cljs new file mode 100644 index 0000000000..303b80e5a5 --- /dev/null +++ b/frontend/src/app/main/ui/components/title_bar.cljs @@ -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 + +(ns app.main.ui.components.title-bar + (:require-macros [app.main.style :refer [css]]) + (:require + [app.main.ui.icons :as i] + [app.util.dom :as dom] + [rumext.v2 :as mf])) + +(mf/defc title-bar + {::mf/wrap-props false} + [props] + (let [collapsable? (unchecked-get props "collapsable?") + collapsed? (unchecked-get props "collapsed?") + on-collapsed (unchecked-get props "on-collapsed") + title (unchecked-get props "title") + children (unchecked-get props "children") + on-btn-click (unchecked-get props "on-btn-click") + btn-children (unchecked-get props "btn-children") + klass (unchecked-get props "klass")] + + [:div {:class (dom/classnames (css :title-bar) true + klass true)} + (if collapsable? + [:button {:class (dom/classnames (css :toggle-btn) true) + :on-click on-collapsed} + [:span {:class (dom/classnames (css :collased-icon) true + (css :rotated) collapsed?)} + i/arrow-refactor] + [:div {:class (dom/classnames (css :title) true)} + title]] + [:div {:class (dom/classnames (css :title-only) true)} + title]) + children + (when (some? on-btn-click) + [:button {:class (dom/classnames (css :title-button) true) + :on-click on-btn-click} + btn-children])])) \ No newline at end of file diff --git a/frontend/src/app/main/ui/components/title_bar.css.json b/frontend/src/app/main/ui/components/title_bar.css.json new file mode 100644 index 0000000000..57d0368568 --- /dev/null +++ b/frontend/src/app/main/ui/components/title_bar.css.json @@ -0,0 +1 @@ +{"button-primary":"components_title_bar_button-primary_svLtU","button-secondary":"components_title_bar_button-secondary_JA5NP","button-tertiary":"components_title_bar_button-tertiary_yqQfO","title-bar":"components_title_bar_title-bar_oUkS0","title-button":"components_title_bar_title-button_xTE-7","button-tag":"components_title_bar_button-tag_o9yFT","button-icon":"components_title_bar_button-icon_ROHrz","button-icon-small":"components_title_bar_button-icon-small_WibJp","toggle-btn":"components_title_bar_toggle-btn_9ekUv","collased-icon":"components_title_bar_collased-icon_SJ1ls","asset-element":"components_title_bar_asset-element_64u6f","title":"components_title_bar_title_qPuju","title-only":"components_title_bar_title-only_aSsdC","rotated":"components_title_bar_rotated_9z7Rn"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/components/title_bar.scss b/frontend/src/app/main/ui/components/title_bar.scss new file mode 100644 index 0000000000..9f4a9549c4 --- /dev/null +++ b/frontend/src/app/main/ui/components/title_bar.scss @@ -0,0 +1,78 @@ +// 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"; + +.title-bar { + display: flex; + align-items: center; + justify-content: space-between; + height: $s-32; + width: 100%; + min-height: $s-32; + background-color: var(--title-background-color); + .title, + .title-only { + @include tabTitleTipography; + display: flex; + align-items: center; + flex-grow: 1; + height: 100%; + min-height: $s-32; + margin-right: $s-8; + color: var(--title-foreground-color); + } + .title-only { + margin-left: $s-8; + } + + .toggle-btn { + @include buttonStyle; + display: flex; + align-items: center; + flex-grow: 1; + gap: $s-4; + padding: 0; + color: var(--title-foreground-color); + stroke: var(--title-foreground-color); + .collased-icon { + @include flexCenter; + height: $s-24; + width: $s-24; + border-radius: $br-8; + padding: 0 $s-4 0 $s-8; + svg { + @extend .button-icon-small; + transform: rotate(90deg); + } + &.rotated svg { + transform: rotate(0deg); + } + } + &:hover { + color: var(--title-foreground-color-hover); + stroke: var(--title-foreground-color-hover); + .title { + color: var(--title-foreground-color-hover); + stroke: var(--title-foreground-color-hover); + } + .collased-icon svg { + stroke: var(--title-foreground-color-hover); + } + } + } + + .title-button { + @extend .button-tertiary; + height: $s-32; + width: calc($s-24 + $s-4); + padding: 0; + border-radius: $br-8; + svg { + @extend .button-icon; + } + } +} diff --git a/frontend/src/app/main/ui/debug/components_preview.cljs b/frontend/src/app/main/ui/debug/components_preview.cljs index 2b950df042..4282735569 100644 --- a/frontend/src/app/main/ui/debug/components_preview.cljs +++ b/frontend/src/app/main/ui/debug/components_preview.cljs @@ -10,9 +10,23 @@ [app.main.data.users :as du] [app.main.refs :as refs] [app.main.store :as st] + [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] + [app.main.ui.components.search-bar :refer [search-bar]] + [app.main.ui.components.tab-container :refer [tab-container tab-element]] + [app.main.ui.components.title-bar :refer [title-bar]] + [app.main.ui.icons :as i] [app.util.dom :as dom] [rumext.v2 :as mf])) +(mf/defc component-wrapper + {::mf/wrap-props false} + [props] + (let [children (unchecked-get props "children") + title (unchecked-get props "title")] + [:div {:class (dom/classnames (css :component) true)} + [:h4 {:class (dom/classnames (css :component-name) true)} title] + children])) + (mf/defc components-preview {::mf/wrap-props false} [] @@ -24,7 +38,6 @@ (let [theme (dom/event->value event) data (assoc initial :theme theme)] (st/emit! (du/update-profile data)))) - colors [:bg-primary :bg-secondary :bg-tertiary @@ -34,10 +47,37 @@ :acc :acc-muted :acc-secondary - :acc-tertiary]] + :acc-tertiary] + + ;; COMPONENTS FNs + state* (mf/use-state {:collapsed? true + :tab-selected :first + :input-value "" + :radio-selected "first"}) + state (deref state*) + + collapsed? (:collapsed? state) + toggle-collapsed + (mf/use-fn #(swap! state* update :collapsed? not)) + + tab-selected (:tab-selected state) + set-tab (mf/use-fn #(swap! state* assoc :tab-selected %)) + + input-value (:input-value state) + radio-selected (:radio-selected state) + + set-radio-selected (mf/use-fn #(swap! state* assoc :radio-selected %)) + + update-search + (mf/use-fn + (fn [value _event] + (swap! state* assoc :input-value value))) + + + on-btn-click (mf/use-fn #(prn "eyy"))] [:section.debug-components-preview - [:div {:class (css :themes-row)} + [:div {:class (dom/classnames (css :themes-row) true)} [:h2 "Themes"] [:select {:label "Select theme color" :name :theme @@ -46,12 +86,135 @@ :on-change on-change} [:option {:label "Penpot Dark (default)" :value "default"}] [:option {:label "Penpot Light" :value "light"}]] - [:div {:class (css :wrapper)} + [:div {:class (dom/classnames (css :wrapper) true)} (let [css (styles)] (for [color colors] - [:div {:class (dom/classnames (get css color) true + [:div {:key color + :class (dom/classnames (get css color) true (get css :rect) true)} (d/name color)]))]] - [:div {:class (css :components-row)} - [:h2 {:class (css :title)} "Components"] - [:div {:class (css :component-wrapper)}]]])) \ No newline at end of file + [:div {:class (dom/classnames (css :components-row) true)} + [:h2 {:class (dom/classnames (css :title) true)} "Components"] + [:div {:class (dom/classnames (css :components-wrapper) true)} + [:div {:class (dom/classnames (css :component-group) true)} + [:h3 "Titles"] + [:& component-wrapper + {:title "Title"} + [:& title-bar {:collapsable? false + :title "Title"}]] + [:& component-wrapper + {:title "Title and action button"} + [:& title-bar {:collapsable? false + :title "Title" + :on-btn-click on-btn-click + :btn-children i/add-refactor}]] + [:& component-wrapper + {:title "Collapsed title and action button"} + [:& title-bar {:collapsable? true + :collapsed? collapsed? + :on-collapsed toggle-collapsed + :title "Title" + :on-btn-click on-btn-click + :btn-children i/add-refactor}]] + [:& component-wrapper + {:title "Collapsed title and children"} + [:& title-bar {:collapsable? true + :collapsed? collapsed? + :on-collapsed toggle-collapsed + :title "Title"} + [:& tab-container {:on-change-tab set-tab + :selected tab-selected} + [:& tab-element {:id :first + :title "A tab"}] + [:& tab-element {:id :second + :title "B tab"}]]]]] + + [:div {:class (dom/classnames (css :component-group) true)} + [:h3 "Tabs component"] + [:& component-wrapper + {:title "2 tab component"} + [:& tab-container {:on-change-tab set-tab + :selected tab-selected} + [:& tab-element {:id :first :title "First tab"} + [:div "This is first tab content"]] + + [:& tab-element {:id :second :title "Second tab"} + [:div "This is second tab content"]]]] + [:& component-wrapper + {:title "3 tab component"} + [:& tab-container {:on-change-tab set-tab + :selected tab-selected} + [:& tab-element {:id :first :title "First tab"} + [:div "This is first tab content"]] + + [:& tab-element {:id :second + :title "Second tab"} + [:div "This is second tab content"]] + [:& tab-element {:id :third + :title "Third tab"} + [:div "This is third tab content"]]]]] + + [:div {:class (dom/classnames (css :component-group) true)} + [:h3 "Search bar"] + [:& component-wrapper + {:title "Search bar only"} + [:& search-bar {:on-change update-search + :value input-value + :placeholder "Test value"}]] + [:& component-wrapper + {:title "Search and button"} + [:& search-bar {:on-change update-search + :value input-value + :placeholder "Test value"} + [:button {:class (dom/classnames (css :test-button) true) + :on-click on-btn-click} + "X"]]]] + + [:div {:class (dom/classnames (css :component-group) true)} + [:h3 "Radio buttons"] + [:& component-wrapper + {:title "Two radio buttons (toggle)"} + [:& radio-buttons {:selected radio-selected + :on-change set-radio-selected + :name "listing-style"} + [:& radio-button {:icon (mf/html i/view-as-list-refactor) + :value "first" + :id :list}] + [:& radio-button {:icon (mf/html i/flex-grid-refactor) + :value "second" + :id :grid}]]] + [:& component-wrapper + {:title "Three radio buttons"} + [:& radio-buttons {:selected radio-selected + :on-change set-radio-selected + :name "listing-style"} + [:& radio-button {:icon (mf/html i/view-as-list-refactor) + :value "first" + :id :first}] + [:& radio-button {:icon (mf/html i/flex-grid-refactor) + :value "second" + :id :second}] + + [:& radio-button {:icon (mf/html i/add-refactor) + :value "third" + :id :third}]]] + + [:& component-wrapper + {:title "Four radio buttons"} + [:& radio-buttons {:selected radio-selected + :on-change set-radio-selected + :name "listing-style"} + [:& radio-button {:icon (mf/html i/view-as-list-refactor) + :value "first" + :id :first}] + [:& radio-button {:icon (mf/html i/flex-grid-refactor) + :value "second" + :id :second}] + + [:& radio-button {:icon (mf/html i/add-refactor) + :value "third" + :id :third}] + + [:& radio-button {:icon (mf/html i/board-refactor) + :value "forth" + :id :forth}]]]]]]])) diff --git a/frontend/src/app/main/ui/debug/components_preview.css.json b/frontend/src/app/main/ui/debug/components_preview.css.json index 9b9b623867..ab7afd1ed4 100644 --- a/frontend/src/app/main/ui/debug/components_preview.css.json +++ b/frontend/src/app/main/ui/debug/components_preview.css.json @@ -1 +1 @@ -{"button-primary":"debug_components_preview_button-primary_Q2m40","button-secondary":"debug_components_preview_button-secondary_yPp3n","button-icon":"debug_components_preview_button-icon_J36A6","button-icon-small":"debug_components_preview_button-icon-small_Pf3jb","themes-row":"debug_components_preview_themes-row_wEU8d","wrapper":"debug_components_preview_wrapper_535-4","rect":"debug_components_preview_rect_jomnq","bg-primary":"debug_components_preview_bg-primary_Rt4oW","bg-secondary":"debug_components_preview_bg-secondary_rcmll","bg-tertiary":"debug_components_preview_bg-tertiary_7rITE","bg-cuaternary":"debug_components_preview_bg-cuaternary_UEBPN","fg-primary":"debug_components_preview_fg-primary_naliT","fg-secondary":"debug_components_preview_fg-secondary_zT9IX","acc":"debug_components_preview_acc_h3Bia","acc-muted":"debug_components_preview_acc-muted_uingh","acc-secondary":"debug_components_preview_acc-secondary_oHH6y","acc-tertiary":"debug_components_preview_acc-tertiary_SwBjy","components-row":"debug_components_preview_components-row_N3f-J","title":"debug_components_preview_title_TVtzz","component-wrapper":"debug_components_preview_component-wrapper_yC9G1"} \ No newline at end of file +{"button-primary":"debug_components_preview_button-primary_Q2m40","button-secondary":"debug_components_preview_button-secondary_yPp3n","button-tertiary":"debug_components_preview_button-tertiary_FIKgJ","button-tag":"debug_components_preview_button-tag_NNepE","button-icon":"debug_components_preview_button-icon_J36A6","button-icon-small":"debug_components_preview_button-icon-small_Pf3jb","asset-element":"debug_components_preview_asset-element_LhcNS","themes-row":"debug_components_preview_themes-row_wEU8d","wrapper":"debug_components_preview_wrapper_535-4","rect":"debug_components_preview_rect_jomnq","bg-primary":"debug_components_preview_bg-primary_Rt4oW","bg-secondary":"debug_components_preview_bg-secondary_rcmll","bg-tertiary":"debug_components_preview_bg-tertiary_7rITE","bg-cuaternary":"debug_components_preview_bg-cuaternary_UEBPN","fg-primary":"debug_components_preview_fg-primary_naliT","fg-secondary":"debug_components_preview_fg-secondary_zT9IX","acc":"debug_components_preview_acc_h3Bia","acc-muted":"debug_components_preview_acc-muted_uingh","acc-secondary":"debug_components_preview_acc-secondary_oHH6y","acc-tertiary":"debug_components_preview_acc-tertiary_SwBjy","components-row":"debug_components_preview_components-row_N3f-J","title":"debug_components_preview_title_TVtzz","components-wrapper":"debug_components_preview_components-wrapper_A8IgV","component-group":"debug_components_preview_component-group_dI55k","component":"debug_components_preview_component_xBMSU","component-name":"debug_components_preview_component-name_3ZJMW"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/debug/components_preview.scss b/frontend/src/app/main/ui/debug/components_preview.scss index 4d1cf79e33..3e29500abb 100644 --- a/frontend/src/app/main/ui/debug/components_preview.scss +++ b/frontend/src/app/main/ui/debug/components_preview.scss @@ -79,7 +79,33 @@ .title { padding: $s-20; } - .component-wrapper { + .components-wrapper { padding: $s-20; + display: flex; + flex-wrap: wrap; + .component-group { + @include flexCenter; + justify-content: flex-start; + flex-direction: column; + // width: $s-256; + border-radius: $s-8; + h3 { + @include titleTipography; + font-size: $fs-25; + width: 100%; + } + .component { + display: flex; + flex-direction: column; + gap: $s-8; + width: $s-240; + max-height: $s-80; + margin-bottom: $s-16; + .component-name { + @include tabTitleTipography; + font-weight: bold; + } + } + } } } diff --git a/frontend/src/app/main/ui/icons.cljs b/frontend/src/app/main/ui/icons.cljs index 7e338c7f5c..f62b8662e1 100644 --- a/frontend/src/app/main/ui/icons.cljs +++ b/frontend/src/app/main/ui/icons.cljs @@ -297,6 +297,7 @@ (def distribute-vertical-sapcing-refactor (icon-xref :distribute-vertical-spacing-refactor)) (def delete-refactor (icon-xref :delete-refactor)) (def delete-text-refactor (icon-xref :delete-text-refactor)) +(def detach-refactor (icon-xref :detach-refactor)) (def document-refactor (icon-xref :document-refactor)) (def drop-refactor (icon-xref :drop-refactor)) (def effects-refactor (icon-xref :effects-refactor)) @@ -365,9 +366,23 @@ (def text-align-right-refactor (icon-xref :text-align-right-refactor)) (def text-auto-height-refactor (icon-xref :text-auto-height-refactor)) (def text-auto-width-refactor (icon-xref :text-auto-width-refactor)) + +(def text-fixed-refactor (icon-xref :textfixed--refactor)) +(def text-justify-refactor (icon-xref :text-justify-refactor)) +(def text-letterspacing-refactor (icon-xref :text-letterspacing-refactor)) +(def text-lineheight-refactor (icon-xref :text-lineheight-refactor)) +(def text-lowercase-refactor (icon-xref :text-lowercase-refactor)) +(def text-LTR-refactor (icon-xref :text-LTR-refactor)) +(def text-middle-refactor (icon-xref :text-middle-refactor)) +(def text-mixed-refactor (icon-xref :text-mixed-refactor)) +(def text-palette-refactor (icon-xref :text-palette-refactor)) (def text-paragraph-refactor (icon-xref :text-paragraph-refactor)) (def text-refactor (icon-xref :text-refactor)) -(def text-palette-refactor (icon-xref :text-palette-refactor)) +(def text-RTL-refactor (icon-xref :text-RTL-refactor)) +(def text-stroked-refactor (icon-xref :text-stroked-refactor)) +(def text-top-refactor (icon-xref :text-top-refactor)) +(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 unlock-refactor (icon-xref :unlock-refactor)) (def vertical-align-items-center-refactor (icon-xref :vertical-align-items-center-refactor)) @@ -375,6 +390,7 @@ (def vertical-align-items-start-refactor (icon-xref :vertical-align-items-start-refactor)) (def view-as-icons-refactor (icon-xref :view-as-icons-refactor)) (def wrap-refactor (icon-xref :wrap-refactor)) +(def view-as-list-refactor (icon-xref :view-as-list-refactor)) (def loader-pencil (mf/html [:svg diff --git a/frontend/src/app/main/ui/modal.cljs b/frontend/src/app/main/ui/modal.cljs index 94831f9517..a27a8308c9 100644 --- a/frontend/src/app/main/ui/modal.cljs +++ b/frontend/src/app/main/ui/modal.cljs @@ -66,7 +66,7 @@ (events/listen js/document EventType.KEYDOWN handle-keydown) ;; Changing to js/document breaks the color picker - (events/listen (dom/get-root) EventType.POINTERDOWN handle-click-outside) + (events/listen (dom/get-root) EventType.POINTERDOWN handle-click-outside) (events/listen js/document EventType.CONTEXTMENU handle-click-outside)]] #(doseq [key keys] diff --git a/frontend/src/app/main/ui/workspace.cljs b/frontend/src/app/main/ui/workspace.cljs index 090fd7f75c..85c32b1bfc 100644 --- a/frontend/src/app/main/ui/workspace.cljs +++ b/frontend/src/app/main/ui/workspace.cljs @@ -169,7 +169,7 @@ file-ready? (mf/deref file-ready*) components-v2? (features/use-feature :components-v2) - new-css? (features/use-feature :new-css-system) + new-css-system (features/use-feature :new-css-system) background-color (:background-color wglobal)] @@ -192,9 +192,9 @@ [:& (mf/provider ctx/current-team-id) {:value team-id} [:& (mf/provider ctx/current-page-id) {:value page-id} [:& (mf/provider ctx/components-v2) {:value components-v2?} - [:& (mf/provider ctx/new-css-system) {:value new-css?} + [:& (mf/provider ctx/new-css-system) {:value new-css-system} [:& (mf/provider ctx/workspace-read-only?) {:value read-only?} - [:section#workspace {:class (when new-css? (css :workspace)) + [:section#workspace {:class (when new-css-system (css :workspace)) :style {:background-color background-color :touch-action "none"}} (when (not (:hide-ui layout)) diff --git a/frontend/src/app/main/ui/workspace.css.json b/frontend/src/app/main/ui/workspace.css.json index f37e8f34fe..9b6145dbcb 100644 --- a/frontend/src/app/main/ui/workspace.css.json +++ b/frontend/src/app/main/ui/workspace.css.json @@ -1 +1 @@ -{"button-primary":"ui_workspace_button-primary_FZJ-T","button-secondary":"ui_workspace_button-secondary_oDzCJ","button-icon":"ui_workspace_button-icon_L5y8h","button-icon-small":"ui_workspace_button-icon-small_Ppp3W","workspace":"ui_workspace_workspace_xutJr"} \ No newline at end of file +{"button-primary":"ui_workspace_button-primary_FZJ-T","button-secondary":"ui_workspace_button-secondary_oDzCJ","button-tertiary":"ui_workspace_button-tertiary_LVpr3","button-tag":"ui_workspace_button-tag_cU1Th","button-icon":"ui_workspace_button-icon_L5y8h","button-icon-small":"ui_workspace_button-icon-small_Ppp3W","asset-element":"ui_workspace_asset-element_LTbhl","workspace":"ui_workspace_workspace_xutJr"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/color_palette.css.json b/frontend/src/app/main/ui/workspace/color_palette.css.json index f8fedfc2bf..5e8a9eb6ff 100644 --- a/frontend/src/app/main/ui/workspace/color_palette.css.json +++ b/frontend/src/app/main/ui/workspace/color_palette.css.json @@ -1 +1 @@ -{"button-primary":"workspace_color_palette_button-primary_0d2e2","button-secondary":"workspace_color_palette_button-secondary_C8qJL","button-icon":"workspace_color_palette_button-icon_-tBR6","color-palette":"workspace_color_palette_color-palette_hfJPA","left-arrow":"workspace_color_palette_left-arrow_PK7sj","right-arrow":"workspace_color_palette_right-arrow_swpS9","button-icon-small":"workspace_color_palette_button-icon-small_RrGTg","disabled":"workspace_color_palette_disabled_bz-he","color-palette-content":"workspace_color_palette_color-palette-content_okg18","color-palette-inside":"workspace_color_palette_color-palette-inside_dCIeR","color-cell":"workspace_color_palette_color-cell_ITDgl","is-not-library-color":"workspace_color_palette_is-not-library-color_EqCM6","no-text":"workspace_color_palette_no-text_QMPK0"} \ No newline at end of file +{"button-primary":"workspace_color_palette_button-primary_0d2e2","button-secondary":"workspace_color_palette_button-secondary_C8qJL","button-tertiary":"workspace_color_palette_button-tertiary_X6-9C","button-tag":"workspace_color_palette_button-tag_GtZK2","button-icon":"workspace_color_palette_button-icon_-tBR6","color-palette":"workspace_color_palette_color-palette_hfJPA","left-arrow":"workspace_color_palette_left-arrow_PK7sj","right-arrow":"workspace_color_palette_right-arrow_swpS9","button-icon-small":"workspace_color_palette_button-icon-small_RrGTg","asset-element":"workspace_color_palette_asset-element_3Q2Mp","disabled":"workspace_color_palette_disabled_bz-he","color-palette-content":"workspace_color_palette_color-palette-content_okg18","color-palette-inside":"workspace_color_palette_color-palette-inside_dCIeR","color-cell":"workspace_color_palette_color-cell_ITDgl","is-not-library-color":"workspace_color_palette_is-not-library-color_EqCM6","no-text":"workspace_color_palette_no-text_QMPK0"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/color_palette_ctx_menu.css.json b/frontend/src/app/main/ui/workspace/color_palette_ctx_menu.css.json index a3d1913bbc..0821a3a8c5 100644 --- a/frontend/src/app/main/ui/workspace/color_palette_ctx_menu.css.json +++ b/frontend/src/app/main/ui/workspace/color_palette_ctx_menu.css.json @@ -1 +1 @@ -{"button-primary":"workspace_color_palette_ctx_menu_button-primary_2ka4z","button-secondary":"workspace_color_palette_ctx_menu_button-secondary_jfajf","button-icon":"workspace_color_palette_ctx_menu_button-icon_cCaY2","button-icon-small":"workspace_color_palette_ctx_menu_button-icon-small_-knT4","palette-menu":"workspace_color_palette_ctx_menu_palette-menu_Vrjfy","palette-library":"workspace_color_palette_ctx_menu_palette-library_0LFV5","selected":"workspace_color_palette_ctx_menu_selected_lfchf","icon-wrapper":"workspace_color_palette_ctx_menu_icon-wrapper_v8-ys","recent-colors":"workspace_color_palette_ctx_menu_recent-colors_Q4fss","file-library":"workspace_color_palette_ctx_menu_file-library_8qsbr","option-wrapper":"workspace_color_palette_ctx_menu_option-wrapper_st9Cq","library-name":"workspace_color_palette_ctx_menu_library-name_BL8b8","color-sample":"workspace_color_palette_ctx_menu_color-sample_jQUGL"} \ No newline at end of file +{"button-primary":"workspace_color_palette_ctx_menu_button-primary_2ka4z","button-secondary":"workspace_color_palette_ctx_menu_button-secondary_jfajf","button-tertiary":"workspace_color_palette_ctx_menu_button-tertiary_NLctS","button-tag":"workspace_color_palette_ctx_menu_button-tag_GN3ad","button-icon":"workspace_color_palette_ctx_menu_button-icon_cCaY2","button-icon-small":"workspace_color_palette_ctx_menu_button-icon-small_-knT4","palette-menu":"workspace_color_palette_ctx_menu_palette-menu_Vrjfy","palette-library":"workspace_color_palette_ctx_menu_palette-library_0LFV5","selected":"workspace_color_palette_ctx_menu_selected_lfchf","icon-wrapper":"workspace_color_palette_ctx_menu_icon-wrapper_v8-ys","recent-colors":"workspace_color_palette_ctx_menu_recent-colors_Q4fss","file-library":"workspace_color_palette_ctx_menu_file-library_8qsbr","asset-element":"workspace_color_palette_ctx_menu_asset-element_pV16m","option-wrapper":"workspace_color_palette_ctx_menu_option-wrapper_st9Cq","library-name":"workspace_color_palette_ctx_menu_library-name_BL8b8","color-sample":"workspace_color_palette_ctx_menu_color-sample_jQUGL"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/context_menu.css.json b/frontend/src/app/main/ui/workspace/context_menu.css.json index a41facee1a..a3b579c750 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.css.json +++ b/frontend/src/app/main/ui/workspace/context_menu.css.json @@ -1 +1 @@ -{"button-primary":"workspace_context_menu_button-primary_d6q-P","button-secondary":"workspace_context_menu_button-secondary_bIdqe","button-icon":"workspace_context_menu_button-icon_tXvxe","button-icon-small":"workspace_context_menu_button-icon-small_c0rVU","workspace-context-menu":"workspace_context_menu_workspace-context-menu_2NyvR","icon-menu-item":"workspace_context_menu_icon-menu-item_P3-bA","shape-icon":"workspace_context_menu_shape-icon_xx1Ll","workspace-context-submenu":"workspace_context_menu_workspace-context-submenu_BUNLt","selected-icon":"workspace_context_menu_selected-icon_pZqBp","context-menu-item":"workspace_context_menu_context-menu-item_Tx-Ty","submenu-icon":"workspace_context_menu_submenu-icon_JwYm8","separator":"workspace_context_menu_separator_E9-aR","title":"workspace_context_menu_title_P8iFL","shortcut":"workspace_context_menu_shortcut_rypUe","shortcut-key":"workspace_context_menu_shortcut-key_3rF3t","icon-wrapper":"workspace_context_menu_icon-wrapper_n7VO2"} \ No newline at end of file +{"button-primary":"workspace_context_menu_button-primary_d6q-P","button-secondary":"workspace_context_menu_button-secondary_bIdqe","button-tertiary":"workspace_context_menu_button-tertiary_vGSns","button-tag":"workspace_context_menu_button-tag_rOUbd","button-icon":"workspace_context_menu_button-icon_tXvxe","button-icon-small":"workspace_context_menu_button-icon-small_c0rVU","workspace-context-menu":"workspace_context_menu_workspace-context-menu_2NyvR","icon-menu-item":"workspace_context_menu_icon-menu-item_P3-bA","shape-icon":"workspace_context_menu_shape-icon_xx1Ll","workspace-context-submenu":"workspace_context_menu_workspace-context-submenu_BUNLt","selected-icon":"workspace_context_menu_selected-icon_pZqBp","context-menu-item":"workspace_context_menu_context-menu-item_Tx-Ty","submenu-icon":"workspace_context_menu_submenu-icon_JwYm8","asset-element":"workspace_context_menu_asset-element_jkrbj","separator":"workspace_context_menu_separator_E9-aR","title":"workspace_context_menu_title_P8iFL","shortcut":"workspace_context_menu_shortcut_rypUe","shortcut-key":"workspace_context_menu_shortcut-key_3rF3t","icon-wrapper":"workspace_context_menu_icon-wrapper_n7VO2"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/left_toolbar.cljs b/frontend/src/app/main/ui/workspace/left_toolbar.cljs index 6728f863ba..cd4deb9dc7 100644 --- a/frontend/src/app/main/ui/workspace/left_toolbar.cljs +++ b/frontend/src/app/main/ui/workspace/left_toolbar.cljs @@ -66,10 +66,10 @@ (let [selected-drawtool (mf/deref refs/selected-drawing-tool) edition (mf/deref refs/selected-edition) - new-css? (mf/use-ctx ctx/new-css-system) + new-css-system (mf/use-ctx ctx/new-css-system) read-only? (mf/use-ctx ctx/workspace-read-only?) - show-palette-btn? (and (not ^boolean read-only?) (not ^boolean new-css?)) + show-palette-btn? (and (not ^boolean read-only?) (not ^boolean new-css-system)) interrupt diff --git a/frontend/src/app/main/ui/workspace/libraries.cljs b/frontend/src/app/main/ui/workspace/libraries.cljs index e1b6660447..8b3adf122b 100644 --- a/frontend/src/app/main/ui/workspace/libraries.cljs +++ b/frontend/src/app/main/ui/workspace/libraries.cljs @@ -5,14 +5,20 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.workspace.libraries + (:require-macros [app.main.style :refer [css]]) (:require [app.common.data :as d] [app.common.data.macros :as dm] [app.common.types.components-list :as ctkl] [app.main.data.modal :as modal] [app.main.data.workspace.libraries :as dwl] + [app.main.features :as features] [app.main.refs :as refs] [app.main.store :as st] + [app.main.ui.components.search-bar :refer [search-bar]] + [app.main.ui.components.tab-container :refer [tab-container tab-element]] + [app.main.ui.components.title-bar :refer [title-bar]] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -56,6 +62,39 @@ (conj (tr "workspace.libraries.typography" typography-count)))) "\u00A0")) +(mf/defc describe-library-blocks + [{:keys [components-count graphics-count colors-count typography-count] :as props}] + + (let [last-one (cond + (> colors-count 0) :color + (> graphics-count 0) :graphics + (> components-count 0) :components)] + [:* + (when (pos? components-count) + [:* + [:span {:class (css :element-count)} + (tr "workspace.libraries.components" components-count)] + (when (not= last-one :components) + [:span " · "])]) + + (when (pos? graphics-count) + [:* + [:span {:class (css :element-count)} + (tr "workspace.libraries.graphics" graphics-count)] + (when (not= last-one :graphics) + [:span " · "])]) + + (when (pos? colors-count) + [:* + [:span {:class (css :element-count)} + (tr "workspace.libraries.colors" colors-count)] + (when (not= last-one :colors) + [:span " · "])]) + + (when (pos? typography-count) + [:span {:class (css :element-count)} + (tr "workspace.libraries.typography" typography-count)])])) + (defn- describe-linked-library [library] (let [components-count (count (or (ctkl/components-seq (:data library)) [])) @@ -75,16 +114,16 @@ (mf/defc libraries-tab {::mf/wrap-props false} [{:keys [file-id shared? linked-libraries shared-libraries]}] - (let [search-term* (mf/use-state "") - search-term (deref search-term*) - - library-ref (mf/with-memo [file-id] - (create-file-library-ref file-id)) - library (deref library-ref) - colors (:colors library) - components (:components library) - media (:media library) - typographies (:typographies library) + (let [search-term* (mf/use-state "") + search-term (deref search-term*) + new-css-system (mf/use-ctx ctx/new-css-system) + library-ref (mf/with-memo [file-id] + (create-file-library-ref file-id)) + library (deref library-ref) + colors (:colors library) + components (:components library) + media (:media library) + typographies (:typographies library) shared-libraries (mf/with-memo [shared-libraries linked-libraries file-id search-term] @@ -101,9 +140,12 @@ change-search-term (mf/use-fn + (mf/deps new-css-system) (fn [event] - (let [value (-> (dom/get-target event) - (dom/get-value))] + (let [value (if new-css-system + event + (-> (dom/get-target event) + (dom/get-value)))] (reset! search-term* value)))) clear-search-term @@ -111,20 +153,28 @@ link-library (mf/use-fn - (mf/deps file-id) + (mf/deps file-id new-css-system) (fn [event] - (let [library-id (some-> (dom/get-target event) - (dom/get-data "library-id") - (parse-uuid))] + (let [library-id (if new-css-system + (some-> (dom/get-current-target event) + (dom/get-data "library-id") + (parse-uuid)) + (some-> (dom/get-target event) + (dom/get-data "library-id") + (parse-uuid)))] (st/emit! (dwl/link-file-to-library file-id library-id))))) unlink-library (mf/use-fn (mf/deps file-id) (fn [event] - (let [library-id (some-> (dom/get-target event) - (dom/get-data "library-id") - (parse-uuid))] + (let [library-id (if new-css-system + (some-> (dom/get-current-target event) + (dom/get-data "library-id") + (parse-uuid)) + (some-> (dom/get-target event) + (dom/get-data "library-id") + (parse-uuid)))] (st/emit! (dwl/unlink-file-from-library file-id library-id) (dwl/sync-file file-id library-id))))) @@ -140,7 +190,10 @@ publish (mf/use-fn (mf/deps file-id) - #(st/emit! (dwl/set-file-shared file-id true))) + (fn [event] + (let [input-node (dom/event->target event)] + (st/emit! (dwl/set-file-shared file-id true)) + (dom/blur! input-node)))) unpublish (mf/use-fn @@ -165,122 +218,230 @@ (when ^boolean esc? (dom/blur! input-node)))))] - [:* - [:div.section - [:div.section-title (tr "workspace.libraries.in-this-file")] - [:div.section-list + (if new-css-system + [:* + [:div {:class (css :section)} + [:& title-bar {:collapsable? false + :title (tr "workspace.libraries.in-this-file") + :klass :title-spacing-lib}] + [:div {:class (css :section-list)} - [:div.section-list-item - [:div - [:div.item-name (tr "workspace.libraries.file-library")] - [:div.item-contents (describe-library - (count components) - (count media) - (count colors) - (count typographies))]] - [:div - (if ^boolean shared? - [:input.item-button {:type "button" - :value (tr "common.unpublish") - :on-click unpublish}] - [:input.item-button {:type "button" - :value (tr "common.publish") - :on-click publish}])]] + [:div {:class (css :section-list-item)} + [:div + [:div {:class (css :item-name)} (tr "workspace.libraries.file-library")] + [:div {:class (css :item-contents)} + [:& describe-library-blocks {:components-count (count components) + :graphics-count (count media) + :colors-count (count colors) + :typography-count (count typographies)}]]] + [:div + (if ^boolean shared? + [:input {:class (css :item-unpublish) + :type "button" + :value (tr "common.unpublish") + :on-click unpublish}] + [:input {:class (css :item-publish) + :type "button" + :value (tr "common.publish") + :on-click publish}])]] - (for [{:keys [id name] :as library} linked-libraries] - [:div.section-list-item {:key (dm/str id)} - [:div.item-name name] - [:div.item-contents (describe-linked-library library)] - [:input.item-button {:type "button" - :value (tr "labels.remove") - :data-library-id (dm/str id) - :on-click unlink-library}]])]] + (for [{:keys [id name] :as library} linked-libraries] + [:div {:class (css :section-list-item) + :key (dm/str id)} + [:div + [:div {:class (css :item-name)} name] + [:div {:class (css :item-contents)} + (let [components-count (count (or (ctkl/components-seq (:data library)) [])) + graphics-count (count (dm/get-in library [:data :media] [])) + colors-count (count (dm/get-in library [:data :colors] [])) + typography-count (count (dm/get-in library [:data :typographies] []))] + [:& describe-library-blocks {:components-count components-count + :graphics-count graphics-count + :colors-count colors-count + :typography-count typography-count}])]] - [:div.section - [:div.section-title (tr "workspace.libraries.shared-libraries")] - [:div.libraries-search - [:input.search-input - {:placeholder (tr "workspace.libraries.search-shared-libraries") - :type "text" - :value search-term - :on-change change-search-term - :on-key-down handle-key-down}] - (if (str/empty? search-term) - [:div.search-icon - i/search] - [:div.search-icon.search-close - {:on-click clear-search-term} - i/close])] + [:button {:class (css :item-button) + :type "button" + :data-library-id (dm/str id) + :on-click unlink-library} + i/delete-refactor]])]] - (if (seq shared-libraries) + [:div {:class (css :section)} + [:& title-bar {:collapsable? false + :title (tr "workspace.libraries.shared-libraries") + :klass :title-spacing-lib}] + [:div {:class (css :libraries-search)} + [:& search-bar {:on-change change-search-term + :value search-term + :placeholder (tr "workspace.libraries.search-shared-libraries") + :icon (mf/html [:span {:class (css :search-icon)} i/search-refactor])}]] + + (if (seq shared-libraries) + [:div {:class (css :section-list)} + (for [{:keys [id name] :as library} shared-libraries] + [:div {:class (css :section-list-item) + :key (dm/str id)} + [:div + [:div {:class (css :item-name)} name] + [:div {:class (css :item-contents)} + (let [components-count (dm/get-in library [:library-summary :components :count] 0) + graphics-count (dm/get-in library [:library-summary :media :count] 0) + colors-count (dm/get-in library [:library-summary :colors :count] 0) + typography-count (dm/get-in library [:library-summary :typographies :count] 0)] + [:& describe-library-blocks {:components-count components-count + :graphics-count graphics-count + :colors-count colors-count + :typography-count typography-count}])]] + [:button {:class (css :item-button) + :data-library-id (dm/str id) + :on-click link-library} + i/add-refactor]])] + + [:div {:class (css :section-list-empty)} + (if (nil? shared-libraries) + i/loader-pencil + (if (str/empty? search-term) + (tr "workspace.libraries.no-shared-libraries-available") + (tr "workspace.libraries.no-matches-for" search-term)))])]] + + [:* + [:div.section + [:div.section-title (tr "workspace.libraries.in-this-file")] [:div.section-list - (for [{:keys [id name] :as library} shared-libraries] + + [:div.section-list-item + [:div + [:div.item-name (tr "workspace.libraries.file-library")] + [:div.item-contents (describe-library + (count components) + (count media) + (count colors) + (count typographies))]] + [:div + (if ^boolean shared? + [:input.item-button {:type "button" + :value (tr "common.unpublish") + :on-click unpublish}] + [:input.item-button {:type "button" + :value (tr "common.publish") + :on-click publish}])]] + + (for [{:keys [id name] :as library} linked-libraries] [:div.section-list-item {:key (dm/str id)} [:div.item-name name] - [:div.item-contents (describe-external-library library)] + [:div.item-contents (describe-linked-library library)] [:input.item-button {:type "button" - :value (tr "workspace.libraries.add") + :value (tr "labels.remove") :data-library-id (dm/str id) - :on-click link-library}]])] + :on-click unlink-library}]])]] - [:div.section-list-empty - (if (nil? shared-libraries) - i/loader-pencil - [:* i/library - (if (str/empty? search-term) - (tr "workspace.libraries.no-shared-libraries-available") - (tr "workspace.libraries.no-matches-for" search-term))])])]])) + [:div.section + [:div.section-title (tr "workspace.libraries.shared-libraries")] + [:div.libraries-search + [:input.search-input + {:placeholder (tr "workspace.libraries.search-shared-libraries") + :type "text" + :value search-term + :on-change change-search-term + :on-key-down handle-key-down}] + (if (str/empty? search-term) + [:div.search-icon + i/search] + [:div.search-icon.search-close + {:on-click clear-search-term} + i/close])] + (if (seq shared-libraries) + [:div.section-list + (for [{:keys [id name] :as library} shared-libraries] + [:div.section-list-item {:key (dm/str id)} + [:div.item-name name] + [:div.item-contents (describe-external-library library)] + [:input.item-button {:type "button" + :value (tr "workspace.libraries.add") + :data-library-id (dm/str id) + :on-click link-library}]])] + + [:div.section-list-empty + (if (nil? shared-libraries) + i/loader-pencil + [:* i/library + (if (str/empty? search-term) + (tr "workspace.libraries.no-shared-libraries-available") + (tr "workspace.libraries.no-matches-for" search-term))])])]]))) (mf/defc updates-tab {::mf/wrap-props false} [{:keys [file-id file-data libraries]}] - (let [libraries (mf/with-memo [file-data libraries] - (filter #(seq (dwl/assets-need-sync % file-data)) - (vals libraries))) + (let [libraries (mf/with-memo [file-data libraries] + (filter #(seq (dwl/assets-need-sync % file-data)) (vals libraries))) + new-css-system (mf/use-ctx ctx/new-css-system) - update (mf/use-fn - (mf/deps file-id) - (fn [event] - (let [library-id (some-> (dom/get-target event) - (dom/get-data "library-id") - (parse-uuid))] - (st/emit! (dwl/sync-file file-id library-id)))))] - [:div.section - (if (empty? libraries) - [:div.section-list-empty - i/library - (tr "workspace.libraries.no-libraries-need-sync")] - [:* - [:div.section-title (tr "workspace.libraries.library")] - [:div.section-list - (for [{:keys [id name] :as library} libraries] - [:div.section-list-item {:key (dm/str id)} - [:div.item-name name] - [:div.item-contents (describe-external-library library)] - [:input.item-button {:type "button" - :value (tr "workspace.libraries.update") - :data-library-id (dm/str id) - :on-click update}]])]])])) + update (mf/use-fn + (mf/deps file-id) + (fn [event] + (let [library-id (some-> (dom/get-target event) + (dom/get-data "library-id") + (parse-uuid))] + (st/emit! (dwl/sync-file file-id library-id)))))] + (if new-css-system + [:div {:class (css :section)} + (if (empty? libraries) + [:div {:class (css :section-list-empty)} + (tr "workspace.libraries.no-libraries-need-sync")] + [:* + [:div {:class (css :section-title)} (tr "workspace.libraries.library")] + + [:div {:class (css :section-list)} + (for [{:keys [id name] :as library} libraries] + [:div {:class (css :section-list-item) + :key (dm/str id)} + [:div + [:div {:class (css :item-name)} name] + [:div {:class (css :item-contents)} (describe-external-library library)]] + [:input {:class (css :item-update) + :type "button" + :value (tr "workspace.libraries.update") + :data-library-id (dm/str id) + :on-click update}]])]])] + + [:div.section + (if (empty? libraries) + [:div.section-list-empty + i/library + (tr "workspace.libraries.no-libraries-need-sync")] + [:* + [:div.section-title (tr "workspace.libraries.library")] + + [:div.section-list + (for [{:keys [id name] :as library} libraries] + [:div.section-list-item {:key (dm/str id)} + [:div.item-name name] + [:div.item-contents (describe-external-library library)] + [:input.item-button {:type "button" + :value (tr "workspace.libraries.update") + :data-library-id (dm/str id) + :on-click update}]])]])]))) (mf/defc libraries-dialog {::mf/register modal/components ::mf/register-as :libraries-dialog} [] - (let [project (mf/deref refs/workspace-project) - file-data (mf/deref refs/workspace-data) - file (mf/deref ref:workspace-file) + (let [new-css-system (features/use-feature :new-css-system) + project (mf/deref refs/workspace-project) + file-data (mf/deref refs/workspace-data) + file (mf/deref ref:workspace-file) - team-id (:team-id project) - file-id (:id file) - shared? (:is-shared file) + team-id (:team-id project) + file-id (:id file) + shared? (:is-shared file) - selected-tab* (mf/use-state :libraries) - selected-tab (deref selected-tab*) + selected-tab* (mf/use-state :libraries) + selected-tab (deref selected-tab*) - libraries (mf/deref refs/workspace-libraries) - libraries (mf/with-memo [libraries] - (d/removem (fn [[_ val]] (:is-indirect val)) libraries)) + libraries (mf/deref refs/workspace-libraries) + libraries (mf/with-memo [libraries] + (d/removem (fn [[_ val]] (:is-indirect val)) libraries)) ;; NOTE: we really don't need react on shared files shared-libraries @@ -292,35 +453,61 @@ select-updates-tab (mf/use-fn #(reset! selected-tab* :updates)) + on-tab-change + (mf/use-fn #(reset! selected-tab* %)) + close-dialog - (mf/use-fn #(modal/hide!))] + (mf/use-fn (fn [_] (modal/hide!) + (modal/disallow-click-outside!)))] (mf/with-effect [team-id] (when team-id (st/emit! (dwl/fetch-shared-files {:team-id team-id})))) + [:& (mf/provider ctx/new-css-system) {:value new-css-system} + (if new-css-system + [:div {:class (css :modal-overlay)} + [:div {:class (css :modal-dialog)} + [:div {:class (css :modal-content)} + [:div {:class (css :libraries-header)} - [:div.modal-overlay - [:div.modal.libraries-dialog - [:a.close {:on-click close-dialog} i/close] - [:div.modal-content - [:div.libraries-header - [:div.header-item - {:class (dom/classnames :active (= selected-tab :libraries)) - :on-click select-libraries-tab} - (tr "workspace.libraries.libraries")] - [:div.header-item - {:class (dom/classnames :active (= selected-tab :updates)) - :on-click select-updates-tab} - (tr "workspace.libraries.updates")]] - [:div.libraries-content - (case selected-tab - :libraries - [:& libraries-tab {:file-id file-id - :shared? shared? - :linked-libraries libraries - :shared-libraries shared-libraries}] - :updates - [:& updates-tab {:file-id file-id - :file-data file-data - :libraries libraries}])]]]])) + [:& tab-container + {:on-change-tab on-tab-change + :selected selected-tab + :collapsable? false} + [:& tab-element {:id :libraries :title (tr "workspace.libraries.libraries")} + [:div {:class (dom/classnames (css :libraries-content) true)} + [:& libraries-tab {:file-id file-id + :shared? shared? + :linked-libraries libraries + :shared-libraries shared-libraries}]]] + [:& tab-element {:id :updates :title (tr "workspace.libraries.updates")} + [:div {:class (dom/classnames (css :updates-content) true)} + [:& updates-tab {:file-id file-id + :file-data file-data + :libraries libraries}]]]]]]]] + + [:div.modal-overlay + [:div.modal.libraries-dialog + [:a.close {:on-click close-dialog} i/close] + [:div.modal-content + [:div.libraries-header + [:div.header-item + {:class (dom/classnames :active (= selected-tab :libraries)) + :on-click select-libraries-tab} + (tr "workspace.libraries.libraries")] + [:div.header-item + {:class (dom/classnames :active (= selected-tab :updates)) + :on-click select-updates-tab} + (tr "workspace.libraries.updates")]] + [:div.libraries-content + (case selected-tab + :libraries + [:& libraries-tab {:file-id file-id + :shared? shared? + :linked-libraries libraries + :shared-libraries shared-libraries}] + :updates + [:& updates-tab {:file-id file-id + :file-data file-data + :libraries libraries}])]]]])])) diff --git a/frontend/src/app/main/ui/workspace/libraries.css.json b/frontend/src/app/main/ui/workspace/libraries.css.json new file mode 100644 index 0000000000..0ffda66893 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/libraries.css.json @@ -0,0 +1 @@ +{"button-primary":"workspace_libraries_button-primary_Hsioh","modal-overlay":"workspace_libraries_modal-overlay_qC-df","modal-dialog":"workspace_libraries_modal-dialog_Kj293","modal-content":"workspace_libraries_modal-content_4EVEQ","libraries-content":"workspace_libraries_libraries-content_ycQdm","section":"workspace_libraries_section_SUsgi","section-list":"workspace_libraries_section-list_lGSrM","section-list-item":"workspace_libraries_section-list-item_hwASN","item-publish":"workspace_libraries_item-publish_ZMopF","item-unpublish":"workspace_libraries_item-unpublish_1seca","item-update":"workspace_libraries_item-update_GklIE","updates-content":"workspace_libraries_updates-content_lqMoE","button-secondary":"workspace_libraries_button-secondary_l5M0x","button-tertiary":"workspace_libraries_button-tertiary_C54rH","item-button":"workspace_libraries_item-button_dKUeX","close":"workspace_libraries_close_bED7B","button-tag":"workspace_libraries_button-tag_wAh-s","button-icon":"workspace_libraries_button-icon_kxS7q","item-button-icon":"workspace_libraries_item-button-icon_CeJWg","button-icon-small":"workspace_libraries_button-icon-small_Q9eo3","section-list-empty":"workspace_libraries_section-list-empty_mOKkJ","libraries-search":"workspace_libraries_libraries-search_JS70w","search-icon":"workspace_libraries_search-icon_y7N9S","asset-element":"workspace_libraries_asset-element_-FuJl","libraries-header":"workspace_libraries_libraries-header_-W6bJ","item-name":"workspace_libraries_item-name_Zjbsw","item-contents":"workspace_libraries_item-contents_EPTF6","section-title":"workspace_libraries_section-title_7rsm7","element-count":"workspace_libraries_element-count_07SV2"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/libraries.scss b/frontend/src/app/main/ui/workspace/libraries.scss new file mode 100644 index 0000000000..8ab144ad6e --- /dev/null +++ b/frontend/src/app/main/ui/workspace/libraries.scss @@ -0,0 +1,139 @@ +// 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 { + @include flexCenter; + position: fixed; + left: 0; + top: 0; + height: 100%; + width: 100%; + z-index: $z-index-modal; + background-color: var(--overlay-color); + pointer-events: none; // This is to allow outside click that closes modal. + .modal-dialog { + height: $s-400; + max-height: 100%; + width: $s-664; + border-radius: $br-8; + background-color: var(--modal-background-color); + pointer-events: all; + .close { + @extend .button-tertiary; + width: $s-32; + height: $s-32; + border-radius: $br-8; + svg { + @extend .button-icon; + } + } + .modal-content { + .libraries-header { + padding: $s-12; + } + + .libraries-content, + .updates-content { + display: grid; + grid-template-columns: 1fr 1fr; + gap: $s-24; + padding-top: $s-16; + height: 100%; + + .section { + height: 100%; + :global(.title-spacing-lib) { + margin: 0 0 $s-8 calc(-1 * $s-8); + } + .section-list { + height: 100%; + max-height: 250px; + overflow: auto; + .section-list-item { + display: grid; + grid-template-columns: 1fr auto; + margin-bottom: $s-12; + + .item-name { + @include titleTipography; + color: var(--library-name-foreground-color); + } + .item-contents { + @include titleTipography; + color: var(--library-content-foreground-color); + } + + .item-publish, + .item-unpublish, + .item-update { + @extend .button-primary; + @include tabTitleTipography; + height: $s-32; + min-width: $s-92; + padding: $s-8 $s-12; + border-radius: $br-8; + } + .item-unpublish { + @extend .button-secondary; + } + .item-button { + @extend .button-tertiary; + width: $s-32; + height: $s-32; + border-radius: $br-8; + svg { + @extend .button-icon; + } + } + .item-button-icon { + width: $s-28; + height: $s-28; + svg { + @extend .button-icon; + } + } + } + } + + .section-title { + @include titleTipography; + margin-bottom: $s-12; + } + .libraries-search { + margin-bottom: $s-12; + .search-icon { + @include flexCenter; + padding: 0 0 0 $s-8; + width: $s-20; + svg { + @extend .button-icon-small; + } + } + } + .section-list-empty { + @include titleTipography; + display: flex; + align-items: center; + + svg { + @extend .button-icon-small; + width: $s-16; + height: $s-16; + } + } + } + } + .updates-content { + grid-template-columns: 1fr; + } + } + } +} + +.element-count { + white-space: nowrap; +} diff --git a/frontend/src/app/main/ui/workspace/palette.css.json b/frontend/src/app/main/ui/workspace/palette.css.json index ec0718e284..decd0e4101 100644 --- a/frontend/src/app/main/ui/workspace/palette.css.json +++ b/frontend/src/app/main/ui/workspace/palette.css.json @@ -1 +1 @@ -{"button-primary":"workspace_palette_button-primary_zEUyD","palettes":"workspace_palette_palettes_JHGUw","palette-actions":"workspace_palette_palette-actions_2GwR6","palette-btn-list":"workspace_palette_palette-btn-list_x7gPS","palette-item":"workspace_palette_palette-item_50uj6","palette-btn":"workspace_palette_palette-btn_kP66y","button-secondary":"workspace_palette_button-secondary_ksr24","button-icon":"workspace_palette_button-icon_pmEDv","button-icon-small":"workspace_palette_button-icon-small_vbLDq","wide":"workspace_palette_wide_3G4e1","mid-palette":"workspace_palette_mid-palette_rGR5I","small-palette":"workspace_palette_small-palette_18Otk","resize-area":"workspace_palette_resize-area_0LwVu","selected":"workspace_palette_selected_Z6BFo","palette":"workspace_palette_palette_eqp3q","handler":"workspace_palette_handler_4JV0J","handler-btn":"workspace_palette_handler-btn_7lnlF","hidden-bts":"workspace_palette_hidden-bts_mhbc0"} \ No newline at end of file +{"button-primary":"workspace_palette_button-primary_zEUyD","button-secondary":"workspace_palette_button-secondary_ksr24","button-tertiary":"workspace_palette_button-tertiary_91YQK","palettes":"workspace_palette_palettes_JHGUw","palette-actions":"workspace_palette_palette-actions_2GwR6","palette-btn-list":"workspace_palette_palette-btn-list_x7gPS","palette-item":"workspace_palette_palette-item_50uj6","palette-btn":"workspace_palette_palette-btn_kP66y","button-tag":"workspace_palette_button-tag_S9v-Z","button-icon":"workspace_palette_button-icon_pmEDv","button-icon-small":"workspace_palette_button-icon-small_vbLDq","asset-element":"workspace_palette_asset-element_4bXi3","wide":"workspace_palette_wide_3G4e1","mid-palette":"workspace_palette_mid-palette_rGR5I","small-palette":"workspace_palette_small-palette_18Otk","resize-area":"workspace_palette_resize-area_0LwVu","selected":"workspace_palette_selected_Z6BFo","palette":"workspace_palette_palette_eqp3q","handler":"workspace_palette_handler_4JV0J","handler-btn":"workspace_palette_handler-btn_7lnlF","hidden-bts":"workspace_palette_hidden-bts_mhbc0"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/palette.scss b/frontend/src/app/main/ui/workspace/palette.scss index 209593e2fe..e2d1d2ea41 100644 --- a/frontend/src/app/main/ui/workspace/palette.scss +++ b/frontend/src/app/main/ui/workspace/palette.scss @@ -57,7 +57,7 @@ opacity: $op-10; transition: opacity 1s ease; .palette-btn { - @extend .button-primary; + @extend .button-tertiary; height: $s-32; width: $s-32; border-radius: $br-8; @@ -82,7 +82,7 @@ } } .palette-actions { - @extend .button-primary; + @extend .button-tertiary; grid-area: actions; height: calc(var(--height) - $s-16); width: $s-32; diff --git a/frontend/src/app/main/ui/workspace/sidebar.cljs b/frontend/src/app/main/ui/workspace/sidebar.cljs index 2f6de0e75b..870487b719 100644 --- a/frontend/src/app/main/ui/workspace/sidebar.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar.cljs @@ -12,6 +12,7 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.tab-container :refer [tab-container tab-element]] + [app.main.ui.components.tabs-container :refer [tabs-container tabs-element]] [app.main.ui.context :as ctx] [app.main.ui.hooks.resize :refer [use-resize-hook]] [app.main.ui.icons :as i] @@ -40,7 +41,7 @@ (contains? layout :assets) :assets) shortcuts? (contains? layout :shortcuts) show-debug? (contains? layout :debug-panel) - new-css? (mf/use-ctx ctx/new-css-system) + new-css-system (mf/use-ctx ctx/new-css-system) {:keys [on-pointer-down on-lost-pointer-capture on-pointer-move parent-ref size]} (use-resize-hook :left-sidebar 255 255 500 :x false :left) @@ -49,12 +50,14 @@ (mf/use-fn #(st/emit! (dw/toggle-layout-flag :collapse-left-sidebar))) on-tab-change - (mf/use-fn #(st/emit! (dw/go-to-layout %))) - ] + (mf/use-fn #(st/emit! (dw/go-to-layout %)))] [:aside {:ref parent-ref - :class (if ^boolean new-css? - (dom/classnames (css :left-settings-bar) true) + :class (if ^boolean new-css-system + (dom/classnames (css :left-settings-bar) true + :two-row (<= size 300) + :three-row (and (> size 300) (<= size 400)) + :four-row (> size 400)) (dom/classnames :settings-bar true :settings-bar-left true :two-row (<= size 300) @@ -65,10 +68,10 @@ [:div {:on-pointer-down on-pointer-down :on-lost-pointer-capture on-lost-pointer-capture :on-pointer-move on-pointer-move - :class (if ^boolean new-css? + :class (if ^boolean new-css-system (dom/classnames (css :resize-area) true) (dom/classnames :resize-area true))}] - [:div {:class (if ^boolean new-css? + [:div {:class (if ^boolean new-css-system (dom/classnames (css :settings-bar-inside) true) (dom/classnames :settings-bar-inside true))} (cond @@ -79,22 +82,23 @@ [:& debug-panel] :else - (if ^boolean new-css? - [:& tab-container - {:on-change-tab on-tab-change - :selected section - :shortcuts? shortcuts? - :collapsable? true - :handle-collapse handle-collapse} + (if ^boolean new-css-system + [:div {:class (dom/classnames (css :tabs-wrapper) true)} + [:& tab-container + {:on-change-tab on-tab-change + :selected section + :shortcuts? shortcuts? + :collapsable? true + :handle-collapse handle-collapse + :klass :tab-spacing} + [:& tab-element {:id :layers :title (tr "workspace.sidebar.layers")} + [:div {:class (dom/classnames (css :layers-tab) true)} + [:& sitemap {:layout layout}] + [:& layers-toolbox {:size-parent size}]]] - [:& tab-element {:id :layers :title (tr "workspace.sidebar.layers")} - [:div {:class (dom/classnames (css :layers-tab) true)} - [:& sitemap {:layout layout}] - [:& layers-toolbox {:size-parent size}]]] - - (when-not ^boolean mode-inspect? - [:& tab-element {:id :assets :title (tr "workspace.toolbar.assets")} - [:& assets-toolbox]])] + (when-not ^boolean mode-inspect? + [:& tab-element {:id :assets :title (tr "workspace.toolbar.assets")} + [:& assets-toolbox]])]] [:* [:button.collapse-sidebar @@ -102,20 +106,20 @@ :aria-label (tr "workspace.sidebar.collapse")} i/arrow-slide] - [:& tab-container + [:& tabs-container {:on-change-tab on-tab-change :selected section :shortcuts? shortcuts? :collapsable? true :handle-collapse handle-collapse} - [:& tab-element {:id :layers :title (tr "workspace.sidebar.layers")} + [:& tabs-element {:id :layers :title (tr "workspace.sidebar.layers")} [:div {:class (dom/classnames :layers-tab true)} [:& sitemap {:layout layout}] [:& layers-toolbox {:size-parent size}]]] (when-not ^boolean mode-inspect? - [:& tab-element {:id :assets :title (tr "workspace.toolbar.assets")} + [:& tabs-element {:id :assets :title (tr "workspace.toolbar.assets")} [:& assets-toolbox]])]]))]])) ;; --- Right Sidebar (Component) @@ -150,4 +154,3 @@ :else [:> options-toolbox props])]])) - diff --git a/frontend/src/app/main/ui/workspace/sidebar.css.json b/frontend/src/app/main/ui/workspace/sidebar.css.json index c27ff9be21..ac76a824b5 100644 --- a/frontend/src/app/main/ui/workspace/sidebar.css.json +++ b/frontend/src/app/main/ui/workspace/sidebar.css.json @@ -1 +1 @@ -{"button-primary":"workspace_sidebar_button-primary_K7xW6","button-secondary":"workspace_sidebar_button-secondary_e2eQE","button-icon":"workspace_sidebar_button-icon_OXdmL","button-icon-small":"workspace_sidebar_button-icon-small_EYb9x","left-settings-bar":"workspace_sidebar_left-settings-bar_7co5t","resize-area":"workspace_sidebar_resize-area_ny1v0","settings-bar-inside":"workspace_sidebar_settings-bar-inside_YnFv8","layers-tab":"workspace_sidebar_layers-tab_soxRL"} \ No newline at end of file +{"button-primary":"workspace_sidebar_button-primary_K7xW6","button-secondary":"workspace_sidebar_button-secondary_e2eQE","button-tertiary":"workspace_sidebar_button-tertiary_QKqHT","button-tag":"workspace_sidebar_button-tag_Xc0Sm","button-icon":"workspace_sidebar_button-icon_OXdmL","button-icon-small":"workspace_sidebar_button-icon-small_EYb9x","asset-element":"workspace_sidebar_asset-element_vzrfV","left-settings-bar":"workspace_sidebar_left-settings-bar_7co5t","resize-area":"workspace_sidebar_resize-area_ny1v0","settings-bar-inside":"workspace_sidebar_settings-bar-inside_YnFv8","tabs-wrapper":"workspace_sidebar_tabs-wrapper_YDo4o","layers-tab":"workspace_sidebar_layers-tab_soxRL"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar.scss b/frontend/src/app/main/ui/workspace/sidebar.scss index 6f2d76f8da..c7a1aedee7 100644 --- a/frontend/src/app/main/ui/workspace/sidebar.scss +++ b/frontend/src/app/main/ui/workspace/sidebar.scss @@ -33,12 +33,17 @@ $width-settings-bar-max: 500px; grid-template-columns: 100%; grid-template-rows: 100%; height: calc(100% - 2px); - .layers-tab { - display: grid; - grid-template-rows: auto 1fr; - grid-template-columns: 100%; - height: 100%; - overflow: hidden; + .tabs-wrapper { + .layers-tab { + display: grid; + grid-template-rows: auto 1fr; + grid-template-columns: 100%; + height: 100%; + overflow: hidden; + } } } } +:global(.tab-spacing) { + margin: $s-4 $s-4 0 $s-4; +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs index 339aa0bf6e..cd6b7a4066 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs @@ -5,2374 +5,23 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.workspace.sidebar.assets + (:require-macros [app.main.style :refer [css]]) (:require - [app.common.data :as d] [app.common.data.macros :as dm] - [app.common.media :as cm] - [app.common.pages.helpers :as cph] - [app.common.spec :as us] - [app.common.types.file :as ctf] - [app.config :as cf] - [app.main.data.events :as ev] [app.main.data.modal :as modal] - [app.main.data.workspace :as dw] - [app.main.data.workspace.colors :as dc] - [app.main.data.workspace.libraries :as dwl] - [app.main.data.workspace.media :as dwm] - [app.main.data.workspace.texts :as dwt] - [app.main.data.workspace.undo :as dwu] [app.main.refs :as refs] - [app.main.render :refer [component-svg]] - [app.main.store :as st] - [app.main.ui.components.color-bullet :as bc] - [app.main.ui.components.context-menu :refer [context-menu]] - [app.main.ui.components.editable-label :refer [editable-label]] - [app.main.ui.components.file-uploader :refer [file-uploader]] - [app.main.ui.components.forms :as fm] + [app.main.ui.components.context-menu-a11y :refer [context-menu-a11y]] + [app.main.ui.components.search-bar :refer [search-bar]] [app.main.ui.context :as ctx] - [app.main.ui.hooks :as h] [app.main.ui.icons :as i] - [app.main.ui.workspace.libraries :refer [create-file-library-ref]] - [app.main.ui.workspace.sidebar.options.menus.typography :refer [typography-entry]] - [app.util.color :as uc] + [app.main.ui.workspace.sidebar.assets.common :as cmm] + [app.main.ui.workspace.sidebar.assets.file-library :refer [file-library]] [app.util.dom :as dom] - [app.util.dom.dnd :as dnd] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] - [app.util.router :as rt] - [app.util.strings :refer [matches-search]] - [app.util.timers :as ts] - [cljs.spec.alpha :as s] [cuerdas.core :as str] - [okulary.core :as l] - [potok.core :as ptk] [rumext.v2 :as mf])) -(def ctx:filters (mf/create-context nil)) -(def ctx:toggle-ordering (mf/create-context nil)) -(def ctx:toggle-list-style (mf/create-context nil)) - -(def lens:selected - (-> (l/in [:workspace-assets :selected]) - (l/derived st/state))) - -(def lens:open-status - (l/derived (l/in [:workspace-assets :open-status]) st/state)) - -(def lens:typography-section-state - (l/derived (fn [gstate] - {:rename-typography (:rename-typography gstate) - :edit-typography (:edit-typography gstate)}) - refs/workspace-global - =)) - -;; ---- Group assets management ---- - -(defn group-assets - "Convert a list of assets in a nested structure like this: - - {'': [{assetA} {assetB}] - 'group1': {'': [{asset1A} {asset1B}] - 'subgroup11': {'': [{asset11A} {asset11B} {asset11C}]} - 'subgroup12': {'': [{asset12A}]}} - 'group2': {'subgroup21': {'': [{asset21A}}}} - " - [assets reverse-sort?] - (when-not (empty? assets) - (reduce (fn [groups {:keys [path] :as asset}] - (let [path (cph/split-path (or path ""))] - (update-in groups - (conj path "") - (fn [group] - (if group - (conj group asset) - [asset]))))) - (sorted-map-by (fn [key1 key2] - (if reverse-sort? - (compare key2 key1) - (compare key1 key2)))) - assets))) - -(defn add-group - [asset group-name] - (-> (:path asset) - (cph/merge-path-item group-name) - (cph/merge-path-item (:name asset)))) - -(defn rename-group - [asset path last-path] - (-> (:path asset) - (str/slice 0 (count path)) - (cph/split-path) - butlast - (vec) - (conj last-path) - (cph/join-path) - (str (str/slice (:path asset) (count path))) - (cph/merge-path-item (:name asset)))) - -(defn ungroup - [asset path] - (-> (:path asset) - (str/slice 0 (count path)) - (cph/split-path) - butlast - (cph/join-path) - (str (str/slice (:path asset) (count path))) - (cph/merge-path-item (:name asset)))) - -(s/def ::asset-name ::us/not-empty-string) -(s/def ::name-group-form - (s/keys :req-un [::asset-name])) - -(mf/defc name-group-dialog - {::mf/register modal/components - ::mf/register-as :name-group-dialog} - [{:keys [path last-path accept] :as ctx - :or {path "" last-path ""}}] - (let [initial (mf/use-memo - (mf/deps last-path) - (constantly {:asset-name last-path})) - form (fm/use-form :spec ::name-group-form - :initial initial) - - create? (empty? path) - - on-close (mf/use-fn #(modal/hide!)) - - on-accept - (mf/use-fn - (mf/deps form) - (fn [_] - (let [asset-name (get-in @form [:clean-data :asset-name])] - (if create? - (accept asset-name) - (accept path asset-name)) - (modal/hide!))))] - - [: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}]]]]])) - - -;; ---- Group assets by drag and drop ---- - -(defn- create-assets-group - [rename components-to-group group-name] - (let [undo-id (js/Symbol)] - (st/emit! (dwu/start-undo-transaction undo-id)) - (apply st/emit! - (->> components-to-group - (map #(rename - (:id %) - (add-group % group-name))))) - (st/emit! (dwu/commit-undo-transaction undo-id)))) - -(defn- on-drop-asset - [event asset dragging* selected selected-full selected-paths rename] - (let [create-typed-assets-group (partial create-assets-group rename)] - (when (not (dnd/from-child? event)) - (reset! dragging* false) - (when - (and (not (contains? selected (:id asset))) - (every? #(= % (:path asset)) selected-paths)) - (let [components-to-group (conj selected-full asset) - create-typed-assets-group (partial create-typed-assets-group components-to-group)] - (modal/show! :name-group-dialog {:accept create-typed-assets-group})))))) - -(defn- on-drag-enter-asset - [event asset dragging* selected selected-paths] - (when (and - (not (dnd/from-child? event)) - (every? #(= % (:path asset)) selected-paths) - (not (contains? selected (:id asset)))) - (reset! dragging* true))) - -(defn- on-drag-leave-asset - [event dragging*] - (when (not (dnd/from-child? event)) - (reset! dragging* false))) - -(defn- create-counter-element - [asset-count] - (let [counter-el (dom/create-element "div")] - (dom/set-property! counter-el "class" "drag-counter") - (dom/set-text! counter-el (str asset-count)) - counter-el)) - -(defn- set-drag-image - [event item-ref num-selected] - (let [offset (dom/get-offset-position (.-nativeEvent event)) - item-el (mf/ref-val item-ref) - counter-el (create-counter-element num-selected)] - - ;; set-drag-image requires that the element is rendered and - ;; visible to the user at the moment of creating the ghost - ;; image (to make a snapshot), but you may remove it right - ;; afterwards, in the next render cycle. - (dom/append-child! item-el counter-el) - (dnd/set-drag-image! event item-el (:x offset) (:y offset)) - (ts/raf #(.removeChild ^js item-el counter-el)))) - -(defn- on-asset-drag-start - [event file-id asset selected item-ref asset-type on-drag-start] - (let [id-asset (:id asset) - num-selected (if (contains? selected id-asset) - (count selected) - 1)] - (when (not (contains? selected id-asset)) - (st/emit! (dw/unselect-all-assets file-id) - (dw/toggle-selected-assets file-id id-asset asset-type))) - (on-drag-start asset event) - (when (> num-selected 1) - (set-drag-image event item-ref num-selected)))) - -(defn- on-drag-enter-asset-group - [event dragging* prefix selected-paths] - (dom/stop-propagation event) - (when (and (not (dnd/from-child? event)) - (not (every? #(= % prefix) selected-paths))) - (reset! dragging* true))) - -(defn- on-drop-asset-group - [event dragging* prefix selected-paths selected-full rename] - (dom/stop-propagation event) - (when (not (dnd/from-child? event)) - (reset! dragging* false) - (when (not (every? #(= % prefix) selected-paths)) - (doseq [target-asset selected-full] - (st/emit! - (rename - (:id target-asset) - (cph/merge-path-item prefix (:name target-asset)))))))) - -;; ---- Common blocks ---- - -(def ^:private initial-context-menu-state - {:open? false :top nil :left nil}) - -(defn- open-context-menu - [state pos] - (let [top (:y pos) - left (+ (:x pos) 10)] - (assoc state - :open? true - :top top - :left left))) - -(defn- close-context-menu - [state] - (assoc state :open? false)) - -(mf/defc assets-context-menu - {::mf/wrap-props false} - [{:keys [options state on-close]}] - [:& context-menu - {:selectable false - :show (:open? state) - :on-close on-close - :top (:top state) - :left (:left state) - :options options}]) - -(mf/defc asset-section - {::mf/wrap-props false} - [{:keys [children file-id title section assets-count open?]}] - (let [children (->> (if (array? children) children [children]) - (filter some?)) - get-role #(.. % -props -role) - title-buttons (filter #(= (get-role %) :title-button) children) - content (filter #(= (get-role %) :content) children)] - [:div.asset-section - [:div.asset-title {:class (when (not ^boolean open?) "closed")} - [:span {:on-click #(st/emit! (dw/set-assets-section-open file-id section (not open?)))} - i/arrow-slide title] - [:span.num-assets (str "\u00A0(") assets-count ")"] ;; Unicode 00A0 is non-breaking space - title-buttons] - (when ^boolean open? - content)])) - -(mf/defc asset-section-block - [{:keys [children]}] - [:* children]) - -(mf/defc asset-group-title - [{:keys [file-id section path group-open? on-rename on-ungroup]}] - (when-not (empty? path) - (let [[other-path last-path truncated] (cph/compact-path path 35) - menu-state (mf/use-state initial-context-menu-state) - - on-fold-group - (mf/use-fn - (mf/deps file-id section path group-open?) - (fn [event] - (dom/stop-propagation event) - (st/emit! (dw/set-assets-group-open file-id - section - path - (not group-open?))))) - on-context-menu - (mf/use-fn - (fn [event] - (dom/prevent-default event) - (let [pos (dom/get-client-position event)] - (swap! menu-state open-context-menu pos)))) - - on-close-menu - (mf/use-fn #(swap! menu-state close-context-menu))] - - [:div.group-title {:class (when-not group-open? "closed") - :on-click on-fold-group - :on-context-menu on-context-menu} - [:span i/arrow-slide] - (when-not (empty? other-path) - [:span.dim {:title (when truncated path)} - other-path "\u00A0/\u00A0"]) - [:span {:title (when truncated path)} - last-path] - [:& assets-context-menu - {:on-close on-close-menu - :state @menu-state - :options [[(tr "workspace.assets.rename") #(on-rename % path last-path)] - [(tr "workspace.assets.ungroup") #(on-ungroup path)]]}]]))) - - -;;---- Components section ---- - - -(defn- get-component-root-and-container - [file-id component components-v2] - (if (= file-id (:id @refs/workspace-file)) - (let [data @refs/workspace-data] - [(ctf/get-component-root data component) - (if components-v2 - (ctf/get-component-page data component) - component)]) - (let [data (dm/get-in @refs/workspace-libraries [file-id :data])] - [(ctf/get-component-root data component) - (if components-v2 - (ctf/get-component-page data component) - component)]))) - -(mf/defc components-item - {::mf/wrap-props false} - [{:keys [component renaming listing-thumbs? selected - file-id on-asset-click on-context-menu on-drag-start do-rename - cancel-rename selected-full selected-paths]}] - (let [item-ref (mf/use-ref) - - dragging* (mf/use-state false) - dragging? (deref dragging*) - - read-only? (mf/use-ctx ctx/workspace-read-only?) - components-v2 (mf/use-ctx ctx/components-v2) - component-id (:id component) - - ;; NOTE: we don't use reactive deref for it because we don't - ;; really need rerender on any change on the file change. If - ;; the component changes, it will trigger rerender anyway. - [root-shape container] - (get-component-root-and-container file-id component components-v2) - - unselect-all - (mf/use-fn - (fn [] - (st/emit! (dw/unselect-all-assets)))) - - on-component-click - (mf/use-fn - (mf/deps component selected) - (fn [event] - (dom/stop-propagation event) - (on-asset-click component-id unselect-all event))) - - on-component-double-click - (mf/use-fn - (mf/deps file-id component-id) - (fn [event] - (dom/stop-propagation event) - (st/emit! (dw/go-to-main-instance file-id component-id)))) - - on-drop - (mf/use-fn - (mf/deps component dragging* selected selected-full selected-paths) - (fn [event] - (on-drop-asset event component dragging* selected selected-full - selected-paths dwl/rename-component))) - - on-drag-enter - (mf/use-fn - (mf/deps component dragging* selected selected-paths) - (fn [event] - (on-drag-enter-asset event component dragging* selected selected-paths))) - - on-drag-leave - (mf/use-fn - (mf/deps dragging*) - (fn [event] - (on-drag-leave-asset event dragging*))) - - on-component-drag-start - (mf/use-fn - (mf/deps file-id component selected item-ref on-drag-start read-only?) - (fn [event] - (if read-only? - (dom/prevent-default event) - (on-asset-drag-start event file-id component selected item-ref :components on-drag-start)))) - - on-context-menu - (mf/use-fn - (mf/deps component-id) - (partial on-context-menu component-id))] - - [:div {:ref item-ref - :class (dom/classnames - :selected (contains? selected (:id component)) - :grid-cell listing-thumbs? - :enum-item (not listing-thumbs?)) - :id (dm/str "component-shape-id-" (:id component)) - :draggable (not read-only?) - :on-click on-component-click - :on-double-click on-component-double-click - :on-context-menu on-context-menu - :on-drag-start on-component-drag-start - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - - (when (and (some? root-shape) - (some? container)) - [:* - [:& component-svg {:root-shape root-shape - :objects (:objects container)}] - (let [renaming? (= renaming (:id component))] - [:* - [:& editable-label - {:class-name (dom/classnames - :cell-name listing-thumbs? - :item-name (not listing-thumbs?) - :editing renaming?) - :value (cph/merge-path-item (:path component) (:name component)) - :tooltip (cph/merge-path-item (:path component) (:name component)) - :display-value (:name component) - :editing? renaming? - :disable-dbl-click? true - :on-change do-rename - :on-cancel cancel-rename}] - - (when ^boolean dragging? - [:div.dragging])])])])) - -(mf/defc components-group - {::mf/wrap-props false} - [{:keys [file-id prefix groups open-groups renaming listing-thumbs? selected on-asset-click - on-drag-start do-rename cancel-rename on-rename-group on-group on-ungroup on-context-menu - selected-full]}] - - (let [group-open? (get open-groups prefix true) - - dragging* (mf/use-state false) - dragging? (deref dragging*) - - selected-paths (mf/with-memo [selected-full] - (into #{} - (comp (map :path) (d/nilv "")) - selected-full)) - on-drag-enter - (mf/use-fn - (mf/deps dragging* prefix selected-paths) - (fn [event] - (on-drag-enter-asset-group event dragging* prefix selected-paths))) - - on-drag-leave - (mf/use-fn - (mf/deps dragging*) - (fn [event] - (on-drag-leave-asset event dragging*))) - - on-drop - (mf/use-fn - (mf/deps dragging* prefix selected-paths selected-full) - (fn [event] - (on-drop-asset-group event dragging* prefix selected-paths selected-full dwl/rename-component)))] - - [:div {:on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - - [:& asset-group-title - {:file-id file-id - :section :components - :path prefix - :group-open? group-open? - :on-rename on-rename-group - :on-ungroup on-ungroup}] - - (when group-open? - [:* - (let [components (get groups "" [])] - [:div {:class-name (dom/classnames - :asset-grid listing-thumbs? - :big listing-thumbs? - :asset-enum (not listing-thumbs?) - :drop-space (and - (empty? components) - (some? groups) - (not dragging?))) - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - - (when ^boolean dragging? - [:div.grid-placeholder "\u00A0"]) - - (when (and (empty? components) - (some? groups)) - [:div.drop-space]) - - (for [component components] - [:& components-item - {:component component - :key (dm/str "component-" (:id component)) - :renaming renaming - :listing-thumbs? listing-thumbs? - :file-id file-id - :selected selected - :selected-full selected-full - :selected-paths selected-paths - :on-asset-click on-asset-click - :on-context-menu on-context-menu - :on-drag-start on-drag-start - :on-group on-group - :do-rename do-rename - :cancel-rename cancel-rename}])]) - - (for [[path-item content] groups] - (when-not (empty? path-item) - [:& components-group {:file-id file-id - :key path-item - :prefix (cph/merge-path-item prefix path-item) - :groups content - :open-groups open-groups - :renaming renaming - :listing-thumbs? listing-thumbs? - :selected selected - :on-asset-click on-asset-click - :on-drag-start on-drag-start - :do-rename do-rename - :cancel-rename cancel-rename - :on-rename-group on-rename-group - :on-ungroup on-ungroup - :on-context-menu on-context-menu - :selected-full selected-full}]))])])) - -(mf/defc components-section - {::mf/wrap-props false} - [{:keys [file-id local? components listing-thumbs? open? reverse-sort? selected - on-asset-click on-assets-delete on-clear-selection open-status-ref]}] - - (let [input-ref (mf/use-ref nil) - - state* (mf/use-state {}) - state (deref state*) - - current-component-id (:component-id state) - renaming? (:renaming state) - - open-groups-ref (mf/with-memo [open-status-ref] - (-> (l/in [:groups :components]) - (l/derived open-status-ref))) - - open-groups (mf/deref open-groups-ref) - - menu-state (mf/use-state initial-context-menu-state) - read-only? (mf/use-ctx ctx/workspace-read-only?) - - selected (:components selected) - selected-full (into #{} (filter #(contains? selected (:id %))) components) - multi-components? (> (count selected) 1) - multi-assets? (or (seq (:graphics selected)) - (seq (:colors selected)) - (seq (:typographies selected))) - - groups (mf/with-memo [components reverse-sort?] - (group-assets components reverse-sort?)) - - components-v2 (mf/use-ctx ctx/components-v2) - - add-component - (mf/use-fn - (fn [] - (st/emit! (dw/set-assets-section-open file-id :components true)) - (dom/click (mf/ref-val input-ref)))) - - on-file-selected - (mf/use-fn - (mf/deps file-id) - (fn [blobs] - (let [params {:file-id file-id - :blobs (seq blobs)}] - (st/emit! (dwm/upload-media-components params) - (ptk/event ::ev/event {::ev/name "add-asset-to-library" - :asset-type "components"}))))) - - on-duplicate - (mf/use-fn - (mf/deps current-component-id selected) - (fn [] - (if (empty? selected) - (st/emit! (dwl/duplicate-component file-id current-component-id)) - (let [undo-id (js/Symbol)] - (st/emit! (dwu/start-undo-transaction undo-id)) - (run! st/emit! (map (partial dwl/duplicate-component file-id) selected)) - (st/emit! (dwu/commit-undo-transaction undo-id)))))) - - on-delete - (mf/use-fn - (mf/deps current-component-id file-id multi-components? multi-assets? on-assets-delete) - (fn [] - (let [undo-id (js/Symbol)] - (if (or multi-components? multi-assets?) - (on-assets-delete) - (st/emit! (dwu/start-undo-transaction undo-id) - (dwl/delete-component {:id current-component-id}) - (dwl/sync-file file-id file-id :components current-component-id) - (dwu/commit-undo-transaction undo-id)))))) - - on-close-menu - (mf/use-fn #(swap! menu-state close-context-menu)) - - on-rename - (mf/use-fn #(swap! state* assoc :renaming true)) - - cancel-rename - (mf/use-fn #(swap! state* dissoc :renaming)) - - do-rename - (mf/use-fn - (mf/deps current-component-id) - (fn [new-name] - (swap! state* dissoc :renaming) - (st/emit! - (dwl/rename-component-and-main-instance current-component-id new-name)))) - - on-context-menu - (mf/use-fn - (mf/deps selected on-clear-selection read-only?) - (fn [component-id event] - (dom/prevent-default event) - (let [pos (dom/get-client-position event)] - (when (and local? (not read-only?)) - (when-not (contains? selected component-id) - (on-clear-selection)) - - (swap! state* assoc :component-id component-id) - (swap! menu-state open-context-menu pos))))) - - create-group - (mf/use-fn - (mf/deps current-component-id components selected on-clear-selection) - (fn [group-name] - (on-clear-selection) - (let [undo-id (js/Symbol)] - (st/emit! (dwu/start-undo-transaction undo-id)) - (run! st/emit! - (->> components - (filter #(if multi-components? - (contains? selected (:id %)) - (= current-component-id (:id %)))) - (map #(dwl/rename-component - (:id %) - (add-group % group-name))))) - (st/emit! (dwu/commit-undo-transaction undo-id))))) - - rename-group - (mf/use-fn - (mf/deps components) - (fn [path last-path] - (on-clear-selection) - (let [undo-id (js/Symbol)] - (st/emit! (dwu/start-undo-transaction undo-id)) - (run! st/emit! - (->> components - (filter #(str/starts-with? (:path %) path)) - (map #(dwl/rename-component - (:id %) - (rename-group % path last-path))))) - (st/emit! (dwu/commit-undo-transaction undo-id))))) - - on-group - (mf/use-fn - (mf/deps components selected create-group) - (fn [event] - (dom/stop-propagation event) - (modal/show! :name-group-dialog {:accept create-group}))) - - on-rename-group - (mf/use-fn - (mf/deps components) - (fn [event path last-path] - (dom/stop-propagation event) - (modal/show! :name-group-dialog {:path path - :last-path last-path - :accept rename-group}))) - - on-ungroup - (mf/use-fn - (mf/deps components) - (fn [path] - (on-clear-selection) - (let [undo-id (js/Symbol)] - (st/emit! (dwu/start-undo-transaction undo-id)) - (run! st/emit! - (->> components - (filter #(str/starts-with? (:path %) path)) - (map #(dwl/rename-component (:id %) (ungroup % path))))) - (st/emit! (dwu/commit-undo-transaction undo-id))))) - - on-drag-start - (mf/use-fn - (mf/deps file-id) - (fn [component event] - (dnd/set-data! event "penpot/component" {:file-id file-id - :component component}) - (dnd/set-allowed-effect! event "move"))) - - on-show-main - (mf/use-fn - (mf/deps current-component-id file-id) - (fn [event] - (dom/stop-propagation event) - (st/emit! (dw/go-to-main-instance file-id current-component-id)))) - - on-asset-click - (mf/use-fn (mf/deps groups on-asset-click) (partial on-asset-click groups))] - - [:& asset-section {:file-id file-id - :title (tr "workspace.assets.components") - :section :components - :assets-count (count components) - :open? open?} - (when local? - [:& asset-section-block {:role :title-button} - (when (and components-v2 (not read-only?)) - [:div.assets-button {:on-click add-component} - i/plus - [:& file-uploader {:accept cm/str-image-types - :multi true - :ref input-ref - :on-selected on-file-selected}]])]) - - [:& asset-section-block {:role :content} - [:& components-group {:file-id file-id - :prefix "" - :groups groups - :open-groups open-groups - :renaming (when ^boolean renaming? current-component-id) - :listing-thumbs? listing-thumbs? - :selected selected - :on-asset-click on-asset-click - :on-drag-start on-drag-start - :do-rename do-rename - :cancel-rename cancel-rename - :on-rename-group on-rename-group - :on-group on-group - :on-ungroup on-ungroup - :on-context-menu on-context-menu - :selected-full selected-full}] - (when local? - [:& assets-context-menu - {:on-close on-close-menu - :state @menu-state - :options [(when-not (or multi-components? multi-assets?) - [(tr "workspace.assets.rename") on-rename]) - (when-not multi-assets? - [(if components-v2 - (tr "workspace.assets.duplicate-main") - (tr "workspace.assets.duplicate")) on-duplicate]) - [(tr "workspace.assets.delete") on-delete] - (when-not multi-assets? - [(tr "workspace.assets.group") on-group]) - (when (and components-v2 (not multi-assets?)) - [(tr "workspace.shape.menu.show-main") on-show-main])]}])]])) - - -;; ---- Graphics section ---- - -(mf/defc graphics-item - [{:keys [object renaming listing-thumbs? selected-objects file-id - on-asset-click on-context-menu on-drag-start do-rename cancel-rename - selected-full selected-graphics-paths]}] - (let [item-ref (mf/use-ref) - visible? (h/use-visible item-ref :once? true) - object-id (:id object) - - dragging* (mf/use-state false) - dragging? (deref dragging*) - - read-only? (mf/use-ctx ctx/workspace-read-only?) - - on-drop - (mf/use-fn - (mf/deps object dragging* selected-objects selected-full selected-graphics-paths) - (fn [event] - (on-drop-asset event object dragging* selected-objects selected-full - selected-graphics-paths dwl/rename-media))) - - on-drag-enter - (mf/use-fn - (mf/deps object dragging* selected-objects selected-graphics-paths) - (fn [event] - (on-drag-enter-asset event object dragging* selected-objects selected-graphics-paths))) - - on-drag-leave - (mf/use-fn - (mf/deps dragging*) - (fn [event] - (on-drag-leave-asset event dragging*))) - - on-grahic-drag-start - (mf/use-fn - (mf/deps object file-id selected-objects item-ref on-drag-start read-only?) - (fn [event] - (if read-only? - (dom/prevent-default event) - (on-asset-drag-start event file-id object selected-objects item-ref :graphics on-drag-start)))) - - on-context-menu - (mf/use-fn - (mf/deps object-id) - (partial on-context-menu object-id)) - - on-asset-click - (mf/use-fn - (mf/deps object-id on-asset-click) - (partial on-asset-click object-id nil)) - - ] - - [:div {:ref item-ref - :class-name (dom/classnames - :selected (contains? selected-objects object-id) - :grid-cell listing-thumbs? - :enum-item (not listing-thumbs?)) - :draggable (not read-only?) - :on-click on-asset-click - :on-context-menu on-context-menu - :on-drag-start on-grahic-drag-start - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - - (when visible? - [:* - [:img {:src (when visible? (cf/resolve-file-media object true)) - :draggable false}] ;; Also need to add css pointer-events: none - - (let [renaming? (= renaming (:id object))] - [:* - [:& editable-label - {:class-name (dom/classnames - :cell-name listing-thumbs? - :item-name (not listing-thumbs?) - :editing renaming?) - :value (cph/merge-path-item (:path object) (:name object)) - :tooltip (cph/merge-path-item (:path object) (:name object)) - :display-value (:name object) - :editing? renaming? - :disable-dbl-click? true - :on-change do-rename - :on-cancel cancel-rename}] - - (when ^boolean dragging? - [:div.dragging])])])])) - -(mf/defc graphics-group - [{:keys [file-id prefix groups open-groups renaming listing-thumbs? selected-objects on-asset-click - on-drag-start do-rename cancel-rename on-rename-group on-ungroup - on-context-menu selected-full]}] - (let [group-open? (get open-groups prefix true) - - dragging* (mf/use-state false) - dragging? (deref dragging*) - - selected-paths - (mf/with-memo [selected-full] - (into #{} - (comp (map :path) (d/nilv "")) - selected-full)) - - on-drag-enter - (mf/use-fn - (mf/deps dragging* prefix selected-paths) - (fn [event] - (on-drag-enter-asset-group event dragging* prefix selected-paths))) - - on-drag-leave - (mf/use-fn - (mf/deps dragging*) - (fn [event] - (on-drag-leave-asset event dragging*))) - - on-drop - (mf/use-fn - (mf/deps dragging* prefix selected-paths selected-full) - (fn [event] - (on-drop-asset-group event dragging* prefix selected-paths selected-full dwl/rename-media)))] - - [:div {:on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - [:& asset-group-title {:file-id file-id - :section :graphics - :path prefix - :group-open? group-open? - :on-rename on-rename-group - :on-ungroup on-ungroup}] - (when group-open? - [:* - (let [objects (get groups "" [])] - [:div {:class-name (dom/classnames - :asset-grid listing-thumbs? - :asset-enum (not listing-thumbs?) - :drop-space (and - (empty? objects) - (some? groups) - (not dragging?))) - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - - (when ^boolean dragging? - [:div.grid-placeholder "\u00A0"]) - - (when (and (empty? objects) - (some? groups)) - [:div.drop-space]) - - (for [object objects] - [:& graphics-item {:key (dm/str "object-" (:id object)) - :file-id file-id - :object object - :renaming renaming - :listing-thumbs? listing-thumbs? - :selected-objects selected-objects - :on-asset-click on-asset-click - :on-context-menu on-context-menu - :on-drag-start on-drag-start - :do-rename do-rename - :cancel-rename cancel-rename - :selected-full selected-full - :selected-paths selected-paths}])]) - (for [[path-item content] groups] - (when-not (empty? path-item) - [:& graphics-group {:file-id file-id - :key path-item - :prefix (cph/merge-path-item prefix path-item) - :groups content - :open-groups open-groups - :renaming renaming - :listing-thumbs? listing-thumbs? - :selected-objects selected-objects - :on-asset-click on-asset-click - :on-drag-start on-drag-start - :do-rename do-rename - :cancel-rename cancel-rename - :on-rename-group on-rename-group - :on-ungroup on-ungroup - :on-context-menu on-context-menu - :selected-full selected-full - :selected-paths selected-paths}]))])])) - -(mf/defc graphics-section - {::mf/wrap-props false} - [{:keys [file-id project-id local? objects listing-thumbs? open? open-status-ref selected reverse-sort? - on-asset-click on-assets-delete on-clear-selection]}] - (let [input-ref (mf/use-ref nil) - state (mf/use-state {:renaming nil :object-id nil}) - - menu-state (mf/use-state initial-context-menu-state) - read-only? (mf/use-ctx ctx/workspace-read-only?) - - open-groups-ref (mf/with-memo [open-status-ref] - (-> (l/in [:groups :graphics]) - (l/derived open-status-ref))) - open-groups (mf/deref open-groups-ref) - - selected (:graphics selected) - selected-full (into #{} (filter #(contains? selected (:id %))) objects) - multi-objects? (> (count selected) 1) - multi-assets? (or (seq (:components selected)) - (seq (:colors selected)) - (seq (:typographies selected))) - - objects (mf/with-memo [objects] - (mapv dwl/extract-path-if-missing objects)) - - groups (mf/with-memo [objects reverse-sort?] - (group-assets objects reverse-sort?)) - - components-v2 (mf/use-ctx ctx/components-v2) - team-id (mf/use-ctx ctx/current-team-id) - - add-graphic - (mf/use-fn - (fn [] - (st/emit! (dw/set-assets-section-open file-id :graphics true)) - (dom/click (mf/ref-val input-ref)))) - - on-file-selected - (mf/use-fn - (mf/deps file-id project-id team-id) - (fn [blobs] - (let [params {:file-id file-id - :blobs (seq blobs)}] - (st/emit! (dwm/upload-media-asset params) - (ptk/event ::ev/event {::ev/name "add-asset-to-library" - :asset-type "graphics" - :file-id file-id - :project-id project-id - :team-id team-id}))))) - on-delete - (mf/use-fn - (mf/deps @state multi-objects? multi-assets?) - (fn [] - (if (or multi-objects? multi-assets?) - (on-assets-delete) - (st/emit! (dwl/delete-media {:id (:object-id @state)}))))) - - on-rename - (mf/use-fn - (fn [] - (swap! state (fn [state] - (assoc state :renaming (:component-id state)))))) - cancel-rename - (mf/use-fn - (fn [] - (swap! state assoc :renaming nil))) - - do-rename - (mf/use-fn - (mf/deps @state) - (fn [new-name] - (st/emit! (dwl/rename-media (:renaming @state) new-name)) - (swap! state assoc :renaming nil))) - - on-context-menu - (mf/use-fn - (mf/deps selected on-clear-selection read-only?) - (fn [object-id event] - (dom/prevent-default event) - (let [pos (dom/get-client-position event)] - (when (and local? (not read-only?)) - (when-not (contains? selected object-id) - (on-clear-selection)) - (swap! state assoc :object-id object-id) - (swap! menu-state open-context-menu pos))))) - - on-close-menu - (mf/use-fn - (fn [] - (swap! menu-state close-context-menu))) - - create-group - (mf/use-fn - (mf/deps objects selected on-clear-selection (:object-id @state)) - (fn [group-name] - (on-clear-selection) - (let [undo-id (js/Symbol)] - (st/emit! (dwu/start-undo-transaction undo-id)) - (run! st/emit! - (->> objects - (filter #(if multi-objects? - (contains? selected (:id %)) - (= (:object-id @state) (:id %)))) - (map #(dwl/rename-media (:id %) (add-group % group-name))))) - (st/emit! (dwu/commit-undo-transaction undo-id))))) - - rename-group - (mf/use-fn - (mf/deps objects) - (fn [path last-path] - (on-clear-selection) - (let [undo-id (js/Symbol)] - (st/emit! (dwu/start-undo-transaction undo-id)) - (run! st/emit! - (->> objects - (filter #(str/starts-with? (:path %) path)) - (map #(dwl/rename-media (:id %) (rename-group % path last-path))))) - (st/emit! (dwu/commit-undo-transaction undo-id))))) - - on-group - (mf/use-fn - (mf/deps objects selected create-group) - (fn [event] - (dom/stop-propagation event) - (modal/show! :name-group-dialog {:accept create-group}))) - - on-rename-group - (mf/use-fn - (mf/deps objects) - (fn [event path last-path] - (dom/stop-propagation event) - (modal/show! :name-group-dialog {:path path - :last-path last-path - :accept rename-group}))) - on-ungroup - (mf/use-fn - (mf/deps objects) - (fn [path] - (on-clear-selection) - (let [undo-id (js/Symbol)] - (st/emit! (dwu/start-undo-transaction undo-id)) - (run! st/emit! - (->> objects - (filter #(str/starts-with? (:path %) path)) - (map #(dwl/rename-media (:id %) (ungroup % path))))) - (st/emit! (dwu/commit-undo-transaction undo-id))))) - - on-drag-start - (mf/use-fn - (fn [{:keys [name id mtype]} event] - (dnd/set-data! event "text/asset-id" (str id)) - (dnd/set-data! event "text/asset-name" name) - (dnd/set-data! event "text/asset-type" mtype) - (dnd/set-allowed-effect! event "move"))) - - on-asset-click - (mf/use-fn (mf/deps groups on-asset-click) (partial on-asset-click groups))] - - [:& asset-section {:file-id file-id - :title (tr "workspace.assets.graphics") - :section :graphics - :assets-count (count objects) - :open? open?} - (when local? - [:& asset-section-block {:role :title-button} - (when (and (not components-v2) (not read-only?)) - [:div.assets-button {:on-click add-graphic} - i/plus - [:& file-uploader {:accept cm/str-image-types - :multi true - :ref input-ref - :on-selected on-file-selected}]])]) - - [:& asset-section-block {:role :content} - [:& graphics-group {:file-id file-id - :prefix "" - :groups groups - :open-groups open-groups - :renaming (:renaming @state) - :listing-thumbs? listing-thumbs? - :selected selected - :on-asset-click on-asset-click - :on-drag-start on-drag-start - :do-rename do-rename - :cancel-rename cancel-rename - :on-rename-group on-rename-group - :on-ungroup on-ungroup - :on-context-menu on-context-menu - :selected-full selected-full}] - (when local? - [:& assets-context-menu - {:on-close on-close-menu - :state @menu-state - :options [(when-not (or multi-objects? multi-assets?) - [(tr "workspace.assets.rename") on-rename]) - [(tr "workspace.assets.delete") on-delete] - (when-not multi-assets? - [(tr "workspace.assets.group") on-group])]}])]])) - - -;; ---- Colors section ---- - -(mf/defc color-item - {::mf/wrap-props false} - [{:keys [color local? file-id selected multi-colors? multi-assets? - on-asset-click on-assets-delete on-clear-selection on-group - selected-full selected-paths move-color]}] - - (let [color (mf/with-memo [color file-id] - (cond-> color - (:value color) (assoc :color (:value color) :opacity 1) - (:value color) (dissoc :value) - true (assoc :file-id file-id))) - - - color-id (:id color) - - item-ref (mf/use-ref) - dragging* (mf/use-state false) - dragging? (deref dragging*) - - rename? (= (:color-for-rename @refs/workspace-local) color-id) - input-ref (mf/use-ref) - - editing* (mf/use-state rename?) - editing? (deref editing*) - - menu-state (mf/use-state initial-context-menu-state) - read-only? (mf/use-ctx ctx/workspace-read-only?) - - default-name (cond - (:gradient color) (uc/gradient-type->string (dm/get-in color [:gradient :type])) - (:color color) (:color color) - :else (:value color)) - - apply-color - (mf/use-fn - (mf/deps color) - (fn [event] - (st/emit! (dc/apply-color-from-palette (merge uc/empty-color color) (kbd/alt? event))))) - - rename-color - (mf/use-fn - (mf/deps file-id color-id) - (fn [name] - (st/emit! (dwl/rename-color file-id color-id name)))) - - edit-color - (mf/use-fn - (mf/deps color file-id) - (fn [attrs] - (let [name (cph/merge-path-item (:path color) (:name color)) - color (-> attrs - (assoc :id (:id color)) - (assoc :file-id file-id) - (assoc :name name))] - (st/emit! (dwl/update-color color file-id))))) - - delete-color - (mf/use-fn - (mf/deps multi-colors? multi-assets? file-id color-id) - (fn [] - (if (or multi-colors? multi-assets?) - (on-assets-delete) - (let [undo-id (js/Symbol)] - (st/emit! (dwu/start-undo-transaction undo-id) - (dwl/delete-color color) - (dwl/sync-file file-id file-id :colors color-id) - (dwu/commit-undo-transaction undo-id)))))) - - rename-color-clicked - (mf/use-fn - (mf/deps read-only? local?) - (fn [event] - (when (and local? (not read-only?)) - (dom/prevent-default event) - (reset! editing* true)))) - - input-blur - (mf/use-fn - (mf/deps rename-color) - (fn [event] - (let [target (dom/event->target event) - name (dom/get-value target)] - (rename-color name) - (st/emit! dwl/clear-color-for-rename) - (reset! editing* false)))) - - input-key-down - (mf/use-fn - (mf/deps input-blur) - (fn [event] - (when (kbd/esc? event) - (st/emit! dwl/clear-color-for-rename) - (reset! editing* false)) - (when (kbd/enter? event) - (input-blur event)))) - - edit-color-clicked - (mf/use-fn - (mf/deps edit-color color) - (fn [event] - (modal/show! :colorpicker - {:x (.-clientX ^js event) - :y (.-clientY ^js event) - :on-accept edit-color - :data color - :position :right}))) - - on-context-menu - (mf/use-fn - (mf/deps color-id selected on-clear-selection read-only?) - (fn [event] - (dom/prevent-default event) - (let [pos (dom/get-client-position event)] - (when (and local? (not read-only?)) - (when-not (contains? selected color-id) - (on-clear-selection)) - (swap! menu-state open-context-menu pos))))) - - on-close-menu - (mf/use-fn - (fn [] - (swap! menu-state close-context-menu))) - - on-drop - (mf/use-fn - (mf/deps color dragging* selected selected-full selected-paths move-color) - (fn [event] - (on-drop-asset event color dragging* selected selected-full - selected-paths move-color))) - - on-drag-enter - (mf/use-fn - (mf/deps color dragging* selected selected-paths) - (fn [event] - (on-drag-enter-asset event color dragging* selected selected-paths))) - - on-drag-leave - (mf/use-fn - (mf/deps dragging*) - (fn [event] - (on-drag-leave-asset event dragging*))) - - on-color-drag-start - (mf/use-fn - (mf/deps color file-id selected item-ref read-only?) - (fn [event] - (if read-only? - (dom/prevent-default event) - (on-asset-drag-start event file-id color selected item-ref :colors identity)))) - - on-click - (mf/use-fn - (mf/deps color-id apply-color on-asset-click) - (partial on-asset-click color-id apply-color))] - - (mf/with-effect [editing?] - (when editing? - (let [input (mf/ref-val input-ref)] - (dom/select-text! input) - nil))) - - [:div.asset-list-item - {:class-name (dom/classnames - :selected (contains? selected (:id color))) - :on-context-menu on-context-menu - :on-click (when-not editing? on-click) - :ref item-ref - :draggable (and (not read-only?) (not editing?)) - :on-drag-start on-color-drag-start - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - - [:& bc/color-bullet {:color color}] - - (if ^boolean editing? - [:input.element-name - {:type "text" - :ref input-ref - :on-blur input-blur - :on-key-down input-key-down - :auto-focus true - :default-value (cph/merge-path-item (:path color) (:name color))}] - - [:div.name-block {:title (:name color) - :on-double-click rename-color-clicked} - (:name color) - (when-not (= (:name color) default-name) - [:span default-name])]) - - (when local? - [:& assets-context-menu - {:on-close on-close-menu - :state @menu-state - :options [(when-not (or multi-colors? multi-assets?) - [(tr "workspace.assets.rename") rename-color-clicked]) - (when-not (or multi-colors? multi-assets?) - [(tr "workspace.assets.edit") edit-color-clicked]) - [(tr "workspace.assets.delete") delete-color] - (when-not multi-assets? - [(tr "workspace.assets.group") (on-group (:id color))])]}]) - - (when ^boolean dragging? - [:div.dragging])])) - -(mf/defc colors-group - [{:keys [file-id prefix groups open-groups local? selected - multi-colors? multi-assets? on-asset-click on-assets-delete - on-clear-selection on-group on-rename-group on-ungroup colors - selected-full]}] - (let [group-open? (get open-groups prefix true) - - dragging* (mf/use-state false) - dragging? (deref dragging*) - - selected-paths (mf/with-memo [selected-full] - (into #{} - (comp (map :path) (d/nilv "")) - selected-full)) - - move-color - (mf/use-fn (mf/deps file-id) (partial dwl/rename-color file-id)) - - on-drag-enter - (mf/use-fn - (mf/deps dragging* prefix selected-paths) - (fn [event] - (on-drag-enter-asset-group event dragging* prefix selected-paths))) - - on-drag-leave - (mf/use-fn - (mf/deps dragging*) - (fn [event] - (on-drag-leave-asset event dragging*))) - - on-drop - (mf/use-fn - (mf/deps dragging* prefix selected-paths selected-full move-color) - (fn [event] - (on-drop-asset-group event dragging* prefix selected-paths selected-full move-color)))] - - [:div {:on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - [:& asset-group-title {:file-id file-id - :section :colors - :path prefix - :group-open? group-open? - :on-rename on-rename-group - :on-ungroup on-ungroup}] - (when group-open? - [:* - (let [colors (get groups "" [])] - [:div.asset-list {:on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - - (when ^boolean dragging? - [:div.grid-placeholder "\u00A0"]) - - (when (and (empty? colors) - (some? groups)) - [:div.drop-space]) - - (for [color colors] - [:& color-item {:key (dm/str (:id color)) - :color color - :file-id file-id - :local? local? - :selected selected - :multi-colors? multi-colors? - :multi-assets? multi-assets? - :on-asset-click on-asset-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection - :on-group on-group - :colors colors - :selected-full selected-full - :selected-paths selected-paths - :move-color move-color}])]) - - (for [[path-item content] groups] - (when-not (empty? path-item) - [:& colors-group {:file-id file-id - :prefix (cph/merge-path-item prefix path-item) - :key (dm/str "group-" path-item) - :groups content - :open-groups open-groups - :local? local? - :selected selected - :multi-colors? multi-colors? - :multi-assets? multi-assets? - :on-asset-click on-asset-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection - :on-group on-group - :on-rename-group on-rename-group - :on-ungroup on-ungroup - :colors colors - :selected-full selected-full}]))])])) - -(mf/defc colors-section - [{:keys [file-id local? colors open? open-status-ref selected reverse-sort? - on-asset-click on-assets-delete on-clear-selection] :as props}] - - (let [selected (:colors selected) - selected-full (mf/with-memo [selected colors] - (into #{} (filter #(contains? selected (:id %))) colors)) - - open-groups-ref (mf/with-memo [open-status-ref] - (-> (l/in [:groups :colors]) - (l/derived open-status-ref))) - open-groups (mf/deref open-groups-ref) - - multi-colors? (> (count selected) 1) - multi-assets? (or (seq (:components selected)) - (seq (:graphics selected)) - (seq (:typographies selected))) - - groups (mf/with-memo [colors reverse-sort?] - (group-assets colors reverse-sort?)) - - read-only? (mf/use-ctx ctx/workspace-read-only?) - - add-color - (mf/use-fn - (fn [value _] - (st/emit! (dwl/add-color value)))) - - add-color-clicked - (mf/use-fn - (fn [event] - (let [position (dom/get-client-position event)] - (st/emit! (dc/select-color position add-color))))) - - create-group - (mf/use-fn - (mf/deps colors selected on-clear-selection file-id) - (fn [color-id] - (fn [group-name] - (on-clear-selection) - (let [undo-id (js/Symbol)] - (st/emit! (dwu/start-undo-transaction undo-id)) - (run! st/emit! - (->> colors - (filter #(if multi-colors? - (contains? selected (:id %)) - (= color-id (:id %)))) - (map #(dwl/update-color - (assoc % :name - (add-group % group-name)) - file-id)))) - (st/emit! (dwu/commit-undo-transaction undo-id)))))) - - rename-group - (mf/use-fn - (mf/deps colors) - (fn [path last-path] - (on-clear-selection) - (let [undo-id (js/Symbol)] - (st/emit! (dwu/start-undo-transaction undo-id)) - (run! st/emit! - (->> colors - (filter #(str/starts-with? (:path %) path)) - (map #(dwl/update-color - (assoc % :name - (rename-group % path last-path)) - file-id)))) - (st/emit! (dwu/commit-undo-transaction undo-id))))) - - on-group - (mf/use-fn - (mf/deps colors selected) - (fn [color-id] - (fn [event] - (dom/stop-propagation event) - (modal/show! :name-group-dialog {:accept (create-group color-id)})))) - - on-rename-group - (mf/use-fn - (mf/deps colors) - (fn [event path last-path] - (dom/stop-propagation event) - (modal/show! :name-group-dialog {:path path - :last-path last-path - :accept rename-group}))) - on-ungroup - (mf/use-fn - (mf/deps colors) - (fn [path] - (on-clear-selection) - (let [undo-id (js/Symbol)] - (st/emit! (dwu/start-undo-transaction undo-id)) - (apply st/emit! - (->> colors - (filter #(str/starts-with? (:path %) path)) - (map #(dwl/update-color - (assoc % :name - (ungroup % path)) - file-id)))) - (st/emit! (dwu/commit-undo-transaction undo-id))))) - - on-asset-click - (mf/use-fn (mf/deps groups on-asset-click) (partial on-asset-click groups))] - - [:& asset-section {:file-id file-id - :title (tr "workspace.assets.colors") - :section :colors - :assets-count (count colors) - :open? open?} - (when local? - [:& asset-section-block {:role :title-button} - (when-not read-only? - [:div.assets-button {:on-click add-color-clicked} - i/plus])]) - - [:& asset-section-block {:role :content} - [:& colors-group {:file-id file-id - :prefix "" - :groups groups - :open-groups open-groups - :local? local? - :selected selected - :multi-colors? multi-colors? - :multi-assets? multi-assets? - :on-asset-click on-asset-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection - :on-group on-group - :on-rename-group on-rename-group - :on-ungroup on-ungroup - :colors colors - :selected-full selected-full}]]])) - -;; ---- Typography section ---- - -(mf/defc typography-item - {::mf/wrap-props false} - [{:keys [typography file-id local? handle-change selected apply-typography editing-id on-asset-click - on-context-menu selected-full selected-paths move-typography rename?]}] - (let [item-ref (mf/use-ref) - typography-id (:id typography) - - dragging* (mf/use-state false) - dragging? (deref dragging*) - - read-only? (mf/use-ctx ctx/workspace-read-only?) - editing? (= editing-id (:id typography)) - - open* (mf/use-state editing?) - open? (deref open*) - - on-drop - (mf/use-fn - (mf/deps typography dragging* selected selected-full selected-paths move-typography) - (fn [event] - (on-drop-asset event typography dragging* selected selected-full - selected-paths move-typography))) - - on-drag-enter - (mf/use-fn - (mf/deps typography dragging* selected selected-paths) - (fn [event] - (on-drag-enter-asset event typography dragging* selected selected-paths))) - - on-drag-leave - (mf/use-fn - (mf/deps dragging*) - (fn [event] - (on-drag-leave-asset event dragging*))) - - on-typography-drag-start - (mf/use-fn - (mf/deps typography file-id selected item-ref read-only?) - (fn [event] - (if read-only? - (dom/prevent-default event) - (on-asset-drag-start event file-id typography selected item-ref :typographies identity)))) - - on-context-menu - (mf/use-fn - (mf/deps on-context-menu typography-id) - (partial on-context-menu typography-id)) - - handle-change - (mf/use-fn - (mf/deps typography) - (partial handle-change typography)) - - apply-typography - (mf/use-fn - (mf/deps typography) - (partial apply-typography typography)) - - on-asset-click - (mf/use-fn - (mf/deps typography apply-typography on-asset-click) - (partial on-asset-click typography-id apply-typography)) - - ] - - [:div.typography-container {:ref item-ref - :draggable (and (not read-only?) (not open?)) - :on-drag-start on-typography-drag-start - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - [:& typography-entry - {:typography typography - :local? local? - :on-context-menu on-context-menu - :on-change handle-change - :selected? (contains? selected typography-id) - :on-click on-asset-click - :editing? editing? - :focus-name? rename? - :external-open* open* - :file-id file-id - }] - - (when ^boolean dragging? - [:div.dragging])])) - -(mf/defc typographies-group - {::mf/wrap-props false} - [{:keys [file-id prefix groups open-groups file local? selected local-data - editing-id on-asset-click handle-change apply-typography on-rename-group - on-ungroup on-context-menu selected-full]}] - (let [group-open? (get open-groups prefix true) - dragging* (mf/use-state false) - dragging? (deref dragging*) - - selected-paths (mf/with-memo [selected-full] - (into #{} - (comp (map :path) (d/nilv "")) - selected-full)) - move-typography - (mf/use-fn - (mf/deps file-id) - (partial dwl/rename-typography file-id)) - - on-drag-enter - (mf/use-fn - (mf/deps dragging* prefix selected-paths) - (fn [event] - (on-drag-enter-asset-group event dragging* prefix selected-paths))) - - on-drag-leave - (mf/use-fn - (mf/deps dragging*) - (fn [event] - (on-drag-leave-asset event dragging*))) - - on-drop - (mf/use-fn - (mf/deps dragging* prefix selected-paths selected-full move-typography) - (fn [event] - (on-drop-asset-group event dragging* prefix selected-paths selected-full move-typography)))] - - [:div {:on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - [:& asset-group-title {:file-id file-id - :section :typographies - :path prefix - :group-open? group-open? - :on-rename on-rename-group - :on-ungroup on-ungroup}] - (when group-open? - [:* - (let [typographies (get groups "" [])] - [:div.asset-list {:on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - - (when ^boolean dragging? - [:div.grid-placeholder "\u00A0"]) - - (when (and - (empty? typographies) - (some? groups)) - [:div.drop-space]) - (for [{:keys [id] :as typography} typographies] - [:& typography-item {:typography typography - :key (dm/str "typography-" id) - :file-id file-id - :local? local? - :handle-change handle-change - :selected selected - :apply-typography apply-typography - :editing-id editing-id - :rename? (= (:rename-typography local-data) id) - :on-asset-click on-asset-click - :on-context-menu on-context-menu - :selected-full selected-full - :selected-paths selected-paths - :move-typography move-typography}])]) - - (for [[path-item content] groups] - (when-not (empty? path-item) - [:& typographies-group {:file-id file-id - :prefix (cph/merge-path-item prefix path-item) - :key (dm/str "group-" path-item) - :groups content - :open-groups open-groups - :file file - :local? local? - :selected selected - :editing-id editing-id - :local-data local-data - :on-asset-click on-asset-click - :handle-change handle-change - :apply-typography apply-typography - :on-rename-group on-rename-group - :on-ungroup on-ungroup - :on-context-menu on-context-menu - :selected-full selected-full}]))])])) - -(mf/defc typographies-section - {::mf/wrap-props false} - [{:keys [file file-id local? typographies open? open-status-ref selected reverse-sort? - on-asset-click on-assets-delete on-clear-selection]}] - (let [state (mf/use-state {:detail-open? false :id nil}) - local-data (mf/deref lens:typography-section-state) - - read-only? (mf/use-ctx ctx/workspace-read-only?) - menu-state (mf/use-state initial-context-menu-state) - typographies (mf/with-memo [typographies] - (mapv dwl/extract-path-if-missing typographies)) - - groups (mf/with-memo [typographies reverse-sort?] - (group-assets typographies reverse-sort?)) - - selected (:typographies selected) - selected-full (mf/with-memo [selected typographies] - (into #{} (filter #(contains? selected (:id %))) typographies)) - - multi-typographies? (> (count selected) 1) - multi-assets? (or (seq (:components selected)) - (seq (:graphics selected)) - (seq (:colors selected))) - - open-groups-ref (mf/with-memo [open-status-ref] - (-> (l/in [:groups :typographies]) - (l/derived open-status-ref))) - - open-groups (mf/deref open-groups-ref) - - add-typography - (mf/use-fn - (mf/deps file-id) - (fn [_] - (st/emit! (dwt/add-typography file-id)))) - - handle-change - (mf/use-fn - (mf/deps file-id) - (fn [typography changes] - (st/emit! (dwl/update-typography (merge typography changes) file-id)))) - - apply-typography - (mf/use-fn - (mf/deps file-id) - (fn [typography _event] - (st/emit! (dwt/apply-typography typography file-id)))) - - create-group - (mf/use-fn - (mf/deps typographies selected on-clear-selection file-id (:id @state)) - (fn [group-name] - (on-clear-selection) - (let [undo-id (js/Symbol)] - (st/emit! (dwu/start-undo-transaction undo-id)) - (run! st/emit! - (->> typographies - (filter #(if multi-typographies? - (contains? selected (:id %)) - (= (:id @state) (:id %)))) - (map #(dwl/update-typography - (assoc % :name - (add-group % group-name)) - file-id)))) - (st/emit! (dwu/commit-undo-transaction undo-id))))) - - rename-group - (mf/use-fn - (mf/deps typographies) - (fn [path last-path] - (on-clear-selection) - (let [undo-id (js/Symbol)] - (st/emit! (dwu/start-undo-transaction undo-id)) - (run! st/emit! - (->> typographies - (filter #(str/starts-with? (:path %) path)) - (map #(dwl/update-typography - (assoc % :name - (rename-group % path last-path)) - file-id)))) - (st/emit! (dwu/commit-undo-transaction undo-id))))) - - on-group - (mf/use-fn - (mf/deps typographies selected create-group) - (fn [event] - (dom/stop-propagation event) - (modal/show! :name-group-dialog {:accept create-group}))) - - on-rename-group - (mf/use-fn - (mf/deps typographies) - (fn [event path last-path] - (dom/stop-propagation event) - (modal/show! :name-group-dialog {:path path - :last-path last-path - :accept rename-group}))) - on-ungroup - (mf/use-fn - (mf/deps typographies) - (fn [path] - (on-clear-selection) - (let [undo-id (js/Symbol)] - (st/emit! (dwu/start-undo-transaction undo-id)) - (apply st/emit! - (->> typographies - (filter #(str/starts-with? (:path %) path)) - (map #(dwl/rename-typography - file-id - (:id %) - (ungroup % path))))) - (st/emit! (dwu/commit-undo-transaction undo-id))))) - - on-context-menu - (mf/use-fn - (mf/deps selected on-clear-selection read-only?) - (fn [id event] - (dom/prevent-default event) - (let [pos (dom/get-client-position event)] - (when (and local? (not read-only?)) - (when-not (contains? selected id) - (on-clear-selection)) - (swap! state assoc :id id) - (swap! menu-state open-context-menu pos))))) - - on-close-menu - (mf/use-fn - (fn [] - (swap! menu-state close-context-menu))) - - handle-rename-typography-clicked - (fn [] - (st/emit! #(assoc-in % [:workspace-global :rename-typography] (:id @state)))) - - handle-edit-typography-clicked - (fn [] - (st/emit! #(assoc-in % [:workspace-global :edit-typography] (:id @state)))) - - handle-delete-typography - (mf/use-fn - (mf/deps @state multi-typographies? multi-assets?) - (fn [] - (let [undo-id (js/Symbol)] - (if (or multi-typographies? multi-assets?) - (on-assets-delete) - (st/emit! (dwu/start-undo-transaction undo-id) - (dwl/delete-typography (:id @state)) - (dwl/sync-file file-id file-id :typographies (:id @state)) - (dwu/commit-undo-transaction undo-id)))))) - - editing-id (or (:rename-typography local-data) - (:edit-typography local-data)) - - on-asset-click - (mf/use-fn - (mf/deps groups on-asset-click) - (partial on-asset-click groups))] - - (mf/use-effect - (mf/deps local-data) - (fn [] - (when (:rename-typography local-data) - (st/emit! #(update % :workspace-global dissoc :rename-typography))) - (when (:edit-typography local-data) - (st/emit! #(update % :workspace-global dissoc :edit-typography))))) - - [:& asset-section {:file-id file-id - :title (tr "workspace.assets.typography") - :section :typographies - :assets-count (count typographies) - :open? open?} - (when local? - [:& asset-section-block {:role :title-button} - (when-not read-only? - [:div.assets-button {:on-click add-typography} - i/plus])]) - - [:& asset-section-block {:role :content} - [:& typographies-group {:file-id file-id - :prefix "" - :groups groups - :open-groups open-groups - :state state - :file file - :local? local? - :selected selected - :editing-id editing-id - :local-data local-data - :on-asset-click on-asset-click - :handle-change handle-change - :apply-typography apply-typography - :on-rename-group on-rename-group - :on-ungroup on-ungroup - :on-context-menu on-context-menu - :selected-full selected-full}] - - (when local? - [:& assets-context-menu - {:on-close on-close-menu - :state @menu-state - :options [(when-not (or multi-typographies? multi-assets?) - [(tr "workspace.assets.rename") handle-rename-typography-clicked]) - (when-not (or multi-typographies? multi-assets?) - [(tr "workspace.assets.edit") handle-edit-typography-clicked]) - [(tr "workspace.assets.delete") handle-delete-typography] - (when-not multi-assets? - [(tr "workspace.assets.group") on-group])]}])]])) - - -;; --- Assets toolsection ---- - -(defn- apply-filters - [coll {:keys [ordering term] :as filters}] - (let [reverse? (= :desc ordering) - comp-fn (if ^boolean reverse? > <)] - (->> coll - (filter (fn [item] - (or (matches-search (:name item "!$!") term) - (matches-search (:value item "!$!") term)))) - ; Sort by folder order, but - ; putting all "root" items - ; always first, independently - ; of sort order. - (sort-by #(str/lower (cph/merge-path-item (if (empty? (:path %)) - (if reverse? "z" "a") - (:path %)) - (:name %))) - comp-fn)))) - - -(mf/defc file-library-title - {::mf/wrap-props false} - [{:keys [open? local? shared? project-id file-id page-id file-name]}] - (let [router (mf/deref refs/router) - url (rt/resolve router :workspace - {:project-id project-id - :file-id file-id} - {:page-id page-id}) - - toggle-open - (mf/use-fn - (mf/deps file-id open?) - (fn [] - (st/emit! (dw/set-assets-section-open file-id :library (not open?))))) - ] - - [:div.tool-window-bar.library-bar - {:on-click toggle-open} - [:div.collapse-library - {:class (dom/classnames :open open?)} - i/arrow-slide] - - (if local? - [:* - [:span file-name " (" (tr "workspace.assets.local-library") ")"] - (when shared? - [:span.tool-badge (tr "workspace.assets.shared")])] - [:* - [:span file-name] - [:span.tool-link.tooltip.tooltip-left {:alt "Open library file"} - [:a {:href (str "#" url) - :target "_blank" - :on-click dom/stop-propagation} - i/chain]]])])) - -(mf/defc file-library-content - {::mf/wrap-props false} - [{:keys [file local? open-status-ref on-clear-selection]}] - (let [components-v2 (mf/use-ctx ctx/components-v2) - open-status (mf/deref open-status-ref) - - file-id (:id file) - project-id (:project-id file) - - filters (mf/use-ctx ctx:filters) - filters-section (:section filters) - filters-term (:term filters) - filters-ordering (:ordering filters) - filters-list-style (:list-style filters) - - reverse-sort? (= :desc filters-ordering) - listing-thumbs? (= :thumbs filters-list-style) - - toggle-ordering (mf/use-ctx ctx:toggle-ordering) - toggle-list-style (mf/use-ctx ctx:toggle-list-style) - - library-ref (mf/with-memo [file-id] - (create-file-library-ref file-id)) - - library (mf/deref library-ref) - colors (:colors library) - components (:components library) - media (:media library) - typographies (:typographies library) - - colors (mf/with-memo [filters colors] - (apply-filters colors filters)) - components (mf/with-memo [filters components] - (apply-filters components filters)) - media (mf/with-memo [filters media] - (apply-filters media filters)) - typographies (mf/with-memo [filters typographies] - (apply-filters typographies filters)) - - show-components? (and (or (= filters-section :all) - (= filters-section :components)) - (or (pos? (count components)) - (str/empty? filters-term))) - show-graphics? (and (or (= filters-section :all) - (= filters-section :graphics)) - (or (pos? (count media)) - (and (str/empty? filters-term) - (not components-v2)))) - show-colors? (and (or (= filters-section :all) - (= filters-section :colors)) - (or (> (count colors) 0) - (str/empty? filters-term))) - show-typography? (and (or (= filters-section :all) - (= filters-section :typographies)) - (or (pos? (count typographies)) - (str/empty? filters-term))) - - - selected-lens (mf/with-memo [file-id] - (-> (l/key file-id) - (l/derived lens:selected))) - selected (mf/deref selected-lens) - selected-count (+ (count (get selected :components)) - (count (get selected :graphics)) - (count (get selected :colors)) - (count (get selected :typographies))) - - extend-selected - (fn [type asset-groups asset-id] - (letfn [(flatten-groups [groups] - (reduce concat [(get groups "" []) - (into [] - (->> (filter #(seq (first %)) groups) - (map second) - (mapcat flatten-groups)))]))] - - (let [selected' (get selected type)] - (if (zero? (count selected')) - (st/emit! (dw/select-single-asset file-id asset-id type)) - (let [all-assets (flatten-groups asset-groups) - click-index (d/index-of-pred all-assets #(= (:id %) asset-id)) - first-index (->> (get selected type) - (map (fn [asset] (d/index-of-pred all-assets #(= (:id %) asset)))) - (sort) - (first)) - - min-index (min first-index click-index) - max-index (max first-index click-index) - ids (->> (d/enumerate all-assets) - (into #{} (comp (filter #(<= min-index (first %) max-index)) - (map (comp :id second)))))] - - (st/emit! (dw/select-assets file-id ids type))))))) - - on-asset-click - (mf/use-fn - (mf/deps file-id extend-selected) - (fn [asset-type asset-groups asset-id default-click event] - (cond - (kbd/mod? event) - (do - (dom/stop-propagation event) - (st/emit! (dw/toggle-selected-assets file-id asset-id asset-type))) - - (kbd/shift? event) - (do - (dom/stop-propagation event) - (extend-selected asset-type asset-groups asset-id)) - - :else - (when default-click - (default-click event))))) - - on-component-click - (mf/use-fn (mf/deps on-asset-click) (partial on-asset-click :components)) - - on-graphics-click - (mf/use-fn (mf/deps on-asset-click) (partial on-asset-click :graphics)) - - on-colors-click - (mf/use-fn (mf/deps on-asset-click) (partial on-asset-click :colors)) - - on-typography-click - (mf/use-fn (mf/deps on-asset-click) (partial on-asset-click :typographies)) - - on-assets-delete - (mf/use-fn - (mf/deps selected file-id) - (fn [] - (let [undo-id (js/Symbol)] - (st/emit! (dwu/start-undo-transaction undo-id)) - (run! st/emit! (map #(dwl/delete-component {:id %}) - (:components selected))) - (run! st/emit! (map #(dwl/delete-media {:id %}) - (:graphics selected))) - (run! st/emit! (map #(dwl/delete-color {:id %}) - (:colors selected))) - (run! st/emit! (map #(dwl/delete-typography %) - (:typographies selected))) - - (when (or (seq (:components selected)) - (seq (:colors selected)) - (seq (:typographies selected))) - (st/emit! (dwl/sync-file file-id file-id))) - - (st/emit! (dwu/commit-undo-transaction undo-id)))))] - - [:div.tool-window-content - [:div.listing-options - (when (> selected-count 0) - [:span.selected-count - (tr "workspace.assets.selected-count" (i18n/c selected-count))]) - [:div.listing-option-btn.first {:on-click toggle-ordering} - (if reverse-sort? - i/sort-ascending - i/sort-descending)] - [:div.listing-option-btn {:on-click toggle-list-style} - (if listing-thumbs? - i/listing-enum - i/listing-thumbs)]] - - (when ^boolean show-components? - [:& components-section - {:file-id file-id - :local? local? - :components components - :listing-thumbs? listing-thumbs? - :open? (get open-status :components true) - :open-status-ref open-status-ref - :reverse-sort? reverse-sort? - :selected selected - :on-asset-click on-component-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection}]) - - (when ^boolean show-graphics? - [:& graphics-section - {:file-id file-id - :project-id project-id - :local? local? - :objects media - :listing-thumbs? listing-thumbs? - :open? (get open-status :graphics true) - :open-status-ref open-status-ref - :reverse-sort? reverse-sort? - :selected selected - :on-asset-click on-graphics-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection}]) - - (when ^boolean show-colors? - [:& colors-section - {:file-id file-id - :local? local? - :colors colors - :open? (get open-status :colors true) - :open-status-ref open-status-ref - :reverse-sort? reverse-sort? - :selected selected - :on-asset-click on-colors-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection}]) - - (when ^boolean show-typography? - [:& typographies-section - {:file file - :file-id (:id file) - :local? local? - :typographies typographies - :open? (get open-status :typographies true) - :open-status-ref open-status-ref - :reverse-sort? reverse-sort? - :selected selected - :on-asset-click on-typography-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection}]) - - (when (and (not ^boolean show-components?) - (not ^boolean show-graphics?) - (not ^boolean show-colors?) - (not ^boolean show-typography?)) - [:div.asset-section - [:div.asset-title (tr "workspace.assets.not-found")]])])) - -(mf/defc file-library - {::mf/wrap-props false} - [{:keys [file local? default-open? filters]}] - (let [file-id (:id file) - file-name (:name file) - shared? (:is-shared file) - project-id (:project-id file) - page-id (dm/get-in file [:data :pages 0]) - - open-status-ref (mf/with-memo [file-id] - (-> (l/key file-id) - (l/derived lens:open-status))) - open-status (mf/deref open-status-ref) - open? (d/nilv (:library open-status) default-open?) - - unselect-all - (mf/use-fn - (mf/deps file-id) - (fn [] - (st/emit! (dw/unselect-all-assets file-id)))) - - ] - - [:div.tool-window {:on-context-menu dom/prevent-default - :on-click unselect-all} - [:& file-library-title - {:project-id project-id - :file-id file-id - :page-id page-id - :file-name file-name - :open? open? - :local? local? - :shared? shared?}] - (when ^boolean open? - [:& file-library-content - {:file file - :local? local? - :filters filters - :on-clear-selection unselect-all - :open-status-ref open-status-ref}])])) - (mf/defc assets-libraries {::mf/wrap [mf/memo] ::mf/wrap-props false} @@ -2420,14 +69,18 @@ ::mf/wrap-props false} [] (let [components-v2 (mf/use-ctx ctx/components-v2) - read-only? (mf/use-ctx ctx/workspace-read-only?) + read-only? (mf/use-ctx ctx/workspace-read-only?) + new-css-system (mf/use-ctx ctx/new-css-system) filters* (mf/use-state {:term "" - :section :all + :section "all" :ordering :asc - :list-style :thumbs}) + :list-style :thumbs + :open-menu false}) filters (deref filters*) term (:term filters) + menu-open? (:open-menu filters) + section (:section filters) toggle-ordering (mf/use-fn #(swap! filters* update :ordering toggle-values [:asc :desc])) @@ -2437,8 +90,13 @@ on-search-term-change (mf/use-fn + (mf/deps new-css-system) (fn [event] - (let [value (dom/get-target-val event)] + ;; NOTE: When old-css-system is removed this function will recibe value and event + ;; Let won't be necessary any more + (let [value (if ^boolean new-css-system + event + (dom/get-target-val event))] (swap! filters* assoc :term value)))) on-search-clear-click @@ -2447,10 +105,11 @@ on-section-filter-change (mf/use-fn (fn [event] - (let [value (-> (dom/get-target event) - (dom/get-value) - (d/read-string))] - (swap! filters* assoc :section value)))) + (let [value (or (-> (dom/get-target event) + (dom/get-value)) + (as-> (dom/get-current-target event) $ + (dom/get-attribute $ "data-test")))] + (swap! filters* assoc :section value :open-menu false)))) handle-key-down (mf/use-fn @@ -2463,46 +122,116 @@ (when ^boolean esc? (dom/blur! node))))) show-libraries-dialog - (mf/use-fn #(modal/show! :libraries-dialog {}))] + (mf/use-fn + (fn [] + (modal/show! :libraries-dialog {}) + (modal/allow-click-outside!))) - [:div.assets-bar - [:div.tool-window - [:div.tool-window-content - [:div.assets-bar-title - (tr "workspace.assets.assets") - (when-not ^boolean read-only? - [:div.libraries-button {:on-click show-libraries-dialog} - i/text-align-justify - (tr "workspace.assets.libraries")])] + on-open-menu + (mf/use-fn #(swap! filters* update :open-menu not)) - [:div.search-block - [:input.search-input - {:placeholder (tr "workspace.assets.search") - :type "text" - :value term - :on-change on-search-term-change - :on-key-down handle-key-down}] + on-menu-close + (mf/use-fn #(swap! filters* assoc :open-menu false)) - (if ^boolean (str/empty? term) - [:div.search-icon - i/search] - [:div.search-icon.close - {:on-click on-search-clear-click} - i/close])] + options [{:option-name (tr "workspace.assets.box-filter-all") + :id "section-all" + :option-handler on-section-filter-change + :data-test "all"} - [:select.input-select {:value (:section filters) - :on-change on-section-filter-change} - [:option {:value ":all"} (tr "workspace.assets.box-filter-all")] - [:option {:value ":components"} (tr "workspace.assets.components")] - (when-not components-v2 - [:option {:value ":graphics"} (tr "workspace.assets.graphics")]) - [:option {:value ":colors"} (tr "workspace.assets.colors")] - [:option {:value ":typographies"} (tr "workspace.assets.typography")]]]] + {:option-name (tr "workspace.assets.components") + :id "section-components" + :option-handler on-section-filter-change + :data-test "components"} - [:& (mf/provider ctx:filters) {:value filters} - [:& (mf/provider ctx:toggle-ordering) {:value toggle-ordering} - [:& (mf/provider ctx:toggle-list-style) {:value toggle-list-style} - [:div.libraries-wrapper - [:& assets-local-library {:filters filters}] - [:& assets-libraries {:filters filters}]]]]]])) + {:option-name (tr "workspace.assets.graphics") + :id "section-graphics" + :option-handler on-section-filter-change + :data-test "graphics"} + + {:option-name (tr "workspace.assets.colors") + :id "section-color" + :option-handler on-section-filter-change + :data-test "colors"} + + {:option-name (tr "workspace.assets.typography") + :id "section-typography" + :option-handler on-section-filter-change + :data-test "typographies"}]] + + (if ^boolean new-css-system + [:div {:class (dom/classnames (css :assets-bar) true)} + [:div {:class (dom/classnames (css :assets-header) true)} + (when-not read-only? + [:button {:class (dom/classnames (css :libraries-button) true) + :on-click #(modal/show! :libraries-dialog {})} + [:span {:class (dom/classnames (css :libraries-icon) true)} + i/library-refactor] + (tr "workspace.assets.libraries")]) + + [:& search-bar {:on-change on-search-term-change + :value term + :placeholder (tr "workspace.assets.search")} + [:button + {:on-click on-open-menu + :class (dom/classnames (css :section-button) true)} + i/filter-refactor]] + [:& context-menu-a11y + {:on-close on-menu-close + :selectable true + :selected section + :show menu-open? + :fixed? true + :min-width? true + :top 152 + :left 64 + :options options + :workspace? true}]] + + [:& (mf/provider cmm/assets-filters) {:value filters} + [:& (mf/provider cmm/assets-toggle-ordering) {:value toggle-ordering} + [:& (mf/provider cmm/assets-toggle-list-style) {:value toggle-list-style} + [:div {:class (dom/classnames (css :libraries-wrapper) true)} + [:& assets-local-library {:filters filters}] + [:& assets-libraries {:filters filters}]]]]]] + + [:div.assets-bar + [:div.tool-window + [:div.tool-window-content + [:div.assets-bar-title + (tr "workspace.assets.assets") + + (when-not ^boolean read-only? + [:div.libraries-button {:on-click show-libraries-dialog} + i/text-align-justify + (tr "workspace.assets.libraries")])] + [:div.search-block + [:input.search-input + {:placeholder (tr "workspace.assets.search") + :type "text" + :value term + :on-change on-search-term-change + :on-key-down handle-key-down}] + + (if ^boolean (str/empty? term) + [:div.search-icon + i/search] + [:div.search-icon.close + {:on-click on-search-clear-click} + i/close])] + + [:select.input-select {:value (:section filters) + :on-change on-section-filter-change} + [:option {:value "all"} (tr "workspace.assets.box-filter-all")] + [:option {:value "components"} (tr "workspace.assets.components")] + (when-not components-v2 + [:option {:value "graphics"} (tr "workspace.assets.graphics")]) + [:option {:value "colors"} (tr "workspace.assets.colors")] + [:option {:value "typographies"} (tr "workspace.assets.typography")]]]] + + [:& (mf/provider cmm/assets-filters) {:value filters} + [:& (mf/provider cmm/assets-toggle-ordering) {:value toggle-ordering} + [:& (mf/provider cmm/assets-toggle-list-style) {:value toggle-list-style} + [:div.libraries-wrapper + [:& assets-local-library {:filters filters}] + [:& assets-libraries {:filters filters}]]]]]]))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.css.json b/frontend/src/app/main/ui/workspace/sidebar/assets.css.json new file mode 100644 index 0000000000..33be480970 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.css.json @@ -0,0 +1 @@ +{"button-primary":"sidebar_assets_button-primary_a9p4J","button-secondary":"sidebar_assets_button-secondary_hbgBA","assets-bar":"sidebar_assets_assets-bar_gzAPj","libraries-button":"sidebar_assets_libraries-button_cKUY7","button-tertiary":"sidebar_assets_button-tertiary_KoPen","button-tag":"sidebar_assets_button-tag_yzpPm","button-icon":"sidebar_assets_button-icon_M5Yy6","libraries-icon":"sidebar_assets_libraries-icon_ikusB","button-icon-small":"sidebar_assets_button-icon-small_xZWe1","asset-element":"sidebar_assets_asset-element_xHJzG","section-button":"sidebar_assets_section-button_RSjn8","sections-container":"sidebar_assets_sections-container_r2YTM","section-item":"sidebar_assets_section-item_u6EYM","section-btn":"sidebar_assets_section-btn_s4h2P","libraries-wrapper":"sidebar_assets_libraries-wrapper_agaHg","assets-header":"sidebar_assets_assets-header_aFHCj"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.scss b/frontend/src/app/main/ui/workspace/sidebar/assets.scss new file mode 100644 index 0000000000..eb3603f54f --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.scss @@ -0,0 +1,119 @@ +// 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"; + +.assets-bar { + position: relative; + height: 100%; + overflow: hidden; + + .libraries-button { + @include tabTitleTipography; + @extend .button-secondary; + gap: $s-2; + height: $s-32; + width: 100%; + border-radius: $s-8; + margin-bottom: $s-4; + .libraries-icon { + @include flexCenter; + width: $s-24; + height: 100%; + svg { + @include flexCenter; + @extend .button-icon; + } + } + &:hover { + background-color: var(--button-secondary-background-color-hover); + color: var(--button-secondary-foreground-color-hover); + border: $s-1 solid var(--button-secondary-border-color-hover); + svg { + stroke: var(--button-secondary-foreground-color-hover); + } + } + &:focus { + background-color: var(--button-secondary-background-color-focus); + color: var(--button-secondary-foreground-color-focus); + border: $s-1 solid var(--button-secondary-border-color-focus); + svg { + stroke: var(--button-secondary-foreground-color-focus); + } + } + } + .section-button { + @include flexCenter; + @include buttonStyle; + height: $s-32; + width: $s-32; + margin: 0; + border: 1px solid var(--color-background-tertiary); + border-radius: $br-8 $br-2 $br-2 $br-8; + background-color: var(--color-background-tertiary); + svg { + height: $s-16; + width: $s-16; + stroke: var(--icon-foreground); + } + &:focus { + border: 1px solid var(--input-border-color-focus); + outline: 0; + background-color: var(--input-background-color-active); + color: var(--input-foreground-color-active); + svg { + background-color: var(--input-background-color-active); + } + } + &:hover { + border: 1px solid var(--input-background-color-hover); + background-color: var(--input-background-color-hover); + svg { + background-color: var(--input-background-color-hover); + stroke: var(--button-foreground-hover); + } + } + } + .sections-container { + position: absolute; + top: $s-84; + left: $s-12; + display: flex; + flex-direction: column; + gap: $s-4; + width: $s-192; + padding: $s-4; + border-radius: $br-8; + background-color: var(--menu-background-color); + z-index: $z-index-4; + box-shadow: 0px 0px 10px 0px var(--menu-shadow-color); + .section-item { + @include titleTipography; + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + padding: $s-6; + border-radius: $br-8; + .section-btn { + @include buttonStyle; + } + } + } + .libraries-wrapper { + overflow: auto; + display: flex; + flex-direction: column; + height: calc(100% - $s-72); + overflow-y: auto; + overflow-x: hidden; + scrollbar-gutter: stable; + overflow-y: overlay; + } + .assets-header { + padding: $s-8 $s-12 $s-4 $s-12; + } +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/colors.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/colors.cljs new file mode 100644 index 0000000000..216ece7849 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/colors.cljs @@ -0,0 +1,621 @@ +;; 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 + +(ns app.main.ui.workspace.sidebar.assets.colors + (:require-macros [app.main.style :refer [css]]) + (:require + [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.pages.helpers :as cph] + [app.main.data.events :as ev] + [app.main.data.modal :as modal] + [app.main.data.workspace :as dw] + [app.main.data.workspace.colors :as dc] + [app.main.data.workspace.libraries :as dwl] + [app.main.data.workspace.undo :as dwu] + [app.main.refs :as refs] + [app.main.store :as st] + [app.main.ui.components.color-bullet :as bc] + [app.main.ui.components.color-bullet-new :as cb] + [app.main.ui.context :as ctx] + [app.main.ui.icons :as i] + [app.main.ui.workspace.sidebar.assets.common :as cmm] + [app.main.ui.workspace.sidebar.assets.groups :as grp] + [app.util.color :as uc] + [app.util.dom :as dom] + [app.util.i18n :as i18n :refer [tr]] + [app.util.keyboard :as kbd] + [cuerdas.core :as str] + [okulary.core :as l] + [potok.core :as ptk] + [rumext.v2 :as mf])) + +(mf/defc color-item + {::mf/wrap-props false} + [{:keys [color local? file-id selected multi-colors? multi-assets? + on-asset-click on-assets-delete on-clear-selection on-group + selected-full selected-paths move-color]}] + + (let [color (mf/with-memo [color file-id] + (cond-> color + (:value color) (assoc :color (:value color) :opacity 1) + (:value color) (dissoc :value) + true (assoc :file-id file-id))) + + + color-id (:id color) + + item-ref (mf/use-ref) + dragging* (mf/use-state false) + dragging? (deref dragging*) + + rename? (= (:color-for-rename @refs/workspace-local) color-id) + input-ref (mf/use-ref) + + editing* (mf/use-state rename?) + editing? (deref editing*) + + menu-state (mf/use-state cmm/initial-context-menu-state) + read-only? (mf/use-ctx ctx/workspace-read-only?) + new-css-system (mf/use-ctx ctx/new-css-system) + + default-name (cond + (:gradient color) (uc/gradient-type->string (dm/get-in color [:gradient :type])) + (:color color) (:color color) + :else (:value color)) + + apply-color + (mf/use-fn + (mf/deps color) + (fn [event] + (st/emit! (dc/apply-color-from-palette (merge uc/empty-color color) (kbd/alt? event))))) + + rename-color + (mf/use-fn + (mf/deps file-id color-id) + (fn [name] + (st/emit! (dwl/rename-color file-id color-id name)))) + + edit-color + (mf/use-fn + (mf/deps color file-id) + (fn [attrs] + (let [name (cph/merge-path-item (:path color) (:name color)) + color (-> attrs + (assoc :id (:id color)) + (assoc :file-id file-id) + (assoc :name name))] + (st/emit! (dwl/update-color color file-id))))) + + delete-color + (mf/use-fn + (mf/deps multi-colors? multi-assets? file-id color-id) + (fn [] + (if (or multi-colors? multi-assets?) + (on-assets-delete) + (let [undo-id (js/Symbol)] + (st/emit! (dwu/start-undo-transaction undo-id) + (dwl/delete-color color) + (dwl/sync-file file-id file-id :colors color-id) + (dwu/commit-undo-transaction undo-id)))))) + + rename-color-clicked + (mf/use-fn + (mf/deps read-only? local?) + (fn [event] + (when (and local? (not read-only?)) + (dom/prevent-default event) + (reset! editing* true)))) + + input-blur + (mf/use-fn + (mf/deps rename-color) + (fn [event] + (let [target (dom/event->target event) + name (dom/get-value target)] + (rename-color name) + (st/emit! dwl/clear-color-for-rename) + (reset! editing* false)))) + + input-key-down + (mf/use-fn + (mf/deps input-blur) + (fn [event] + (when (kbd/esc? event) + (st/emit! dwl/clear-color-for-rename) + (reset! editing* false)) + (when (kbd/enter? event) + (input-blur event)))) + + edit-color-clicked + (mf/use-fn + (mf/deps edit-color color) + (fn [event] + (modal/show! :colorpicker + {:x (.-clientX ^js event) + :y (.-clientY ^js event) + :on-accept edit-color + :data color + :position :right}))) + + on-context-menu + (mf/use-fn + (mf/deps color-id selected on-clear-selection read-only?) + (fn [event] + (dom/prevent-default event) + (let [pos (dom/get-client-position event)] + (when (and local? (not read-only?)) + (when-not (contains? selected color-id) + (on-clear-selection)) + (swap! menu-state cmm/open-context-menu pos))))) + + on-close-menu + (mf/use-fn + (fn [] + (swap! menu-state cmm/close-context-menu))) + + on-drop + (mf/use-fn + (mf/deps color dragging* selected selected-full selected-paths move-color) + (fn [event] + (cmm/on-drop-asset event color dragging* selected selected-full + selected-paths move-color))) + + on-drag-enter + (mf/use-fn + (mf/deps color dragging* selected selected-paths) + (fn [event] + (cmm/on-drag-enter-asset event color dragging* selected selected-paths))) + + on-drag-leave + (mf/use-fn + (mf/deps dragging*) + (fn [event] + (cmm/on-drag-leave-asset event dragging*))) + + on-color-drag-start + (mf/use-fn + (mf/deps color file-id selected item-ref read-only?) + (fn [event] + (if read-only? + (dom/prevent-default event) + (cmm/on-asset-drag-start event file-id color selected item-ref :colors identity)))) + + on-click + (mf/use-fn + (mf/deps color-id apply-color on-asset-click) + (partial on-asset-click color-id apply-color))] + + (mf/with-effect [editing?] + (when editing? + (let [input (mf/ref-val input-ref)] + (dom/select-text! input) + nil))) + + (if ^boolean new-css-system + [:div {:class (dom/classnames (css :asset-list-item) true + (css :selected) (contains? selected (:id color)) + (css :editing) editing?) + :style #js {"--bullet-size" "16px"} + :on-context-menu on-context-menu + :on-click (when-not editing? on-click) + :ref item-ref + :draggable (and (not read-only?) (not editing?)) + :on-drag-start on-color-drag-start + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + + [:div {:class (dom/classnames (css :bullet-block) true)} + [:& cb/color-bullet {:color color + :mini? true}]] + + (if ^boolean editing? + [:input + {:type "text" + :class (dom/classnames (css :element-name) true) + :ref input-ref + :on-blur input-blur + :on-key-down input-key-down + :auto-focus true + :default-value (cph/merge-path-item (:path color) (:name color))}] + + [:div {:title (:name color) + :class (dom/classnames (css :name-block) true) + :on-double-click rename-color-clicked} + + (if (= (:name color) default-name) + [:span {:class (dom/classnames (css :default-name-only) true)} default-name] + [:* + [:span {:class (dom/classnames (css :name) true)} (:name color)] + [:span {:class (dom/classnames (css :default-name) true)} default-name]])]) + + (when local? + [:& cmm/assets-context-menu + {:on-close on-close-menu + :state @menu-state + :options [(when-not (or multi-colors? multi-assets?) + {:option-name (tr "workspace.assets.rename") + :id "assets-rename-color" + :option-handler rename-color-clicked}) + (when-not (or multi-colors? multi-assets?) + {:option-name (tr "workspace.assets.edit") + :id "assets-edit-color" + :option-handler edit-color-clicked}) + + {:option-name (tr "workspace.assets.delete") + :id "assets-delete-color" + :option-handler delete-color} + (when-not multi-assets? + {:option-name (tr "workspace.assets.group") + :id "assets-group-color" + :option-handler (on-group (:id color))})]}]) + + (when ^boolean dragging? + [:div {:class (dom/classnames (css :dragging) true)}])] + + [:div.asset-list-item + {:class-name (dom/classnames + :selected (contains? selected (:id color))) + :on-context-menu on-context-menu + :on-click (when-not editing? on-click) + :ref item-ref + :draggable (and (not read-only?) (not editing?)) + :on-drag-start on-color-drag-start + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + + [:& bc/color-bullet {:color color}] + + (if ^boolean editing? + [:input.element-name + {:type "text" + :ref input-ref + :on-blur input-blur + :on-key-down input-key-down + :auto-focus true + :default-value (cph/merge-path-item (:path color) (:name color))}] + + [:div.name-block {:title (:name color) + :on-double-click rename-color-clicked} + (:name color) + (when-not (= (:name color) default-name) + [:span default-name])]) + + (when local? + [:& cmm/assets-context-menu + {:on-close on-close-menu + :state @menu-state + :options [(when-not (or multi-colors? multi-assets?) + [(tr "workspace.assets.rename") rename-color-clicked]) + (when-not (or multi-colors? multi-assets?) + [(tr "workspace.assets.edit") edit-color-clicked]) + [(tr "workspace.assets.delete") delete-color] + (when-not multi-assets? + [(tr "workspace.assets.group") (on-group (:id color))])]}]) + + (when ^boolean dragging? + [:div.dragging])]))) + +(mf/defc colors-group + [{:keys [file-id prefix groups open-groups local? selected + multi-colors? multi-assets? on-asset-click on-assets-delete + on-clear-selection on-group on-rename-group on-ungroup colors + selected-full]}] + (let [group-open? (get open-groups prefix true) + new-css-system (mf/use-ctx ctx/new-css-system) + dragging* (mf/use-state false) + dragging? (deref dragging*) + + selected-paths (mf/with-memo [selected-full] + (into #{} + (comp (map :path) (d/nilv "")) + selected-full)) + + move-color + (mf/use-fn (mf/deps file-id) (partial dwl/rename-color file-id)) + + on-drag-enter + (mf/use-fn + (mf/deps dragging* prefix selected-paths) + (fn [event] + (cmm/on-drag-enter-asset-group event dragging* prefix selected-paths))) + + on-drag-leave + (mf/use-fn + (mf/deps dragging*) + (fn [event] + (cmm/on-drag-leave-asset event dragging*))) + + on-drop + (mf/use-fn + (mf/deps dragging* prefix selected-paths selected-full move-color) + (fn [event] + (cmm/on-drop-asset-group event dragging* prefix selected-paths selected-full move-color)))] + + (if ^boolean new-css-system + [:div {:class (dom/classnames (css :colors-group) true) + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + [:& grp/asset-group-title {:file-id file-id + :section :colors + :path prefix + :group-open? group-open? + :on-rename on-rename-group + :on-ungroup on-ungroup}] + (when group-open? + [:* + (let [colors (get groups "" [])] + [:div {:class (dom/classnames (css :asset-list) true) + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + + (when ^boolean dragging? + [:div {:class (dom/classnames (css :grid-placeholder) true)} + "\u00A0"]) + + (when (and (empty? colors) + (some? groups)) + [:div {:class (dom/classnames (css :drop-space) true)}]) + + (for [color colors] + [:& color-item {:key (dm/str (:id color)) + :color color + :file-id file-id + :local? local? + :selected selected + :multi-colors? multi-colors? + :multi-assets? multi-assets? + :on-asset-click on-asset-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection + :on-group on-group + :colors colors + :selected-full selected-full + :selected-paths selected-paths + :move-color move-color}])]) + + (for [[path-item content] groups] + (when-not (empty? path-item) + [:& colors-group {:file-id file-id + :prefix (cph/merge-path-item prefix path-item) + :key (dm/str "group-" path-item) + :groups content + :open-groups open-groups + :local? local? + :selected selected + :multi-colors? multi-colors? + :multi-assets? multi-assets? + :on-asset-click on-asset-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection + :on-group on-group + :on-rename-group on-rename-group + :on-ungroup on-ungroup + :colors colors + :selected-full selected-full}]))])] + + + [:div {:on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + [:& grp/asset-group-title {:file-id file-id + :section :colors + :path prefix + :group-open? group-open? + :on-rename on-rename-group + :on-ungroup on-ungroup}] + (when group-open? + [:* + (let [colors (get groups "" [])] + [:div.asset-list {:on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + + (when ^boolean dragging? + [:div.grid-placeholder "\u00A0"]) + + (when (and (empty? colors) + (some? groups)) + [:div.drop-space]) + + (for [color colors] + [:& color-item {:key (dm/str (:id color)) + :color color + :file-id file-id + :local? local? + :selected selected + :multi-colors? multi-colors? + :multi-assets? multi-assets? + :on-asset-click on-asset-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection + :on-group on-group + :colors colors + :selected-full selected-full + :selected-paths selected-paths + :move-color move-color}])]) + + (for [[path-item content] groups] + (when-not (empty? path-item) + [:& colors-group {:file-id file-id + :prefix (cph/merge-path-item prefix path-item) + :key (dm/str "group-" path-item) + :groups content + :open-groups open-groups + :local? local? + :selected selected + :multi-colors? multi-colors? + :multi-assets? multi-assets? + :on-asset-click on-asset-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection + :on-group on-group + :on-rename-group on-rename-group + :on-ungroup on-ungroup + :colors colors + :selected-full selected-full}]))])]))) + +(mf/defc colors-section + [{:keys [file-id local? colors open? open-status-ref selected reverse-sort? + on-asset-click on-assets-delete on-clear-selection] :as props}] + + (let [selected (:colors selected) + selected-full (mf/with-memo [selected colors] + (into #{} (filter #(contains? selected (:id %))) colors)) + + open-groups-ref (mf/with-memo [open-status-ref] + (-> (l/in [:groups :colors]) + (l/derived open-status-ref))) + open-groups (mf/deref open-groups-ref) + + multi-colors? (> (count selected) 1) + multi-assets? (or (seq (:components selected)) + (seq (:graphics selected)) + (seq (:typographies selected))) + + groups (mf/with-memo [colors reverse-sort?] + (grp/group-assets colors reverse-sort?)) + + read-only? (mf/use-ctx ctx/workspace-read-only?) + new-css-system (mf/use-ctx ctx/new-css-system) + add-color + (mf/use-fn + (fn [value _] + (st/emit! (dwl/add-color value)))) + + add-color-clicked + (mf/use-fn + (mf/deps file-id) + (fn [event] + (st/emit! (dw/set-assets-section-open file-id :colors true) + (ptk/event ::ev/event {::ev/name "add-asset-to-library" + :asset-type "color"})) + (modal/show! :colorpicker + {:x (.-clientX event) + :y (.-clientY event) + :on-accept add-color + :data {:color "#406280" + :opacity 1} + :position :right}))) + + create-group + (mf/use-fn + (mf/deps colors selected on-clear-selection file-id) + (fn [color-id] + (fn [group-name] + (on-clear-selection) + (let [undo-id (js/Symbol)] + (st/emit! (dwu/start-undo-transaction undo-id)) + (run! st/emit! + (->> colors + (filter #(if multi-colors? + (contains? selected (:id %)) + (= color-id (:id %)))) + (map #(dwl/update-color + (assoc % :name + (cmm/add-group % group-name)) + file-id)))) + (st/emit! (dwu/commit-undo-transaction undo-id)))))) + + rename-group + (mf/use-fn + (mf/deps colors) + (fn [path last-path] + (on-clear-selection) + (let [undo-id (js/Symbol)] + (st/emit! (dwu/start-undo-transaction undo-id)) + (run! st/emit! + (->> colors + (filter #(str/starts-with? (:path %) path)) + (map #(dwl/update-color + (assoc % :name + (cmm/rename-group % path last-path)) + file-id)))) + (st/emit! (dwu/commit-undo-transaction undo-id))))) + + on-group + (mf/use-fn + (mf/deps colors selected) + (fn [color-id] + (fn [event] + (dom/stop-propagation event) + (modal/show! :name-group-dialog {:accept (create-group color-id)})))) + + on-rename-group + (mf/use-fn + (mf/deps colors) + (fn [event path last-path] + (dom/stop-propagation event) + (modal/show! :name-group-dialog {:path path + :last-path last-path + :accept rename-group}))) + on-ungroup + (mf/use-fn + (mf/deps colors) + (fn [path] + (on-clear-selection) + (let [undo-id (js/Symbol)] + (st/emit! (dwu/start-undo-transaction undo-id)) + (apply st/emit! + (->> colors + (filter #(str/starts-with? (:path %) path)) + (map #(dwl/update-color + (assoc % :name + (cmm/ungroup % path)) + file-id)))) + (st/emit! (dwu/commit-undo-transaction undo-id))))) + + on-asset-click + (mf/use-fn (mf/deps groups on-asset-click) (partial on-asset-click groups))] + + + [:& cmm/asset-section {:file-id file-id + :title (tr "workspace.assets.colors") + :section :colors + :assets-count (count colors) + :open? open?} + (if ^boolean new-css-system + (when local? + [:& cmm/asset-section-block {:role :title-button} + (when-not read-only? + [:button {:class (dom/classnames (css :assets-btn) true) + :on-click add-color-clicked} + i/add-refactor])]) + + (when local? + [:& cmm/asset-section-block {:role :title-button} + (when-not read-only? + [:div.assets-button {:on-click add-color-clicked} + i/plus])])) + + + [:& cmm/asset-section-block {:role :content} + [:& colors-group {:file-id file-id + :prefix "" + :groups groups + :open-groups open-groups + :local? local? + :selected selected + :multi-colors? multi-colors? + :multi-assets? multi-assets? + :on-asset-click on-asset-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection + :on-group on-group + :on-rename-group on-rename-group + :on-ungroup on-ungroup + :colors colors + :selected-full selected-full}]]])) \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/colors.css.json b/frontend/src/app/main/ui/workspace/sidebar/assets/colors.css.json new file mode 100644 index 0000000000..3de5347cd7 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/colors.css.json @@ -0,0 +1 @@ +{"button-primary":"assets_colors_button-primary_6ZMmx","button-secondary":"assets_colors_button-secondary_dNSLH","button-tertiary":"assets_colors_button-tertiary_h20-o","assets-btn":"assets_colors_assets-btn_t8DHG","button-tag":"assets_colors_button-tag_ux-NH","button-icon":"assets_colors_button-icon_f-EVH","button-icon-small":"assets_colors_button-icon-small_zq8dv","asset-element":"assets_colors_asset-element_XSxD1","colors-group":"assets_colors_colors-group_fUsuo","asset-list":"assets_colors_asset-list_wMm1l","asset-list-item":"assets_colors_asset-list-item_ZFtXC","bullet-block":"assets_colors_bullet-block_ZRR2Y","name-block":"assets_colors_name-block_Zvmy3","default-name-only":"assets_colors_default-name-only_JFCGo","name":"assets_colors_name_AjZzr","default-name":"assets_colors_default-name_8gEAb","element-name":"assets_colors_element-name_ADGM8","selected":"assets_colors_selected_ElMu0","editing":"assets_colors_editing_FWnHU","grid-placeholder":"assets_colors_grid-placeholder_7wTFd","drop-space":"assets_colors_drop-space_lbzeC","dragging":"assets_colors_dragging_EmBOk"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/colors.scss b/frontend/src/app/main/ui/workspace/sidebar/assets/colors.scss new file mode 100644 index 0000000000..e8d9b6bed0 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/colors.scss @@ -0,0 +1,102 @@ +// 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"; + +.assets-btn { + @extend .button-tertiary; + height: $s-32; + width: calc($s-24 + $s-4); + padding: 0; + border-radius: $br-8; + svg { + @extend .button-icon; + } +} + +.colors-group { + margin-top: $s-4; + .asset-list { + padding: 0 0 0 $s-12; + .asset-list-item { + position: relative; + display: flex; + align-items: center; + height: $s-32; + padding: $s-8 $s-12 $s-8 $s-8; + margin-bottom: $s-4; + border-radius: $br-8; + background-color: var(--assets-item-background-color); + cursor: pointer; + .bullet-block { + @include flexCenter; + height: 100%; + width: $s-32; + } + .name-block { + @include titleTipography; + display: grid; + grid-template-columns: auto 1fr; + margin: 0; + overflow: hidden; + .default-name-only, + .name { + color: var(--assets-item-name-foreground-color-hover); + margin-right: $s-6; + @include textEllipsis; + } + .default-name { + min-width: 0; + color: var(--assets-item-name-foreground-color); + } + } + .element-name { + @include textEllipsis; + color: var(--color-foreground-primary); + } + &.selected { + border: $s-1 solid var(--assets-item-border-color); + } + + &.editing { + border: $s-1 solid var(--input-border-color-focus); + input.element-name { + @include textEllipsis; + @include titleTipography; + @include removeInputStyle; + flex-grow: 1; + height: $s-28; + max-width: calc(var(--parent-size) - (var(--depth) * var(--layer-indentation-size))); + margin: 0; + color: var(--layer-row-foreground-color); + } + } + &:hover { + background-color: var(--assets-item-background-color-hover); + } + } + } + .grid-placeholder { + height: $s-2; + margin-bottom: $s-2; + background-color: var(--color-accent-primary); + } + .drop-space { + height: $s-12; + border-radius: $br-8; + background-color: var(--assets-item-background-color); + } + .dragging { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border-radius: $s-8; + background-color: var(--assets-item-background-color-drag); + border: $s-2 solid var(--assets-item-border-color-drag); + } +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs new file mode 100644 index 0000000000..c75c199c9b --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs @@ -0,0 +1,257 @@ +;; 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 + + +(ns app.main.ui.workspace.sidebar.assets.common + (:require-macros [app.main.style :refer [css]]) + (:require + [app.common.data.macros :as dm] + [app.common.pages.helpers :as cph] + [app.common.spec :as us] + [app.main.data.modal :as modal] + [app.main.data.workspace :as dw] + [app.main.data.workspace.undo :as dwu] + [app.main.store :as st] + [app.main.ui.components.context-menu :refer [context-menu]] + [app.main.ui.components.context-menu-a11y :refer [context-menu-a11y]] + [app.main.ui.components.title-bar :refer [title-bar]] + [app.main.ui.context :as ctx] + [app.main.ui.icons :as i] + [app.util.dom :as dom] + [app.util.dom.dnd :as dnd] + [app.util.strings :refer [matches-search]] + [app.util.timers :as ts] + [cljs.spec.alpha :as s] + [cuerdas.core :as str] + [rumext.v2 :as mf])) + +(def assets-filters (mf/create-context nil)) +(def assets-toggle-ordering (mf/create-context nil)) +(def assets-toggle-list-style (mf/create-context nil)) + +(defn apply-filters + [coll {:keys [ordering term] :as filters}] + (let [reverse? (= :desc ordering) + comp-fn (if ^boolean reverse? > <)] + (->> coll + (filter (fn [item] + (or (matches-search (:name item "!$!") term) + (matches-search (:value item "!$!") term)))) + ; Sort by folder order, but + ; putting all "root" items + ; always first, independently + ; of sort order. + (sort-by #(str/lower (cph/merge-path-item (if (empty? (:path %)) + (if reverse? "z" "a") + (:path %)) + (:name %))) + comp-fn)))) + +(defn add-group + [asset group-name] + (-> (:path asset) + (cph/merge-path-item group-name) + (cph/merge-path-item (:name asset)))) + +(defn rename-group + [asset path last-path] + (-> (:path asset) + (str/slice 0 (count path)) + (cph/split-path) + butlast + (vec) + (conj last-path) + (cph/join-path) + (str (str/slice (:path asset) (count path))) + (cph/merge-path-item (:name asset)))) + +(defn ungroup + [asset path] + (-> (:path asset) + (str/slice 0 (count path)) + (cph/split-path) + butlast + (cph/join-path) + (str (str/slice (:path asset) (count path))) + (cph/merge-path-item (:name asset)))) + +(s/def ::asset-name ::us/not-empty-string) +(s/def ::name-group-form + (s/keys :req-un [::asset-name])) + +(def initial-context-menu-state + {:open? false :top nil :left nil}) + +(defn open-context-menu + [state pos] + (let [top (:y pos) + left (+ (:x pos) 10)] + (assoc state + :open? true + :top top + :left left))) + +(defn close-context-menu + [state] + (assoc state :open? false)) + +(mf/defc assets-context-menu + {::mf/wrap-props false} + [{:keys [options state on-close]}] + (let [new-css-system (mf/use-ctx ctx/new-css-system)] + (if new-css-system + [:& context-menu-a11y + {:show (:open? state) + :fixed? (or (not= (:top state) 0) (not= (:left state) 0)) + :on-close on-close + :top (:top state) + :left (:left state) + :options options + :workspace? true}] + + [:& context-menu + {:selectable false + :show (:open? state) + :on-close on-close + :top (:top state) + :left (:left state) + :options options}]))) + +(mf/defc section-icon + [{:keys [section] :as props}] + (case section + :colors i/drop-refactor + :components i/component-refactor + :typographies i/text-palette-refactor + i/add-refactor)) + +(mf/defc asset-section + {::mf/wrap-props false} + [{:keys [children file-id title section assets-count open?]}] + (let [children (->> (if (array? children) children [children]) + (filter some?)) + get-role #(.. % -props -role) + title-buttons (filter #(= (get-role %) :title-button) children) + content (filter #(= (get-role %) :content) children) + new-css-system (mf/use-ctx ctx/new-css-system)] + (if ^boolean new-css-system + [:div {:class (dom/classnames (css :asset-section) true)} + [:& title-bar {:collapsable? true + :collapsed? (not open?) + :on-collapsed #(st/emit! (dw/set-assets-section-open file-id section (not open?))) + :klass :title-spacing + :title (mf/html [:span {:class (dom/classnames (css :title-name) true)} + [:span {:class (dom/classnames (css :section-icon) true)} + [:& section-icon {:section section}]] + [:span {:class (dom/classnames (css :section-name) true)} + title] + + [:span {:class (dom/classnames (css :num-assets) true)} + assets-count]])} + title-buttons] + (when ^boolean open? + content)] + [:div.asset-section + [:div.asset-title {:class (when (not ^boolean open?) "closed")} + [:span {:on-click #(st/emit! (dw/set-assets-section-open file-id section (not open?)))} + i/arrow-slide title] + [:span.num-assets (dm/str "\u00A0(") assets-count ")"] ;; Unicode 00A0 is non-breaking space + title-buttons] + (when ^boolean open? + content)]))) + +(mf/defc asset-section-block + [{:keys [children]}] + [:* children]) + +(defn create-assets-group + [rename components-to-group group-name] + (let [undo-id (js/Symbol)] + (st/emit! (dwu/start-undo-transaction undo-id)) + (apply st/emit! + (->> components-to-group + (map #(rename + (:id %) + (add-group % group-name))))) + (st/emit! (dwu/commit-undo-transaction undo-id)))) + +(defn on-drop-asset + [event asset dragging* selected selected-full selected-paths rename] + (let [create-typed-assets-group (partial create-assets-group rename)] + (when (not (dnd/from-child? event)) + (reset! dragging* false) + (when + (and (not (contains? selected (:id asset))) + (every? #(= % (:path asset)) selected-paths)) + (let [components-to-group (conj selected-full asset) + create-typed-assets-group (partial create-typed-assets-group components-to-group)] + (modal/show! :name-group-dialog {:accept create-typed-assets-group})))))) + +(defn on-drag-enter-asset + [event asset dragging* selected selected-paths] + (when (and + (not (dnd/from-child? event)) + (every? #(= % (:path asset)) selected-paths) + (not (contains? selected (:id asset)))) + (reset! dragging* true))) + +(defn on-drag-leave-asset + [event dragging*] + (when (not (dnd/from-child? event)) + (reset! dragging* false))) + +(defn create-counter-element + [asset-count] + (let [counter-el (dom/create-element "div")] + (dom/set-property! counter-el "class" "drag-counter") + (dom/set-text! counter-el (str asset-count)) + counter-el)) + +(defn set-drag-image + [event item-ref num-selected] + (let [offset (dom/get-offset-position (.-nativeEvent event)) + item-el (mf/ref-val item-ref) + counter-el (create-counter-element num-selected)] + + ;; set-drag-image requires that the element is rendered and + ;; visible to the user at the moment of creating the ghost + ;; image (to make a snapshot), but you may remove it right + ;; afterwards, in the next render cycle. + (dom/append-child! item-el counter-el) + (dnd/set-drag-image! event item-el (:x offset) (:y offset)) + (ts/raf #(.removeChild ^js item-el counter-el)))) + +(defn on-asset-drag-start + [event file-id asset selected item-ref asset-type on-drag-start] + (let [id-asset (:id asset) + num-selected (if (contains? selected id-asset) + (count selected) + 1)] + (when (not (contains? selected id-asset)) + (st/emit! (dw/unselect-all-assets file-id) + (dw/toggle-selected-assets file-id id-asset asset-type))) + (on-drag-start asset event) + (when (> num-selected 1) + (set-drag-image event item-ref num-selected)))) + +(defn on-drag-enter-asset-group + [event dragging* prefix selected-paths] + (dom/stop-propagation event) + (when (and (not (dnd/from-child? event)) + (not (every? #(= % prefix) selected-paths))) + (reset! dragging* true))) + +(defn on-drop-asset-group + [event dragging* prefix selected-paths selected-full rename] + (dom/stop-propagation event) + (when (not (dnd/from-child? event)) + (reset! dragging* false) + (when (not (every? #(= % prefix) selected-paths)) + (doseq [target-asset selected-full] + (st/emit! + (rename + (:id target-asset) + (cph/merge-path-item prefix (:name target-asset)))))))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/common.css.json b/frontend/src/app/main/ui/workspace/sidebar/assets/common.css.json new file mode 100644 index 0000000000..e4b336d85c --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/common.css.json @@ -0,0 +1 @@ +{"button-primary":"assets_common_button-primary_-eBqD","button-secondary":"assets_common_button-secondary_qo2kg","button-tertiary":"assets_common_button-tertiary_ApdBb","button-tag":"assets_common_button-tag_MHJlj","button-icon":"assets_common_button-icon_0R1zt","button-icon-small":"assets_common_button-icon-small_5kQfO","asset-element":"assets_common_asset-element_frsFR","asset-section":"assets_common_asset-section_uKhc8","title-name":"assets_common_title-name_ZOz9E","section-icon":"assets_common_section-icon_Kitcf","section-name":"assets_common_section-name_RVo-u","num-assets":"assets_common_num-assets_Dguaz"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/common.scss b/frontend/src/app/main/ui/workspace/sidebar/assets/common.scss new file mode 100644 index 0000000000..16a95d2388 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/common.scss @@ -0,0 +1,40 @@ +// 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"; + +.asset-section { + .title-name { + @include tabTitleTipography; + display: flex; + align-items: center; + flex-grow: 1; + width: 100%; + .section-icon { + @include flexCenter; + padding-right: $s-8; + svg { + @include flexCenter; + height: $s-16; + width: $s-16; + color: transparent; + fill: none; + } + } + .section-name { + display: flex; + align-items: center; + } + .num-assets { + @include flexCenter; + height: 100%; + padding-left: $s-8; + } + } +} +:global(.title-spacing) { + margin-bottom: $s-4; +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs new file mode 100644 index 0000000000..fcfbf06db1 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs @@ -0,0 +1,662 @@ +;; 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 + +(ns app.main.ui.workspace.sidebar.assets.components + (:require-macros [app.main.style :refer [css]]) + (:require + [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.media :as cm] + [app.common.pages.helpers :as cph] + [app.common.types.file :as ctf] + [app.main.data.events :as ev] + [app.main.data.modal :as modal] + [app.main.data.workspace :as dw] + [app.main.data.workspace.libraries :as dwl] + [app.main.data.workspace.media :as dwm] + [app.main.data.workspace.undo :as dwu] + [app.main.refs :as refs] + [app.main.render :refer [component-svg]] + [app.main.store :as st] + [app.main.ui.components.editable-label :refer [editable-label]] + [app.main.ui.components.file-uploader :refer [file-uploader]] + [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] + [app.main.ui.context :as ctx] + [app.main.ui.icons :as i] + [app.main.ui.workspace.sidebar.assets.common :as cmm] + [app.main.ui.workspace.sidebar.assets.groups :as grp] + [app.util.dom :as dom] + [app.util.dom.dnd :as dnd] + [app.util.i18n :as i18n :refer [tr]] + [cuerdas.core :as str] + [okulary.core :as l] + [potok.core :as ptk] + [rumext.v2 :as mf])) + +(defn- get-component-root-and-container + [file-id component components-v2] + (if (= file-id (:id @refs/workspace-file)) + (let [data @refs/workspace-data] + [(ctf/get-component-root data component) + (if components-v2 + (ctf/get-component-page data component) + component)]) + (let [data (dm/get-in @refs/workspace-libraries [file-id :data])] + [(ctf/get-component-root data component) + (if components-v2 + (ctf/get-component-page data component) + component)]))) + +(mf/defc components-item + {::mf/wrap-props false} + [{:keys [component renaming listing-thumbs? selected + file-id on-asset-click on-context-menu on-drag-start do-rename + cancel-rename selected-full selected-paths]}] + (let [item-ref (mf/use-ref) + + dragging* (mf/use-state false) + dragging? (deref dragging*) + + read-only? (mf/use-ctx ctx/workspace-read-only?) + components-v2 (mf/use-ctx ctx/components-v2) + new-css-system (mf/use-ctx ctx/new-css-system) + component-id (:id component) + + ;; NOTE: we don't use reactive deref for it because we don't + ;; really need rerender on any change on the file change. If + ;; the component changes, it will trigger rerender anyway. + [root-shape container] + (get-component-root-and-container file-id component components-v2) + + unselect-all + (mf/use-fn + (fn [] + (st/emit! (dw/unselect-all-assets)))) + + on-component-click + (mf/use-fn + (mf/deps component selected) + (fn [event] + (dom/stop-propagation event) + (on-asset-click component-id unselect-all event))) + + on-component-double-click + (mf/use-fn + (mf/deps file-id component-id) + (fn [event] + (dom/stop-propagation event) + (st/emit! (dw/go-to-main-instance file-id component-id)))) + + on-drop + (mf/use-fn + (mf/deps component dragging* selected selected-full selected-paths) + (fn [event] + (cmm/on-drop-asset event component dragging* selected selected-full + selected-paths dwl/rename-component))) + + on-drag-enter + (mf/use-fn + (mf/deps component dragging* selected selected-paths) + (fn [event] + (cmm/on-drag-enter-asset event component dragging* selected selected-paths))) + + on-drag-leave + (mf/use-fn + (mf/deps dragging*) + (fn [event] + (cmm/on-drag-leave-asset event dragging*))) + + on-component-drag-start + (mf/use-fn + (mf/deps file-id component selected item-ref on-drag-start read-only?) + (fn [event] + (if read-only? + (dom/prevent-default event) + (cmm/on-asset-drag-start event file-id component selected item-ref :components on-drag-start)))) + + on-context-menu + (mf/use-fn + (mf/deps component-id) + (partial on-context-menu component-id))] + + (if ^boolean new-css-system + [:div {:ref item-ref + :class (dom/classnames + (css :selected) (contains? selected (:id component)) + (css :grid-cell) listing-thumbs? + (css :enum-item) (not listing-thumbs?)) + :id (dm/str "component-shape-id-" (:id component)) + :draggable (not read-only?) + :on-click on-component-click + :on-double-click on-component-double-click + :on-context-menu on-context-menu + :on-drag-start on-component-drag-start + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + (when (and (some? root-shape) + (some? container)) + [:* + [:& component-svg {:root-shape root-shape + :objects (:objects container)}] + (let [renaming? (= renaming (:id component))] + [:* + [:& editable-label + {:class-name (dom/classnames + (css :cell-name) listing-thumbs? + (css :item-name) (not listing-thumbs?) + (css :editing) renaming?) + :value (cph/merge-path-item (:path component) (:name component)) + :tooltip (cph/merge-path-item (:path component) (:name component)) + :display-value (:name component) + :editing? renaming? + :disable-dbl-click? true + :on-change do-rename + :on-cancel cancel-rename}] + + (when ^boolean dragging? + [:div {:class (dom/classnames (css :dragging) true)}])])])] + + [:div {:ref item-ref + :class (dom/classnames + :selected (contains? selected (:id component)) + :grid-cell listing-thumbs? + :enum-item (not listing-thumbs?)) + :id (dm/str "component-shape-id-" (:id component)) + :draggable (not read-only?) + :on-click on-component-click + :on-double-click on-component-double-click + :on-context-menu on-context-menu + :on-drag-start on-component-drag-start + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + + (when (and (some? root-shape) + (some? container)) + [:* + [:& component-svg {:root-shape root-shape + :objects (:objects container)}] + (let [renaming? (= renaming (:id component))] + [:* + [:& editable-label + {:class-name (dom/classnames + :cell-name listing-thumbs? + :item-name (not listing-thumbs?) + :editing renaming?) + :value (cph/merge-path-item (:path component) (:name component)) + :tooltip (cph/merge-path-item (:path component) (:name component)) + :display-value (:name component) + :editing? renaming? + :disable-dbl-click? true + :on-change do-rename + :on-cancel cancel-rename}] + + (when ^boolean dragging? + [:div.dragging])])])]))) + +(mf/defc components-group + {::mf/wrap-props false} + [{:keys [file-id prefix groups open-groups renaming listing-thumbs? selected on-asset-click + on-drag-start do-rename cancel-rename on-rename-group on-group on-ungroup on-context-menu + selected-full]}] + + (let [group-open? (get open-groups prefix true) + new-css-system (mf/use-ctx ctx/new-css-system) + dragging* (mf/use-state false) + dragging? (deref dragging*) + + selected-paths (mf/with-memo [selected-full] + (into #{} + (comp (map :path) (d/nilv "")) + selected-full)) + on-drag-enter + (mf/use-fn + (mf/deps dragging* prefix selected-paths) + (fn [event] + (cmm/on-drag-enter-asset-group event dragging* prefix selected-paths))) + + on-drag-leave + (mf/use-fn + (mf/deps dragging*) + (fn [event] + (cmm/on-drag-leave-asset event dragging*))) + + on-drop + (mf/use-fn + (mf/deps dragging* prefix selected-paths selected-full) + (fn [event] + (cmm/on-drop-asset-group event dragging* prefix selected-paths selected-full dwl/rename-component)))] + (if ^boolean new-css-system + [:div {:class (dom/classnames (css :component-group) true) + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + [:& grp/asset-group-title + {:file-id file-id + :section :components + :path prefix + :group-open? group-open? + :on-rename on-rename-group + :on-ungroup on-ungroup}] + + + (when group-open? + [:* + (let [components (get groups "" [])] + [:div {:class-name (dom/classnames + (css :asset-grid) listing-thumbs? + (css :asset-enum) (not listing-thumbs?) + (css :drop-space) (and + (empty? components) + (some? groups) + (not dragging?))) + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + + (when ^boolean dragging? + [:div {:class (dom/classnames (css :grid-placeholder) true)} "\u00A0"]) + + + (when (and (empty? components) + (some? groups)) + [:div {:class (dom/classnames (css :drop-space) true)}]) + + (for [component components] + [:& components-item + {:component component + :key (dm/str "component-" (:id component)) + :renaming renaming + :listing-thumbs? listing-thumbs? + :file-id file-id + :selected selected + :selected-full selected-full + :selected-paths selected-paths + :on-asset-click on-asset-click + :on-context-menu on-context-menu + :on-drag-start on-drag-start + :on-group on-group + :do-rename do-rename + :cancel-rename cancel-rename}])]) + + (for [[path-item content] groups] + (when-not (empty? path-item) + [:& components-group {:file-id file-id + :key path-item + :prefix (cph/merge-path-item prefix path-item) + :groups content + :open-groups open-groups + :renaming renaming + :listing-thumbs? listing-thumbs? + :selected selected + :on-asset-click on-asset-click + :on-drag-start on-drag-start + :do-rename do-rename + :cancel-rename cancel-rename + :on-rename-group on-rename-group + :on-ungroup on-ungroup + :on-context-menu on-context-menu + :selected-full selected-full}]))])] + + + [:div {:on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + + [:& grp/asset-group-title + {:file-id file-id + :section :components + :path prefix + :group-open? group-open? + :on-rename on-rename-group + :on-ungroup on-ungroup}] + + (when group-open? + [:* + (let [components (get groups "" [])] + [:div {:class-name (dom/classnames + :asset-grid listing-thumbs? + :big listing-thumbs? + :asset-enum (not listing-thumbs?) + :drop-space (and + (empty? components) + (some? groups) + (not dragging?))) + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + + (when ^boolean dragging? + [:div.grid-placeholder "\u00A0"]) + + (when (and (empty? components) + (some? groups)) + [:div.drop-space]) + + (for [component components] + [:& components-item + {:component component + :key (dm/str "component-" (:id component)) + :renaming renaming + :listing-thumbs? listing-thumbs? + :file-id file-id + :selected selected + :selected-full selected-full + :selected-paths selected-paths + :on-asset-click on-asset-click + :on-context-menu on-context-menu + :on-drag-start on-drag-start + :on-group on-group + :do-rename do-rename + :cancel-rename cancel-rename}])]) + + (for [[path-item content] groups] + (when-not (empty? path-item) + [:& components-group {:file-id file-id + :key path-item + :prefix (cph/merge-path-item prefix path-item) + :groups content + :open-groups open-groups + :renaming renaming + :listing-thumbs? listing-thumbs? + :selected selected + :on-asset-click on-asset-click + :on-drag-start on-drag-start + :do-rename do-rename + :cancel-rename cancel-rename + :on-rename-group on-rename-group + :on-ungroup on-ungroup + :on-context-menu on-context-menu + :selected-full selected-full}]))])]))) + +(mf/defc components-section + {::mf/wrap-props false} + [{:keys [file-id local? components listing-thumbs? open? reverse-sort? selected + on-asset-click on-assets-delete on-clear-selection open-status-ref]}] + + (let [input-ref (mf/use-ref nil) + + state* (mf/use-state {}) + state (deref state*) + + current-component-id (:component-id state) + renaming? (:renaming state) + + open-groups-ref (mf/with-memo [open-status-ref] + (-> (l/in [:groups :components]) + (l/derived open-status-ref))) + + open-groups (mf/deref open-groups-ref) + + menu-state (mf/use-state cmm/initial-context-menu-state) + read-only? (mf/use-ctx ctx/workspace-read-only?) + components-v2 (mf/use-ctx ctx/components-v2) + new-css-system (mf/use-ctx ctx/new-css-system) + toggle-list-style (mf/use-ctx cmm/assets-toggle-list-style) + + selected (:components selected) + selected-full (into #{} (filter #(contains? selected (:id %))) components) + multi-components? (> (count selected) 1) + multi-assets? (or (seq (:graphics selected)) + (seq (:colors selected)) + (seq (:typographies selected))) + + groups (mf/with-memo [components reverse-sort?] + (grp/group-assets components reverse-sort?)) + + add-component + (mf/use-fn + (fn [] + (st/emit! (dw/set-assets-section-open file-id :components true)) + (dom/click (mf/ref-val input-ref)))) + + on-file-selected + (mf/use-fn + (mf/deps file-id) + (fn [blobs] + (let [params {:file-id file-id + :blobs (seq blobs)}] + (st/emit! (dwm/upload-media-components params) + (ptk/event ::ev/event {::ev/name "add-asset-to-library" + :asset-type "components"}))))) + + on-duplicate + (mf/use-fn + (mf/deps current-component-id selected) + (fn [] + (if (empty? selected) + (st/emit! (dwl/duplicate-component file-id current-component-id)) + (let [undo-id (js/Symbol)] + (st/emit! (dwu/start-undo-transaction undo-id)) + (run! st/emit! (map (partial dwl/duplicate-component file-id) selected)) + (st/emit! (dwu/commit-undo-transaction undo-id)))))) + + on-delete + (mf/use-fn + (mf/deps current-component-id file-id multi-components? multi-assets? on-assets-delete) + (fn [] + (let [undo-id (js/Symbol)] + (if (or multi-components? multi-assets?) + (on-assets-delete) + (st/emit! (dwu/start-undo-transaction undo-id) + (dwl/delete-component {:id current-component-id}) + (dwl/sync-file file-id file-id :components current-component-id) + (dwu/commit-undo-transaction undo-id)))))) + + on-close-menu + (mf/use-fn #(swap! menu-state cmm/close-context-menu)) + + on-rename + (mf/use-fn #(swap! state* assoc :renaming true)) + + cancel-rename + (mf/use-fn #(swap! state* dissoc :renaming)) + + do-rename + (mf/use-fn + (mf/deps current-component-id) + (fn [new-name] + (swap! state* dissoc :renaming) + (st/emit! + (dwl/rename-component-and-main-instance current-component-id new-name)))) + + on-context-menu + (mf/use-fn + (mf/deps selected on-clear-selection read-only?) + (fn [component-id event] + (dom/prevent-default event) + (let [pos (dom/get-client-position event)] + (when (and local? (not read-only?)) + (when-not (contains? selected component-id) + (on-clear-selection)) + + (swap! state* assoc :component-id component-id) + (swap! menu-state cmm/open-context-menu pos))))) + + create-group + (mf/use-fn + (mf/deps current-component-id components selected on-clear-selection) + (fn [group-name] + (on-clear-selection) + (let [undo-id (js/Symbol)] + (st/emit! (dwu/start-undo-transaction undo-id)) + (run! st/emit! + (->> components + (filter #(if multi-components? + (contains? selected (:id %)) + (= current-component-id (:id %)))) + (map #(dwl/rename-component + (:id %) + (cmm/add-group % group-name))))) + (st/emit! (dwu/commit-undo-transaction undo-id))))) + + rename-group + (mf/use-fn + (mf/deps components) + (fn [path last-path] + (on-clear-selection) + (let [undo-id (js/Symbol)] + (st/emit! (dwu/start-undo-transaction undo-id)) + (run! st/emit! + (->> components + (filter #(str/starts-with? (:path %) path)) + (map #(dwl/rename-component + (:id %) + (cmm/rename-group % path last-path))))) + (st/emit! (dwu/commit-undo-transaction undo-id))))) + + on-group + (mf/use-fn + (mf/deps components selected create-group) + (fn [event] + (dom/stop-propagation event) + (modal/show! :name-group-dialog {:accept create-group}))) + + on-rename-group + (mf/use-fn + (mf/deps components) + (fn [event path last-path] + (dom/stop-propagation event) + (modal/show! :name-group-dialog {:path path + :last-path last-path + :accept rename-group}))) + + on-ungroup + (mf/use-fn + (mf/deps components) + (fn [path] + (on-clear-selection) + (let [undo-id (js/Symbol)] + (st/emit! (dwu/start-undo-transaction undo-id)) + (run! st/emit! + (->> components + (filter #(str/starts-with? (:path %) path)) + (map #(dwl/rename-component (:id %) (cmm/ungroup % path))))) + (st/emit! (dwu/commit-undo-transaction undo-id))))) + + on-drag-start + (mf/use-fn + (mf/deps file-id) + (fn [component event] + (dnd/set-data! event "penpot/component" {:file-id file-id + :component component}) + (dnd/set-allowed-effect! event "move"))) + + on-show-main + (mf/use-fn + (mf/deps current-component-id file-id) + (fn [event] + (dom/stop-propagation event) + (st/emit! (dw/go-to-main-instance file-id current-component-id)))) + + on-asset-click + (mf/use-fn (mf/deps groups on-asset-click) (partial on-asset-click groups))] + + [:& cmm/asset-section {:file-id file-id + :title (tr "workspace.assets.components") + :section :components + :assets-count (count components) + :open? open?} + (if ^boolean new-css-system + [:& cmm/asset-section-block {:role :title-button} + + (when open? + [:div {:class (dom/classnames (css :listing-options) true)} + (let [option-selected (if listing-thumbs? + "grid" + "list")] + [:& radio-buttons {:selected option-selected + :on-change toggle-list-style + :name "listing-style"} + [:& radio-button {:icon (mf/html i/view-as-list-refactor) + :value "list" + :id :list}] + [:& radio-button {:icon (mf/html i/flex-grid-refactor) + :value "grid" + :id :grid}]])]) + (when (and components-v2 (not read-only?) local?) + [:div {:on-click add-component + :class (dom/classnames (css :add-component) true)} + i/add-refactor + [:& file-uploader {:accept cm/str-image-types + :multi true + :ref input-ref + :on-selected on-file-selected}]])] + (when local? + [:& cmm/asset-section-block {:role :title-button} + (when (and components-v2 (not read-only?)) + [:div.assets-button {:on-click add-component} + i/plus + [:& file-uploader {:accept cm/str-image-types + :multi true + :ref input-ref + :on-selected on-file-selected}]])])) + [:& cmm/asset-section-block {:role :content} + [:& components-group {:file-id file-id + :prefix "" + :groups groups + :open-groups open-groups + :renaming (when ^boolean renaming? current-component-id) + :listing-thumbs? listing-thumbs? + :selected selected + :on-asset-click on-asset-click + :on-drag-start on-drag-start + :do-rename do-rename + :cancel-rename cancel-rename + :on-rename-group on-rename-group + :on-group on-group + :on-ungroup on-ungroup + :on-context-menu on-context-menu + :selected-full selected-full}] + (when local? + [:& cmm/assets-context-menu + {:on-close on-close-menu + :state @menu-state + :options (if new-css-system + [(when-not (or multi-components? multi-assets?) + {:option-name (tr "workspace.assets.rename") + :id "assets-rename-component" + :option-handler on-rename}) + (when-not multi-assets? + {:option-name (if components-v2 + (tr "workspace.assets.duplicate-main") + (tr "workspace.assets.duplicate")) + :id "assets-duplicate-component" + :option-handler on-duplicate}) + + {:option-name (tr "workspace.assets.delete") + :id "assets-delete-component" + :option-handler on-delete} + (when-not multi-assets? + {:option-name (tr "workspace.assets.group") + :id "assets-group-component" + :option-handler on-group}) + + + (when (and components-v2 (not multi-assets?)) + {:option-name (tr "workspace.shape.menu.show-main") + :id "assets-show-main-component" + :option-handler on-show-main})] + + [(when-not (or multi-components? multi-assets?) + [(tr "workspace.assets.rename") on-rename]) + (when-not multi-assets? + [(if components-v2 + (tr "workspace.assets.duplicate-main") + (tr "workspace.assets.duplicate")) on-duplicate]) + [(tr "workspace.assets.delete") on-delete] + (when-not multi-assets? + [(tr "workspace.assets.group") on-group]) + (when (and components-v2 (not multi-assets?)) + [(tr "workspace.shape.menu.show-main") on-show-main])])}])]])) + diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/components.css.json b/frontend/src/app/main/ui/workspace/sidebar/assets/components.css.json new file mode 100644 index 0000000000..64e72325f1 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/components.css.json @@ -0,0 +1 @@ +{"button-primary":"assets_components_button-primary_lsoWq","button-secondary":"assets_components_button-secondary_P8v5X","button-tertiary":"assets_components_button-tertiary_7wMqf","add-component":"assets_components_add-component_X9o2C","button-tag":"assets_components_button-tag_ibmtY","button-icon":"assets_components_button-icon_4Lapr","button-icon-small":"assets_components_button-icon-small_7WrRR","component-group":"assets_components_component-group_AYXVI","asset-enum":"assets_components_asset-enum_iLlfH","enum-item":"assets_components_enum-item_l4zuE","item-name":"assets_components_item-name_Hwadc","editing":"assets_components_editing_3RdZy","asset-grid":"assets_components_asset-grid_mK75F","grid-cell":"assets_components_grid-cell_ctU6T","cell-name":"assets_components_cell-name_DUUMt","asset-element":"assets_components_asset-element_UsbdX","drop-space":"assets_components_drop-space_QhD1-","selected":"assets_components_selected_QLPO7","grid-placeholder":"assets_components_grid-placeholder_a3PoY","listing-options":"assets_components_listing-options_-vPIQ","listing-option-btn":"assets_components_listing-option-btn_-d9cg","first":"assets_components_first_sri1T","dragging":"assets_components_dragging_bWqQC"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/components.scss b/frontend/src/app/main/ui/workspace/sidebar/assets/components.scss new file mode 100644 index 0000000000..66ff32efac --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/components.scss @@ -0,0 +1,249 @@ +// 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"; + +.component-group { + .drop-space { + height: $s-12; + border-radius: $br-8; + background-color: var(--assets-item-background-color); + } + .asset-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-auto-rows: calc(10vh + $s-16); + gap: $s-4; + margin-left: $s-8; + margin-right: $s-12; + .grid-cell { + @include flexCenter; + position: relative; + padding: $s-8; + border: $s-2 solid transparent; + border-radius: $br-8; + background-color: var(--assets-item-background-color); + overflow: hidden; + cursor: pointer; + img { + height: auto; + width: auto; + max-height: 100%; + max-width: 100%; + pointer-events: none; + } + svg { + height: 10vh; + } + .cell-name { + @include titleTipography; + @include textEllipsis; + display: none; + position: absolute; + left: 0; + bottom: 0; + width: 100%; + padding: $s-2; + + &.editing { + display: flex; + align-items: center; + height: $s-32; + border: $s-1 solid var(--input-border-color-focus); + border-radius: $br-8; + background-color: var(--input-background-color); + input { + @include textEllipsis; + @include titleTipography; + @include removeInputStyle; + flex-grow: 1; + height: $s-28; + max-width: calc(var(--parent-size) - (var(--depth) * var(--layer-indentation-size))); + padding-left: $s-6; + margin: 0; + border-radius: $br-8; + color: var(--input-foreground-color); + } + span { + @include flexCenter; + height: $s-28; + background-color: transparent; + border-radius: $br-8; + svg { + @extend .button-icon-small; + stroke: var(--input-foreground-color); + transform: rotate(90deg); + } + } + } + } + + &:hover { + background-color: var(--assets-item-background-color-hover); + .cell-name { + display: block; + color: var(--assets-item-name-foreground-color-hover); + background: linear-gradient(to top, rgba(52, 57, 59, 1) 0%, rgba(52, 57, 59, 0) 100%); + &.editing { + display: flex; + background: var(--input-background-color); + input { + color: var(--input-foreground-color-active); + } + span svg { + stroke: var(--input-foreground-color-active); + } + } + } + } + + &.selected { + border: $s-1 solid var(--assets-item-border-color); + } + } + .grid-placeholder { + width: 100%; + border-radius: $br-8; + background-color: var(--assets-item-background-color); + } + } + .asset-enum { + margin: 0 $s-12; + .enum-item { + position: relative; + display: flex; + align-items: center; + height: $s-36; + margin-bottom: $s-4; + padding: $s-2; + border-radius: $br-8; + background-color: var(--assets-item-background-color); + cursor: pointer; + + svg, + img { + @include flexCenter; + padding: $s-2; + height: $s-32; + width: $s-32; + border-radius: $br-6; + background-color: var(--assets-item-background-color); + } + + .item-name { + @include titleTipography; + @include textEllipsis; + padding-left: $s-8; + color: var(--assets-item-name-foreground-color); + &.editing { + display: flex; + align-items: center; + height: $s-32; + border: $s-1 solid var(--input-border-color-focus); + border-radius: $br-8; + background-color: var(--input-background-color); + input { + @include textEllipsis; + @include titleTipography; + @include removeInputStyle; + flex-grow: 1; + height: $s-28; + max-width: calc(var(--parent-size) - (var(--depth) * var(--layer-indentation-size))); + padding-left: $s-6; + margin: 0; + border-radius: $br-8; + color: var(--input-foreground-color); + } + span { + @include flexCenter; + height: $s-28; + background-color: transparent; + border-radius: $br-8; + svg { + @extend .button-icon-small; + stroke: var(--input-foreground-color); + transform: rotate(90deg); + } + } + } + } + + &:hover { + background-color: var(--assets-item-background-color-hover); + .item-name { + color: var(--assets-item-name-foreground-color-hover); + &.editing { + background: var(--input-background-color); + input { + color: var(--input-foreground-color-active); + } + span svg { + stroke: var(--input-foreground-color-active); + } + } + } + } + &.selected { + border: $s-1 solid var(--assets-item-border-color); + } + } + .grid-placeholder { + height: $s-2; + width: 100%; + background-color: var(--color-accent-primary); + } + } +} +.listing-options { + display: flex; + align-items: center; + + .listing-option-btn { + @include flexCenter; + cursor: pointer; + + &.first { + margin-left: auto; + } + + svg { + height: 16px; + width: 16px; + } + } +} +.add-component { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + margin-left: $s-2; + border-radius: $br-8; + svg { + @extend .button-icon; + } +} +:global(.three-row) { + .asset-grid { + grid-template-columns: repeat(3, 1fr); + } +} + +:global(.four-row) { + .asset-grid { + grid-template-columns: repeat(4, 1fr); + } +} + +.dragging { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border-radius: $s-8; + background-color: var(--assets-item-background-color-drag); + border: $s-2 solid var(--assets-item-border-color-drag); +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs new file mode 100644 index 0000000000..a0252788e9 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs @@ -0,0 +1,420 @@ +;; 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 + +(ns app.main.ui.workspace.sidebar.assets.file-library + (:require-macros [app.main.style :refer [css]]) + (:require + [app.common.data :as d] + [app.common.data.macros :as dm] + [app.main.data.workspace :as dw] + [app.main.data.workspace.libraries :as dwl] + [app.main.data.workspace.undo :as dwu] + [app.main.refs :as refs] + [app.main.store :as st] + [app.main.ui.components.title-bar :refer [title-bar]] + [app.main.ui.context :as ctx] + [app.main.ui.icons :as i] + [app.main.ui.workspace.libraries :refer [create-file-library-ref]] + [app.main.ui.workspace.sidebar.assets.colors :refer [colors-section]] + [app.main.ui.workspace.sidebar.assets.common :as cmm] + [app.main.ui.workspace.sidebar.assets.components :refer [components-section]] + [app.main.ui.workspace.sidebar.assets.graphics :refer [graphics-section]] + [app.main.ui.workspace.sidebar.assets.typographies :refer [typographies-section]] + [app.util.dom :as dom] + [app.util.i18n :as i18n :refer [tr]] + [app.util.keyboard :as kbd] + [app.util.router :as rt] + [cuerdas.core :as str] + [okulary.core :as l] + [rumext.v2 :as mf])) + +(def lens:open-status + (l/derived (l/in [:workspace-assets :open-status]) st/state)) + +(def lens:selected + (-> (l/in [:workspace-assets :selected]) + (l/derived st/state))) + +(mf/defc file-library-title + {::mf/wrap-props false} + [{:keys [open? local? shared? project-id file-id page-id file-name]}] + (let [router (mf/deref refs/router) + url (rt/resolve router :workspace + {:project-id project-id + :file-id file-id} + {:page-id page-id}) + new-css-system (mf/use-ctx ctx/new-css-system) + toggle-open + (mf/use-fn + (mf/deps file-id open?) + (fn [] + (st/emit! (dw/set-assets-section-open file-id :library (not open?)))))] + (if ^boolean new-css-system + [:div {:class (dom/classnames (css :library-title) true)} + [:& title-bar {:collapsable? true + :collapsed? (not open?) + :on-collapsed toggle-open + :title (if local? + (mf/html [:div {:class (dom/classnames (css :special-title) true)} (tr "workspace.assets.local-library") + [:span {:class (dom/classnames (css :special-subtitle) true)} file-name]]) + + (mf/html [:div {:class (dom/classnames (css :special-title) true)} file-name]))} + (when-not local? + [:span.tool-link.tooltip.tooltip-left {:alt "Open library file"} + [:a {:class (dom/classnames (css :file-link) true) + :href (str "#" url) + :target "_blank" + :on-click dom/stop-propagation} + i/open-link-refactor]])]] + + [:div.tool-window-bar.library-bar + {:on-click toggle-open} + [:div.collapse-library + {:class (dom/classnames :open open?)} + i/arrow-slide] + + (if local? + [:* + [:span file-name " (" (tr "workspace.assets.local-library") ")"] + (when shared? + [:span.tool-badge (tr "workspace.assets.shared")])] + [:* + [:span file-name] + [:span.tool-link.tooltip.tooltip-left {:alt "Open library file"} + [:a {:href (str "#" url) + :target "_blank" + :on-click dom/stop-propagation} + i/chain]]])]))) + +(mf/defc file-library-content + {::mf/wrap-props false} + [{:keys [file local? open-status-ref on-clear-selection]}] + (let [components-v2 (mf/use-ctx ctx/components-v2) + new-css-system (mf/use-ctx ctx/new-css-system) + open-status (mf/deref open-status-ref) + + file-id (:id file) + project-id (:project-id file) + + filters (mf/use-ctx cmm/assets-filters) + filters-section (:section filters) + + filters-term (:term filters) + filters-ordering (:ordering filters) + filters-list-style (:list-style filters) + + reverse-sort? (= :desc filters-ordering) + listing-thumbs? (= :thumbs filters-list-style) + + toggle-ordering (mf/use-ctx cmm/assets-toggle-ordering) + toggle-list-style (mf/use-ctx cmm/assets-toggle-list-style) + + library-ref (mf/with-memo [file-id] + (create-file-library-ref file-id)) + + library (mf/deref library-ref) + colors (:colors library) + components (:components library) + media (:media library) + typographies (:typographies library) + + colors (mf/with-memo [filters colors] + (cmm/apply-filters colors filters)) + components (mf/with-memo [filters components] + (cmm/apply-filters components filters)) + media (mf/with-memo [filters media] + (cmm/apply-filters media filters)) + typographies (mf/with-memo [filters typographies] + (cmm/apply-filters typographies filters)) + + show-components? (and (or (= filters-section "all") + (= filters-section "components")) + (or (pos? (count components)) + (str/empty? filters-term))) + show-graphics? (and (or (= filters-section "all") + (= filters-section "graphics")) + (or (pos? (count media)) + (and (str/empty? filters-term) + (not components-v2)))) + show-colors? (and (or (= filters-section "all") + (= filters-section "colors")) + (or (> (count colors) 0) + (str/empty? filters-term))) + show-typography? (and (or (= filters-section "all") + (= filters-section "typographies")) + (or (pos? (count typographies)) + (str/empty? filters-term))) + + + selected-lens (mf/with-memo [file-id] + (-> (l/key file-id) + (l/derived lens:selected))) + + selected (mf/deref selected-lens) + selected-count (+ (count (get selected :components)) + (count (get selected :graphics)) + (count (get selected :colors)) + (count (get selected :typographies))) + + extend-selected + (fn [type asset-groups asset-id] + (letfn [(flatten-groups [groups] + (reduce concat [(get groups "" []) + (into [] + (->> (filter #(seq (first %)) groups) + (map second) + (mapcat flatten-groups)))]))] + + (let [selected' (get selected type)] + (if (zero? (count selected')) + (st/emit! (dw/select-single-asset file-id asset-id type)) + (let [all-assets (flatten-groups asset-groups) + click-index (d/index-of-pred all-assets #(= (:id %) asset-id)) + first-index (->> (get selected type) + (map (fn [asset] (d/index-of-pred all-assets #(= (:id %) asset)))) + (sort) + (first)) + + min-index (min first-index click-index) + max-index (max first-index click-index) + ids (->> (d/enumerate all-assets) + (into #{} (comp (filter #(<= min-index (first %) max-index)) + (map (comp :id second)))))] + + (st/emit! (dw/select-assets file-id ids type))))))) + + on-asset-click + (mf/use-fn + (mf/deps file-id extend-selected) + (fn [asset-type asset-groups asset-id default-click event] + (cond + (kbd/mod? event) + (do + (dom/stop-propagation event) + (st/emit! (dw/toggle-selected-assets file-id asset-id asset-type))) + + (kbd/shift? event) + (do + (dom/stop-propagation event) + (extend-selected asset-type asset-groups asset-id)) + + :else + (when default-click + (default-click event))))) + + on-component-click + (mf/use-fn (mf/deps on-asset-click) (partial on-asset-click :components)) + + on-graphics-click + (mf/use-fn (mf/deps on-asset-click) (partial on-asset-click :graphics)) + + on-colors-click + (mf/use-fn (mf/deps on-asset-click) (partial on-asset-click :colors)) + + on-typography-click + (mf/use-fn (mf/deps on-asset-click) (partial on-asset-click :typographies)) + + on-assets-delete + (mf/use-fn + (mf/deps selected file-id) + (fn [] + (let [undo-id (js/Symbol)] + (st/emit! (dwu/start-undo-transaction undo-id)) + (run! st/emit! (map #(dwl/delete-component {:id %}) + (:components selected))) + (run! st/emit! (map #(dwl/delete-media {:id %}) + (:graphics selected))) + (run! st/emit! (map #(dwl/delete-color {:id %}) + (:colors selected))) + (run! st/emit! (map #(dwl/delete-typography %) + (:typographies selected))) + + (when (or (seq (:components selected)) + (seq (:colors selected)) + (seq (:typographies selected))) + (st/emit! (dwl/sync-file file-id file-id))) + + (st/emit! (dwu/commit-undo-transaction undo-id)))))] + + (if ^boolean new-css-system + [:div {:class (dom/classnames (css :library-content) true)} + (when ^boolean show-components? + [:& components-section + {:file-id file-id + :local? local? + :components components + :listing-thumbs? listing-thumbs? + :open? (get open-status :components true) + :open-status-ref open-status-ref + :reverse-sort? reverse-sort? + :selected selected + :on-asset-click on-component-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection}]) + + (when ^boolean show-graphics? + [:& graphics-section + {:file-id file-id + :project-id project-id + :local? local? + :objects media + :listing-thumbs? listing-thumbs? + :open? (get open-status :graphics true) + :open-status-ref open-status-ref + :reverse-sort? reverse-sort? + :selected selected + :on-asset-click on-graphics-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection}]) + + (when ^boolean show-colors? + [:& colors-section + {:file-id file-id + :local? local? + :colors colors + :open? (get open-status :colors true) + :open-status-ref open-status-ref + :reverse-sort? reverse-sort? + :selected selected + :on-asset-click on-colors-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection}]) + + (when ^boolean show-typography? + [:& typographies-section + {:file file + :file-id (:id file) + :local? local? + :typographies typographies + :open? (get open-status :typographies true) + :open-status-ref open-status-ref + :reverse-sort? reverse-sort? + :selected selected + :on-asset-click on-typography-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection}]) + + (when (and (not ^boolean show-components?) + (not ^boolean show-graphics?) + (not ^boolean show-colors?) + (not ^boolean show-typography?)) + [:div {:class (css :asset-title)} (tr "workspace.assets.not-found")])] + + [:div.tool-window-content + [:div.listing-options + (when (> selected-count 0) + [:span.selected-count + (tr "workspace.assets.selected-count" (i18n/c selected-count))]) + [:div.listing-option-btn.first {:on-click toggle-ordering} + (if reverse-sort? + i/sort-ascending + i/sort-descending)] + [:div.listing-option-btn {:on-click toggle-list-style} + (if listing-thumbs? + i/listing-enum + i/listing-thumbs)]] + + (when ^boolean show-components? + [:& components-section + {:file-id file-id + :local? local? + :components components + :listing-thumbs? listing-thumbs? + :open? (get open-status :components true) + :open-status-ref open-status-ref + :reverse-sort? reverse-sort? + :selected selected + :on-asset-click on-component-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection}]) + + (when ^boolean show-graphics? + [:& graphics-section + {:file-id file-id + :project-id project-id + :local? local? + :objects media + :listing-thumbs? listing-thumbs? + :open? (get open-status :graphics true) + :open-status-ref open-status-ref + :reverse-sort? reverse-sort? + :selected selected + :on-asset-click on-graphics-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection}]) + + (when ^boolean show-colors? + [:& colors-section + {:file-id file-id + :local? local? + :colors colors + :open? (get open-status :colors true) + :open-status-ref open-status-ref + :reverse-sort? reverse-sort? + :selected selected + :on-asset-click on-colors-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection}]) + + (when ^boolean show-typography? + [:& typographies-section + {:file file + :file-id (:id file) + :local? local? + :typographies typographies + :open? (get open-status :typographies true) + :open-status-ref open-status-ref + :reverse-sort? reverse-sort? + :selected selected + :on-asset-click on-typography-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection}]) + + (when (and (not ^boolean show-components?) + (not ^boolean show-graphics?) + (not ^boolean show-colors?) + (not ^boolean show-typography?)) + [:div.asset-section + [:div.asset-title (tr "workspace.assets.not-found")]])]))) + +(mf/defc file-library + {::mf/wrap-props false} + [{:keys [file local? default-open? filters]}] + (let [file-id (:id file) + file-name (:name file) + shared? (:is-shared file) + project-id (:project-id file) + page-id (dm/get-in file [:data :pages 0]) + + open-status-ref (mf/with-memo [file-id] + (-> (l/key file-id) + (l/derived lens:open-status))) + open-status (mf/deref open-status-ref) + open? (d/nilv (:library open-status) default-open?) + + unselect-all + (mf/use-fn + (mf/deps file-id) + (fn [] + (st/emit! (dw/unselect-all-assets file-id))))] + + [:div.tool-window {:on-context-menu dom/prevent-default + :on-click unselect-all} + [:& file-library-title + {:project-id project-id + :file-id file-id + :page-id page-id + :file-name file-name + :open? open? + :local? local? + :shared? shared?}] + (when ^boolean open? + [:& file-library-content + {:file file + :local? local? + :filters filters + :on-clear-selection unselect-all + :open-status-ref open-status-ref}])])) \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.css.json b/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.css.json new file mode 100644 index 0000000000..6cbfca0e4d --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.css.json @@ -0,0 +1 @@ +{"button-primary":"assets_file_library_button-primary_o8czr","button-secondary":"assets_file_library_button-secondary_BCBmw","button-tertiary":"assets_file_library_button-tertiary_JiCQn","library-title":"assets_file_library_library-title_FvGs6","file-link":"assets_file_library_file-link_CtN0K","button-tag":"assets_file_library_button-tag_cyg7Q","button-icon":"assets_file_library_button-icon_R-4R0","button-icon-small":"assets_file_library_button-icon-small_9UOdy","asset-element":"assets_file_library_asset-element_6ian7","file-name":"assets_file_library_file-name_Pc8ng","special-title":"assets_file_library_special-title_-Pqzq","special-subtitle":"assets_file_library_special-subtitle_9xOl9","library-content":"assets_file_library_library-content_Yto-8","asset-title":"assets_file_library_asset-title_ozD8M"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.scss b/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.scss new file mode 100644 index 0000000000..deb65469d0 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.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"; + +.library-title { + .file-name { + @include titleTipography; + display: flex; + justify-content: flex-start; + align-items: center; + flex-grow: 100; + height: 100%; + } + + .special-title { + color: var(--title-foreground-color-hover); + } + .special-subtitle { + padding-left: $s-4; + color: var(--title-foreground-color); + text-transform: initial; + } + .file-link { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + margin-right: $s-12; + border-radius: $br-8; + svg { + @extend .button-icon-small; + fill: var(--title-foreground-color-hover); + } + } +} +.library-content { + display: flex; + flex-direction: column; + height: 100%; + width: 100%; + overflow-y: auto; + overflow-x: hidden; +} + +.asset-title { + @include titleTipography; + margin-left: $s-28; +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/graphics.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/graphics.cljs new file mode 100644 index 0000000000..a1ba98b2c5 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/graphics.cljs @@ -0,0 +1,553 @@ +;; 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 + +(ns app.main.ui.workspace.sidebar.assets.graphics + (:require-macros [app.main.style :refer [css]]) + (:require + [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.media :as cm] + [app.common.pages.helpers :as cph] + [app.config :as cf] + [app.main.data.events :as ev] + [app.main.data.modal :as modal] + [app.main.data.workspace :as dw] + [app.main.data.workspace.libraries :as dwl] + [app.main.data.workspace.media :as dwm] + [app.main.data.workspace.undo :as dwu] + [app.main.store :as st] + [app.main.ui.components.editable-label :refer [editable-label]] + [app.main.ui.components.file-uploader :refer [file-uploader]] + [app.main.ui.context :as ctx] + [app.main.ui.hooks :as h] + [app.main.ui.icons :as i] + [app.main.ui.workspace.sidebar.assets.common :as cmm] + [app.main.ui.workspace.sidebar.assets.groups :as grp] + [app.util.dom :as dom] + [app.util.dom.dnd :as dnd] + [app.util.i18n :as i18n :refer [tr]] + [cuerdas.core :as str] + [okulary.core :as l] + [potok.core :as ptk] + [rumext.v2 :as mf])) + +(mf/defc graphics-item + [{:keys [object renaming listing-thumbs? selected-objects file-id + on-asset-click on-context-menu on-drag-start do-rename cancel-rename + selected-full selected-graphics-paths]}] + (let [item-ref (mf/use-ref) + visible? (h/use-visible item-ref :once? true) + object-id (:id object) + + dragging* (mf/use-state false) + dragging? (deref dragging*) + + read-only? (mf/use-ctx ctx/workspace-read-only?) + new-css-system (mf/use-ctx ctx/new-css-system) + + on-drop + (mf/use-fn + (mf/deps object dragging* selected-objects selected-full selected-graphics-paths) + (fn [event] + (cmm/on-drop-asset event object dragging* selected-objects selected-full + selected-graphics-paths dwl/rename-media))) + + on-drag-enter + (mf/use-fn + (mf/deps object dragging* selected-objects selected-graphics-paths) + (fn [event] + (cmm/on-drag-enter-asset event object dragging* selected-objects selected-graphics-paths))) + + on-drag-leave + (mf/use-fn + (mf/deps dragging*) + (fn [event] + (cmm/on-drag-leave-asset event dragging*))) + + on-grahic-drag-start + (mf/use-fn + (mf/deps object file-id selected-objects item-ref on-drag-start read-only?) + (fn [event] + (if read-only? + (dom/prevent-default event) + (cmm/on-asset-drag-start event file-id object selected-objects item-ref :graphics on-drag-start)))) + + on-context-menu + (mf/use-fn + (mf/deps object-id) + (partial on-context-menu object-id)) + + on-asset-click + (mf/use-fn + (mf/deps object-id on-asset-click) + (partial on-asset-click object-id nil))] + (if ^boolean new-css-system + [:div {:ref item-ref + :class-name (dom/classnames + (css :selected) (contains? selected-objects object-id) + (css :grid-cell) listing-thumbs? + (css :enum-item) (not listing-thumbs?)) + :draggable (not read-only?) + :on-click on-asset-click + :on-context-menu on-context-menu + :on-drag-start on-grahic-drag-start + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + + (when visible? + [:* + [:img {:src (when visible? (cf/resolve-file-media object true)) + :class (css :graphic-image) + :draggable false}] ;; Also need to add css pointer-events: none + + (let [renaming? (= renaming (:id object))] + [:* + [:& editable-label + {:class-name (dom/classnames + (css :cell-name) listing-thumbs? + (css :item-name) (not listing-thumbs?) + (css :editing) renaming?) + :value (cph/merge-path-item (:path object) (:name object)) + :tooltip (cph/merge-path-item (:path object) (:name object)) + :display-value (:name object) + :editing? renaming? + :disable-dbl-click? true + :on-change do-rename + :on-cancel cancel-rename}] + + (when ^boolean dragging? + [:div {:class (dom/classnames (css :dragging) true)}])])])] + + [:div {:ref item-ref + :class-name (dom/classnames + :selected (contains? selected-objects object-id) + :grid-cell listing-thumbs? + :enum-item (not listing-thumbs?)) + :draggable (not read-only?) + :on-click on-asset-click + :on-context-menu on-context-menu + :on-drag-start on-grahic-drag-start + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + + (when visible? + [:* + [:img {:src (when visible? (cf/resolve-file-media object true)) + :draggable false}] ;; Also need to add css pointer-events: none + + (let [renaming? (= renaming (:id object))] + [:* + [:& editable-label + {:class-name (dom/classnames + :cell-name listing-thumbs? + :item-name (not listing-thumbs?) + :editing renaming?) + :value (cph/merge-path-item (:path object) (:name object)) + :tooltip (cph/merge-path-item (:path object) (:name object)) + :display-value (:name object) + :editing? renaming? + :disable-dbl-click? true + :on-change do-rename + :on-cancel cancel-rename}] + + (when ^boolean dragging? + [:div.dragging])])])]))) + +(mf/defc graphics-group + [{:keys [file-id prefix groups open-groups renaming listing-thumbs? selected-objects on-asset-click + on-drag-start do-rename cancel-rename on-rename-group on-ungroup + on-context-menu selected-full]}] + (let [group-open? (get open-groups prefix true) + new-css-system (mf/use-ctx ctx/new-css-system) + dragging* (mf/use-state false) + dragging? (deref dragging*) + + selected-paths + (mf/with-memo [selected-full] + (into #{} + (comp (map :path) (d/nilv "")) + selected-full)) + + on-drag-enter + (mf/use-fn + (mf/deps dragging* prefix selected-paths) + (fn [event] + (cmm/on-drag-enter-asset-group event dragging* prefix selected-paths))) + + on-drag-leave + (mf/use-fn + (mf/deps dragging*) + (fn [event] + (cmm/on-drag-leave-asset event dragging*))) + + on-drop + (mf/use-fn + (mf/deps dragging* prefix selected-paths selected-full) + (fn [event] + (cmm/on-drop-asset-group event dragging* prefix selected-paths selected-full dwl/rename-media)))] + (if ^boolean new-css-system + [:div {:class (dom/classnames (css :graphics-group) true) + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + [:& grp/asset-group-title + {:file-id file-id + :section :graphics + :path prefix + :group-open? group-open? + :on-rename on-rename-group + :on-ungroup on-ungroup}] + (when group-open? + [:* + (let [objects (get groups "" [])] + [:div {:class-name (dom/classnames + (css :asset-grid) listing-thumbs? + (css :asset-enum) (not listing-thumbs?) + (css :drop-space) (and + (empty? objects) + (some? groups) + (not dragging?))) + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + + (when ^boolean dragging? + [:div {:class (dom/classnames (css :grid-placeholder) true)} "\u00A0"]) + + (when (and (empty? objects) + (some? groups)) + [:div {:class (dom/classnames (css :drop-space) true)}]) + + (for [object objects] + [:& graphics-item + {:key (dm/str "object-" (:id object)) + :file-id file-id + :object object + :renaming renaming + :listing-thumbs? listing-thumbs? + :selected-objects selected-objects + :on-asset-click on-asset-click + :on-context-menu on-context-menu + :on-drag-start on-drag-start + :do-rename do-rename + :cancel-rename cancel-rename + :selected-full selected-full + :selected-paths selected-paths}])]) + (for [[path-item content] groups] + (when-not (empty? path-item) + [:& graphics-group {:file-id file-id + :key path-item + :prefix (cph/merge-path-item prefix path-item) + :groups content + :open-groups open-groups + :renaming renaming + :listing-thumbs? listing-thumbs? + :selected-objects selected-objects + :on-asset-click on-asset-click + :on-drag-start on-drag-start + :do-rename do-rename + :cancel-rename cancel-rename + :on-rename-group on-rename-group + :on-ungroup on-ungroup + :on-context-menu on-context-menu + :selected-full selected-full + :selected-paths selected-paths}]))])] + + [:div {:on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + [:& grp/asset-group-title {:file-id file-id + :section :graphics + :path prefix + :group-open? group-open? + :on-rename on-rename-group + :on-ungroup on-ungroup}] + (when group-open? + [:* + (let [objects (get groups "" [])] + [:div {:class-name (dom/classnames + :asset-grid listing-thumbs? + :asset-enum (not listing-thumbs?) + :drop-space (and + (empty? objects) + (some? groups) + (not dragging?))) + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + + (when ^boolean dragging? + [:div.grid-placeholder "\u00A0"]) + + (when (and (empty? objects) + (some? groups)) + [:div.drop-space]) + + (for [object objects] + [:& graphics-item {:key (dm/str "object-" (:id object)) + :file-id file-id + :object object + :renaming renaming + :listing-thumbs? listing-thumbs? + :selected-objects selected-objects + :on-asset-click on-asset-click + :on-context-menu on-context-menu + :on-drag-start on-drag-start + :do-rename do-rename + :cancel-rename cancel-rename + :selected-full selected-full + :selected-paths selected-paths}])]) + (for [[path-item content] groups] + (when-not (empty? path-item) + [:& graphics-group {:file-id file-id + :key path-item + :prefix (cph/merge-path-item prefix path-item) + :groups content + :open-groups open-groups + :renaming renaming + :listing-thumbs? listing-thumbs? + :selected-objects selected-objects + :on-asset-click on-asset-click + :on-drag-start on-drag-start + :do-rename do-rename + :cancel-rename cancel-rename + :on-rename-group on-rename-group + :on-ungroup on-ungroup + :on-context-menu on-context-menu + :selected-full selected-full + :selected-paths selected-paths}]))])]))) + +(mf/defc graphics-section + {::mf/wrap-props false} + [{:keys [file-id project-id local? objects listing-thumbs? open? open-status-ref selected reverse-sort? + on-asset-click on-assets-delete on-clear-selection]}] + (let [input-ref (mf/use-ref nil) + state (mf/use-state {:renaming nil :object-id nil}) + + menu-state (mf/use-state cmm/initial-context-menu-state) + read-only? (mf/use-ctx ctx/workspace-read-only?) + + open-groups-ref (mf/with-memo [open-status-ref] + (-> (l/in [:groups :graphics]) + (l/derived open-status-ref))) + open-groups (mf/deref open-groups-ref) + + selected (:graphics selected) + selected-full (into #{} (filter #(contains? selected (:id %))) objects) + multi-objects? (> (count selected) 1) + multi-assets? (or (seq (:components selected)) + (seq (:colors selected)) + (seq (:typographies selected))) + + objects (mf/with-memo [objects] + (mapv dwl/extract-path-if-missing objects)) + + groups (mf/with-memo [objects reverse-sort?] + (grp/group-assets objects reverse-sort?)) + + components-v2 (mf/use-ctx ctx/components-v2) + team-id (mf/use-ctx ctx/current-team-id) + new-css-system (mf/use-ctx ctx/new-css-system) + + add-graphic + (mf/use-fn + (fn [] + (st/emit! (dw/set-assets-section-open file-id :graphics true)) + (dom/click (mf/ref-val input-ref)))) + + on-file-selected + (mf/use-fn + (mf/deps file-id project-id team-id) + (fn [blobs] + (let [params {:file-id file-id + :blobs (seq blobs)}] + (st/emit! (dwm/upload-media-asset params) + (ptk/event ::ev/event {::ev/name "add-asset-to-library" + :asset-type "graphics" + :file-id file-id + :project-id project-id + :team-id team-id}))))) + on-delete + (mf/use-fn + (mf/deps @state multi-objects? multi-assets?) + (fn [] + (if (or multi-objects? multi-assets?) + (on-assets-delete) + (st/emit! (dwl/delete-media {:id (:object-id @state)}))))) + + on-rename + (mf/use-fn + (fn [] + (swap! state (fn [state] + (assoc state :renaming (:component-id state)))))) + cancel-rename + (mf/use-fn + (fn [] + (swap! state assoc :renaming nil))) + + do-rename + (mf/use-fn + (mf/deps @state) + (fn [new-name] + (st/emit! (dwl/rename-media (:renaming @state) new-name)) + (swap! state assoc :renaming nil))) + + on-context-menu + (mf/use-fn + (mf/deps selected on-clear-selection read-only?) + (fn [object-id event] + (dom/prevent-default event) + (let [pos (dom/get-client-position event)] + (when (and local? (not read-only?)) + (when-not (contains? selected object-id) + (on-clear-selection)) + (swap! state assoc :object-id object-id) + (swap! menu-state cmm/open-context-menu pos))))) + + on-close-menu + (mf/use-fn + (fn [] + (swap! menu-state cmm/close-context-menu))) + + create-group + (mf/use-fn + (mf/deps objects selected on-clear-selection (:object-id @state)) + (fn [group-name] + (on-clear-selection) + (let [undo-id (js/Symbol)] + (st/emit! (dwu/start-undo-transaction undo-id)) + (run! st/emit! + (->> objects + (filter #(if multi-objects? + (contains? selected (:id %)) + (= (:object-id @state) (:id %)))) + (map #(dwl/rename-media (:id %) (cmm/add-group % group-name))))) + (st/emit! (dwu/commit-undo-transaction undo-id))))) + + rename-group + (mf/use-fn + (mf/deps objects) + (fn [path last-path] + (on-clear-selection) + (let [undo-id (js/Symbol)] + (st/emit! (dwu/start-undo-transaction undo-id)) + (run! st/emit! + (->> objects + (filter #(str/starts-with? (:path %) path)) + (map #(dwl/rename-media (:id %) (cmm/rename-group % path last-path))))) + (st/emit! (dwu/commit-undo-transaction undo-id))))) + + on-group + (mf/use-fn + (mf/deps objects selected create-group) + (fn [event] + (dom/stop-propagation event) + (modal/show! :name-group-dialog {:accept create-group}))) + + on-rename-group + (mf/use-fn + (mf/deps objects) + (fn [event path last-path] + (dom/stop-propagation event) + (modal/show! :name-group-dialog {:path path + :last-path last-path + :accept rename-group}))) + on-ungroup + (mf/use-fn + (mf/deps objects) + (fn [path] + (on-clear-selection) + (let [undo-id (js/Symbol)] + (st/emit! (dwu/start-undo-transaction undo-id)) + (run! st/emit! + (->> objects + (filter #(str/starts-with? (:path %) path)) + (map #(dwl/rename-media (:id %) (cmm/ungroup % path))))) + (st/emit! (dwu/commit-undo-transaction undo-id))))) + + on-drag-start + (mf/use-fn + (fn [{:keys [name id mtype]} event] + (dnd/set-data! event "text/asset-id" (str id)) + (dnd/set-data! event "text/asset-name" name) + (dnd/set-data! event "text/asset-type" mtype) + (dnd/set-allowed-effect! event "move"))) + + on-asset-click + (mf/use-fn (mf/deps groups on-asset-click) (partial on-asset-click groups))] + + [:& cmm/asset-section {:file-id file-id + :title (tr "workspace.assets.graphics") + :section :graphics + :assets-count (count objects) + :open? open?} + (if new-css-system + (when local? + [:& cmm/asset-section-block {:role :title-button} + (when (and (not components-v2) (not read-only?)) + [:button {:class (dom/classnames (css :assets-btn) true) + :on-click add-graphic} + i/add-refactor + [:& file-uploader {:accept cm/str-image-types + :multi true + :ref input-ref + :on-selected on-file-selected}]])]) + (when local? + [:& cmm/asset-section-block {:role :title-button} + (when (and (not components-v2) (not read-only?)) + [:div.assets-button {:on-click add-graphic} + i/plus + [:& file-uploader {:accept cm/str-image-types + :multi true + :ref input-ref + :on-selected on-file-selected}]])])) + + [:& cmm/asset-section-block {:role :content} + [:& graphics-group {:file-id file-id + :prefix "" + :groups groups + :open-groups open-groups + :renaming (:renaming @state) + :listing-thumbs? listing-thumbs? + :selected selected + :on-asset-click on-asset-click + :on-drag-start on-drag-start + :do-rename do-rename + :cancel-rename cancel-rename + :on-rename-group on-rename-group + :on-ungroup on-ungroup + :on-context-menu on-context-menu + :selected-full selected-full}] + (when local? + [:& cmm/assets-context-menu + {:on-close on-close-menu + :state @menu-state + :options (if new-css-system + [(when-not (or multi-objects? multi-assets?) + {:option-name (tr "workspace.assets.rename") + :id "assets-rename-graphics" + :option-handler on-rename}) + {:option-name (tr "workspace.assets.delete") + :id "assets-delete-graphics" + :option-handler on-delete} + (when-not multi-assets? + {:option-name (tr "workspace.assets.group") + :id "assets-group-graphics" + :option-handler on-group})] + + [(when-not (or multi-objects? multi-assets?) + [(tr "workspace.assets.rename") on-rename]) + [(tr "workspace.assets.delete") on-delete] + (when-not multi-assets? + [(tr "workspace.assets.group") on-group])])}])]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/graphics.css.json b/frontend/src/app/main/ui/workspace/sidebar/assets/graphics.css.json new file mode 100644 index 0000000000..0917c975f5 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/graphics.css.json @@ -0,0 +1 @@ +{"button-primary":"assets_graphics_button-primary_6qIO6","button-secondary":"assets_graphics_button-secondary_0qkG4","button-tertiary":"assets_graphics_button-tertiary_bWZ1s","assets-btn":"assets_graphics_assets-btn_BIoeo","add-component":"assets_graphics_add-component_bgwrr","button-tag":"assets_graphics_button-tag_K3ckf","button-icon":"assets_graphics_button-icon_En5qq","button-icon-small":"assets_graphics_button-icon-small_xNHzC","asset-element":"assets_graphics_asset-element_-VwmF","graphics-group":"assets_graphics_graphics-group_kibPf","drop-space":"assets_graphics_drop-space_2UAKf","asset-grid":"assets_graphics_asset-grid_6ET0K","grid-cell":"assets_graphics_grid-cell_EHW4x","cell-name":"assets_graphics_cell-name_JgbUS","editing":"assets_graphics_editing_O-Ozt","editable-label-input":"assets_graphics_editable-label-input_Yc2zA","editable-label-close":"assets_graphics_editable-label-close_16VT8","selected":"assets_graphics_selected_Q9YJC","dragging":"assets_graphics_dragging_oVA41","asset-enum":"assets_graphics_asset-enum_TS6Je","enum-item":"assets_graphics_enum-item_UFh4c","item-name":"assets_graphics_item-name_HGIQs","grid-placeholder":"assets_graphics_grid-placeholder_9brkO","listing-options":"assets_graphics_listing-options_Jw51P","listing-option-btn":"assets_graphics_listing-option-btn_3IkTO","first":"assets_graphics_first_kU3zf"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/graphics.scss b/frontend/src/app/main/ui/workspace/sidebar/assets/graphics.scss new file mode 100644 index 0000000000..2ac0a04d8d --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/graphics.scss @@ -0,0 +1,190 @@ +// 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"; + +.graphics-group { + .drop-space { + height: $s-12; + border-radius: $br-8; + background-color: var(--assets-item-background-color); + } + .asset-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: $s-4; + margin-left: $s-8; + margin-right: $s-12; + .grid-cell { + @include flexCenter; + position: relative; + padding: $s-8; + border: $s-2 solid transparent; + border-radius: $br-8; + aspect-ratio: 1/1; + background-color: var(--assets-item-background-color); + overflow: hidden; + cursor: pointer; + img { + height: auto; + width: auto; + max-height: 100%; + max-width: 100%; + pointer-events: none; + } + svg { + height: 10vh; + } + .cell-name { + @include titleTipography; + @include textEllipsis; + display: none; + position: absolute; + left: 0; + bottom: 0; + width: 100%; + padding: $s-2; + + &.editing { + display: block; + } + + .editable-label-input { + height: unset; + width: 100%; + padding: $s-2; + margin: 0; + } + + .editable-label-close { + display: none; + } + } + + &:hover { + background-color: var(--assets-item-background-color-hover); + .cell-name { + display: block; + color: var(--assets-item-name-foreground-color-hover); + background: linear-gradient(to top, rgba(52, 57, 59, 1) 0%, rgba(52, 57, 59, 0) 100%); + } + } + + &.selected { + border: $s-1 solid var(--assets-item-border-color); + } + + .dragging { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border-radius: $s-8; + background-color: var(--assets-item-background-color-drag); + border: $s-2 solid var(--assets-item-border-color-drag); + } + } + } + .asset-enum { + margin: 0 $s-12; + + .enum-item { + position: relative; + display: flex; + align-items: center; + height: $s-36; + margin-bottom: $s-4; + padding: $s-2; + border-radius: $br-8; + background-color: var(--assets-item-background-color); + cursor: pointer; + + svg, + img { + @include flexCenter; + padding: $s-2; + height: $s-32; + width: $s-32; + border-radius: $br-6; + background-color: var(--assets-component-background-color); + } + + .item-name { + @include titleTipography; + @include textEllipsis; + padding-left: $s-8; + color: var(--assets-item-name-foreground-color); + + &.editing { + display: flex; + align-items: center; + + .editable-label-input { + height: $s-24; + } + + .editable-label-close { + display: none; + } + } + } + &:hover { + background-color: var(--assets-item-background-color-hover); + .item-name { + color: var(--assets-item-name-foreground-color-hover); + } + } + &.selected { + border: $s-1 solid var(--assets-item-border-color); + } + } + } + .grid-placeholder { + height: $s-2; + background-color: var(--color-accent-primary); + margin-bottom: $s-2; + } +} +.listing-options { + display: flex; + align-items: center; + + .listing-option-btn { + @include flexCenter; + cursor: pointer; + + &.first { + margin-left: auto; + } + + svg { + height: $s-16; + width: $s-16; + } + } +} +.add-component { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + margin-left: $s-2; + border-radius: $br-8; + svg { + @extend .button-icon; + } +} + +.assets-btn { + @extend .button-tertiary; + height: $s-32; + width: calc($s-24 + $s-4); + padding: 0; + border-radius: $br-8; + svg { + @extend .button-icon; + } +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/groups.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/groups.cljs new file mode 100644 index 0000000000..6f41143ac5 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/groups.cljs @@ -0,0 +1,171 @@ +;; 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 + +(ns app.main.ui.workspace.sidebar.assets.groups + (:require-macros [app.main.style :refer [css]]) + (:require + [app.common.pages.helpers :as cph] + [app.common.spec :as us] + [app.main.data.modal :as modal] + [app.main.data.workspace :as dw] + [app.main.store :as st] + [app.main.ui.components.forms :as fm] + [app.main.ui.components.title-bar :refer [title-bar]] + [app.main.ui.context :as ctx] + [app.main.ui.icons :as i] + [app.main.ui.workspace.sidebar.assets.common :as cmm] + [app.util.dom :as dom] + [app.util.i18n :as i18n :refer [tr]] + [cljs.spec.alpha :as s] + [rumext.v2 :as mf])) + +(mf/defc asset-group-title + [{:keys [file-id section path group-open? on-rename on-ungroup]}] + (when-not (empty? path) + (let [[other-path last-path truncated] (cph/compact-path path 35 true) + menu-state (mf/use-state cmm/initial-context-menu-state) + new-css-system (mf/use-ctx ctx/new-css-system) + on-fold-group + (mf/use-fn + (mf/deps file-id section path group-open?) + (fn [event] + (dom/stop-propagation event) + (st/emit! (dw/set-assets-group-open file-id + section + path + (not group-open?))))) + on-context-menu + (mf/use-fn + (fn [event] + (dom/prevent-default event) + (let [pos (dom/get-client-position event)] + (swap! menu-state cmm/open-context-menu pos)))) + + 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) + :on-context-menu on-context-menu} + [:& title-bar {:collapsable? true + :collapsed? (not group-open?) + :on-collapsed on-fold-group + :title (mf/html [:* (when-not (empty? other-path) + [:span {:class (dom/classnames (css :pre-path) true) + :title (when truncated path)} + other-path "\u00A0\u2022\u00A0"]) + [:span {:class (dom/classnames (css :path) true) + :title (when truncated path)} + last-path]])}] + [:& cmm/assets-context-menu + {:on-close on-close-menu + :state @menu-state + :options [{:option-name (tr "workspace.assets.rename") + :id "assets-rename-group" + :option-handler #(on-rename % path last-path)} + {:option-name (tr "workspace.assets.ungroup") + :id "assets-ungroup-group" + :option-handler #(on-ungroup path)}]}]] + + + [:div.group-title {:class (when-not group-open? "closed") + :on-click on-fold-group + :on-context-menu on-context-menu} + [:span i/arrow-slide] + (when-not (empty? other-path) + [:span.dim {:title (when truncated path)} + other-path "\u00A0/\u00A0"]) + [:span {:title (when truncated path)} + last-path] + [:& cmm/assets-context-menu + {:on-close on-close-menu + :state @menu-state + :options [[(tr "workspace.assets.rename") #(on-rename % path last-path)] + [(tr "workspace.assets.ungroup") #(on-ungroup path)]]}]])))) + +(defn group-assets + "Convert a list of assets in a nested structure like this: + + {'': [{assetA} {assetB}] + 'group1': {'': [{asset1A} {asset1B}] + 'subgroup11': {'': [{asset11A} {asset11B} {asset11C}]} + 'subgroup12': {'': [{asset12A}]}} + 'group2': {'subgroup21': {'': [{asset21A}}}} + " + [assets reverse-sort?] + (when-not (empty? assets) + (reduce (fn [groups {:keys [path] :as asset}] + (let [path (cph/split-path (or path ""))] + (update-in groups + (conj path "") + (fn [group] + (if group + (conj group asset) + [asset]))))) + (sorted-map-by (fn [key1 key2] + (if reverse-sort? + (compare key2 key1) + (compare key1 key2)))) + assets))) + +(s/def ::asset-name ::us/not-empty-string) +(s/def ::name-group-form + (s/keys :req-un [::asset-name])) + +(mf/defc name-group-dialog + {::mf/register modal/components + ::mf/register-as :name-group-dialog} + [{:keys [path last-path accept] :as ctx + :or {path "" last-path ""}}] + (let [initial (mf/use-memo + (mf/deps last-path) + (constantly {:asset-name last-path})) + form (fm/use-form :spec ::name-group-form + :initial initial) + + create? (empty? path) + + on-close (mf/use-fn #(modal/hide!)) + + on-accept + (mf/use-fn + (mf/deps form) + (fn [_] + (let [asset-name (get-in @form [:clean-data :asset-name])] + (if create? + (accept asset-name) + (accept path asset-name)) + (modal/hide!))))] + + [: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.css.json b/frontend/src/app/main/ui/workspace/sidebar/assets/groups.css.json new file mode 100644 index 0000000000..c6166fbe30 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/groups.css.json @@ -0,0 +1 @@ +{"button-primary":"assets_groups_button-primary_2o3Db","button-secondary":"assets_groups_button-secondary_-qdxB","button-tertiary":"assets_groups_button-tertiary_1f4Jy","button-tag":"assets_groups_button-tag_yIgd9","button-icon":"assets_groups_button-icon_MSptS","button-icon-small":"assets_groups_button-icon-small_73Ir0","asset-element":"assets_groups_asset-element_RgKXH","group-title":"assets_groups_group-title_cV4AQ","pre-path":"assets_groups_pre-path_1rE71","path":"assets_groups_path_m0esc"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/groups.scss b/frontend/src/app/main/ui/workspace/sidebar/assets/groups.scss new file mode 100644 index 0000000000..5ba5138060 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/groups.scss @@ -0,0 +1,19 @@ +// 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"; + +.group-title { + padding-left: $s-16; + .pre-path { + text-transform: initial; + color: var(--title-foreground-color); + } + .path { + text-transform: initial; + color: var(--title-foreground-color-hover); + } +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.cljs new file mode 100644 index 0000000000..ab8da5c9aa --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.cljs @@ -0,0 +1,553 @@ +;; 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 + +(ns app.main.ui.workspace.sidebar.assets.typographies + (:require-macros [app.main.style :refer [css]]) + (:require + [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.pages.helpers :as cph] + [app.main.data.modal :as modal] + [app.main.data.workspace.libraries :as dwl] + [app.main.data.workspace.texts :as dwt] + [app.main.data.workspace.undo :as dwu] + [app.main.refs :as refs] + [app.main.store :as st] + [app.main.ui.context :as ctx] + [app.main.ui.icons :as i] + [app.main.ui.workspace.sidebar.assets.common :as cmm] + [app.main.ui.workspace.sidebar.assets.groups :as grp] + [app.main.ui.workspace.sidebar.options.menus.typography :refer [typography-entry]] + [app.util.dom :as dom] + [app.util.i18n :as i18n :refer [tr]] + [cuerdas.core :as str] + [okulary.core :as l] + [rumext.v2 :as mf])) + +(def lens:typography-section-state + (l/derived (fn [gstate] + {:rename-typography (:rename-typography gstate) + :edit-typography (:edit-typography gstate)}) + refs/workspace-global + =)) + +(mf/defc typography-item + {::mf/wrap-props false} + [{:keys [typography file-id local? handle-change selected apply-typography editing-id renaming-id on-asset-click + on-context-menu selected-full selected-paths move-typography rename?]}] + (let [item-ref (mf/use-ref) + typography-id (:id typography) + + dragging* (mf/use-state false) + dragging? (deref dragging*) + + read-only? (mf/use-ctx ctx/workspace-read-only?) + new-css-system (mf/use-ctx ctx/new-css-system) + editing? (= editing-id (:id typography)) + renaming? (= renaming-id (:id typography)) + + open* (mf/use-state editing?) + open? (deref open*) + + on-drop + (mf/use-fn + (mf/deps typography dragging* selected selected-full selected-paths move-typography) + (fn [event] + (cmm/on-drop-asset event typography dragging* selected selected-full + selected-paths move-typography))) + + on-drag-enter + (mf/use-fn + (mf/deps typography dragging* selected selected-paths) + (fn [event] + (cmm/on-drag-enter-asset event typography dragging* selected selected-paths))) + + on-drag-leave + (mf/use-fn + (mf/deps dragging*) + (fn [event] + (cmm/on-drag-leave-asset event dragging*))) + + on-typography-drag-start + (mf/use-fn + (mf/deps typography file-id selected item-ref read-only?) + (fn [event] + (if read-only? + (dom/prevent-default event) + (cmm/on-asset-drag-start event file-id typography selected item-ref :typographies identity)))) + + on-context-menu + (mf/use-fn + (mf/deps on-context-menu typography-id) + (partial on-context-menu typography-id)) + + handle-change + (mf/use-fn + (mf/deps typography) + (partial handle-change typography)) + + apply-typography + (mf/use-fn + (mf/deps typography) + (partial apply-typography typography)) + + on-asset-click + (mf/use-fn + (mf/deps typography apply-typography on-asset-click) + (partial on-asset-click typography-id apply-typography))] + + (if ^boolean new-css-system + [:div {:class (dom/classnames (css :typography-item) true) + :ref item-ref + :draggable (and (not read-only?) (not open?)) + :on-drag-start on-typography-drag-start + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + [:& typography-entry + {:typography typography + :local? local? + :on-context-menu on-context-menu + :on-change handle-change + :selected? (contains? selected typography-id) + :on-click on-asset-click + :editing? editing? + :renaming? renaming? + :focus-name? rename? + :external-open* open* + :file-id file-id}] + + (when ^boolean dragging? + [:div {:class (dom/classnames (css :dragging) true)}])] + + [:div.typography-container {:ref item-ref + :draggable (and (not read-only?) (not open?)) + :on-drag-start on-typography-drag-start + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + [:& typography-entry + {:typography typography + :local? local? + :on-context-menu on-context-menu + :on-change handle-change + :selected? (contains? selected typography-id) + :on-click on-asset-click + :editing? editing? + :focus-name? rename? + :external-open* open* + :file-id file-id}] + + (when ^boolean dragging? + [:div.dragging])]))) + +(mf/defc typographies-group + {::mf/wrap-props false} + [{:keys [file-id prefix groups open-groups file local? selected local-data + editing-id renaming-id on-asset-click handle-change apply-typography on-rename-group + on-ungroup on-context-menu selected-full]}] + (let [group-open? (get open-groups prefix true) + dragging* (mf/use-state false) + dragging? (deref dragging*) + new-css-system (mf/use-ctx ctx/new-css-system) + selected-paths (mf/with-memo [selected-full] + (into #{} + (comp (map :path) (d/nilv "")) + selected-full)) + move-typography + (mf/use-fn + (mf/deps file-id) + (partial dwl/rename-typography file-id)) + + on-drag-enter + (mf/use-fn + (mf/deps dragging* prefix selected-paths) + (fn [event] + (cmm/on-drag-enter-asset-group event dragging* prefix selected-paths))) + + on-drag-leave + (mf/use-fn + (mf/deps dragging*) + (fn [event] + (cmm/on-drag-leave-asset event dragging*))) + + on-drop + (mf/use-fn + (mf/deps dragging* prefix selected-paths selected-full move-typography) + (fn [event] + (cmm/on-drop-asset-group event dragging* prefix selected-paths selected-full move-typography)))] + + (if ^boolean new-css-system + [:div {:class (dom/classnames (css :typographies-group) true) + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + [:& grp/asset-group-title {:file-id file-id + :section :typographies + :path prefix + :group-open? group-open? + :on-rename on-rename-group + :on-ungroup on-ungroup}] + + (when group-open? + [:* + (let [typographies (get groups "" [])] + [:div {:class (dom/classnames (css :assets-list) true) + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + + (when ^boolean dragging? + [:div {:class (css :grid-placeholder)} "\u00A0"]) + + (when (and + (empty? typographies) + (some? groups)) + [:div {:class (css :drop-space)}]) + (for [{:keys [id] :as typography} typographies] + [:& typography-item {:typography typography + :key (dm/str "typography-" id) + :file-id file-id + :local? local? + :handle-change handle-change + :selected selected + :apply-typography apply-typography + :editing-id editing-id + :renaming-id renaming-id + :rename? (= (:rename-typography local-data) id) + :on-asset-click on-asset-click + :on-context-menu on-context-menu + :selected-full selected-full + :selected-paths selected-paths + :move-typography move-typography}])]) + + (for [[path-item content] groups] + (when-not (empty? path-item) + [:& typographies-group {:file-id file-id + :prefix (cph/merge-path-item prefix path-item) + :key (dm/str "group-" path-item) + :groups content + :open-groups open-groups + :file file + :local? local? + :selected selected + :editing-id editing-id + :renaming-id renaming-id + :local-data local-data + :on-asset-click on-asset-click + :handle-change handle-change + :apply-typography apply-typography + :on-rename-group on-rename-group + :on-ungroup on-ungroup + :on-context-menu on-context-menu + :selected-full selected-full}]))])] + [:div {:on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + [:& grp/asset-group-title {:file-id file-id + :section :typographies + :path prefix + :group-open? group-open? + :on-rename on-rename-group + :on-ungroup on-ungroup}] + (when group-open? + [:* + (let [typographies (get groups "" [])] + [:div.asset-list {:on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + + (when ^boolean dragging? + [:div.grid-placeholder "\u00A0"]) + + (when (and + (empty? typographies) + (some? groups)) + [:div.drop-space]) + (for [{:keys [id] :as typography} typographies] + [:& typography-item {:typography typography + :key (dm/str "typography-" id) + :file-id file-id + :local? local? + :handle-change handle-change + :selected selected + :apply-typography apply-typography + :editing-id editing-id + :rename? (= (:rename-typography local-data) id) + :on-asset-click on-asset-click + :on-context-menu on-context-menu + :selected-full selected-full + :selected-paths selected-paths + :move-typography move-typography}])]) + + (for [[path-item content] groups] + (when-not (empty? path-item) + [:& typographies-group {:file-id file-id + :prefix (cph/merge-path-item prefix path-item) + :key (dm/str "group-" path-item) + :groups content + :open-groups open-groups + :file file + :local? local? + :selected selected + :editing-id editing-id + :local-data local-data + :on-asset-click on-asset-click + :handle-change handle-change + :apply-typography apply-typography + :on-rename-group on-rename-group + :on-ungroup on-ungroup + :on-context-menu on-context-menu + :selected-full selected-full}]))])]))) + +(mf/defc typographies-section + {::mf/wrap-props false} + [{:keys [file file-id local? typographies open? open-status-ref selected reverse-sort? + on-asset-click on-assets-delete on-clear-selection]}] + (let [state (mf/use-state {:detail-open? false :id nil}) + local-data (mf/deref lens:typography-section-state) + + read-only? (mf/use-ctx ctx/workspace-read-only?) + new-css-system (mf/use-ctx ctx/new-css-system) + menu-state (mf/use-state cmm/initial-context-menu-state) + typographies (mf/with-memo [typographies] + (mapv dwl/extract-path-if-missing typographies)) + + groups (mf/with-memo [typographies reverse-sort?] + (grp/group-assets typographies reverse-sort?)) + + selected (:typographies selected) + selected-full (mf/with-memo [selected typographies] + (into #{} (filter #(contains? selected (:id %))) typographies)) + + multi-typographies? (> (count selected) 1) + multi-assets? (or (seq (:components selected)) + (seq (:graphics selected)) + (seq (:colors selected))) + + open-groups-ref (mf/with-memo [open-status-ref] + (-> (l/in [:groups :typographies]) + (l/derived open-status-ref))) + + open-groups (mf/deref open-groups-ref) + + add-typography + (mf/use-fn + (mf/deps file-id) + (fn [_] + (st/emit! (dwt/add-typography file-id)))) + + handle-change + (mf/use-fn + (mf/deps file-id) + (fn [typography changes] + (st/emit! (dwl/update-typography (merge typography changes) file-id)))) + + apply-typography + (mf/use-fn + (mf/deps file-id) + (fn [typography _event] + (st/emit! (dwt/apply-typography typography file-id)))) + + create-group + (mf/use-fn + (mf/deps typographies selected on-clear-selection file-id (:id @state)) + (fn [group-name] + (on-clear-selection) + (let [undo-id (js/Symbol)] + (st/emit! (dwu/start-undo-transaction undo-id)) + (run! st/emit! + (->> typographies + (filter #(if multi-typographies? + (contains? selected (:id %)) + (= (:id @state) (:id %)))) + (map #(dwl/update-typography + (assoc % :name + (cmm/add-group % group-name)) + file-id)))) + (st/emit! (dwu/commit-undo-transaction undo-id))))) + + rename-group + (mf/use-fn + (mf/deps typographies) + (fn [path last-path] + (on-clear-selection) + (let [undo-id (js/Symbol)] + (st/emit! (dwu/start-undo-transaction undo-id)) + (run! st/emit! + (->> typographies + (filter #(str/starts-with? (:path %) path)) + (map #(dwl/update-typography + (assoc % :name + (cmm/rename-group % path last-path)) + file-id)))) + (st/emit! (dwu/commit-undo-transaction undo-id))))) + + on-group + (mf/use-fn + (mf/deps typographies selected create-group) + (fn [event] + (dom/stop-propagation event) + (modal/show! :name-group-dialog {:accept create-group}))) + + on-rename-group + (mf/use-fn + (mf/deps typographies) + (fn [event path last-path] + (dom/stop-propagation event) + (modal/show! :name-group-dialog {:path path + :last-path last-path + :accept rename-group}))) + on-ungroup + (mf/use-fn + (mf/deps typographies) + (fn [path] + (on-clear-selection) + (let [undo-id (js/Symbol)] + (st/emit! (dwu/start-undo-transaction undo-id)) + (apply st/emit! + (->> typographies + (filter #(str/starts-with? (:path %) path)) + (map #(dwl/rename-typography + file-id + (:id %) + (cmm/ungroup % path))))) + (st/emit! (dwu/commit-undo-transaction undo-id))))) + + on-context-menu + (mf/use-fn + (mf/deps selected on-clear-selection read-only?) + (fn [id event] + (dom/prevent-default event) + (let [pos (dom/get-client-position event)] + (when (and local? (not read-only?)) + (when-not (contains? selected id) + (on-clear-selection)) + (swap! state assoc :id id) + (swap! menu-state cmm/open-context-menu pos))))) + + on-close-menu + (mf/use-fn + (fn [] + (swap! menu-state cmm/close-context-menu))) + + handle-rename-typography-clicked + (fn [] + (st/emit! #(assoc-in % [:workspace-global :rename-typography] (:id @state)))) + + handle-edit-typography-clicked + (fn [] + (st/emit! #(assoc-in % [:workspace-global :edit-typography] (:id @state)))) + + handle-delete-typography + (mf/use-fn + (mf/deps @state multi-typographies? multi-assets?) + (fn [] + (let [undo-id (js/Symbol)] + (if (or multi-typographies? multi-assets?) + (on-assets-delete) + (st/emit! (dwu/start-undo-transaction undo-id) + (dwl/delete-typography (:id @state)) + (dwl/sync-file file-id file-id :typographies (:id @state)) + (dwu/commit-undo-transaction undo-id)))))) + + editing-id (if new-css-system + (:edit-typography local-data) + (or (:rename-typography local-data) + (:edit-typography local-data))) + + renaming-id (:rename-typography local-data) + + on-asset-click + (mf/use-fn + (mf/deps groups on-asset-click) + (partial on-asset-click groups))] + + (mf/use-effect + (mf/deps local-data new-css-system) + (fn [] + (when (and (not new-css-system)(:rename-typography local-data)) + (st/emit! #(update % :workspace-global dissoc :rename-typography))) + (when (:edit-typography local-data) + (st/emit! #(update % :workspace-global dissoc :edit-typography))))) + + [:& cmm/asset-section {:file-id file-id + :title (tr "workspace.assets.typography") + :section :typographies + :assets-count (count typographies) + :open? open?} + (if ^boolean new-css-system + (when local? + [:& cmm/asset-section-block {:role :title-button} + (when-not read-only? + [:button {:class (dom/classnames (css :assets-btn) true) + :on-click add-typography} + i/add-refactor])]) + + (when local? + [:& cmm/asset-section-block {:role :title-button} + (when-not read-only? + [:div.assets-button {:on-click add-typography} + i/plus])])) + + [:& cmm/asset-section-block {:role :content} + [:& typographies-group {:file-id file-id + :prefix "" + :groups groups + :open-groups open-groups + :state state + :file file + :local? local? + :selected selected + :editing-id editing-id + :renaming-id renaming-id + :local-data local-data + :on-asset-click on-asset-click + :handle-change handle-change + :apply-typography apply-typography + :on-rename-group on-rename-group + :on-ungroup on-ungroup + :on-context-menu on-context-menu + :selected-full selected-full}] + + (when local? + [:& cmm/assets-context-menu + {:on-close on-close-menu + :state @menu-state + :options (if new-css-system + [(when-not (or multi-typographies? multi-assets?) + {:option-name (tr "workspace.assets.rename") + :id "assets-rename-typography" + :option-handler handle-rename-typography-clicked}) + + (when-not (or multi-typographies? multi-assets?) + {:option-name (tr "workspace.assets.edit") + :id "assets-edit-typography" + :option-handler handle-edit-typography-clicked}) + + {:option-name (tr "workspace.assets.delete") + :id "assets-delete-typography" + :option-handler handle-delete-typography} + + (when-not multi-assets? + {:option-name (tr "workspace.assets.group") + :id "assets-group-typography" + :option-handler on-group})] + + + [(when-not (or multi-typographies? multi-assets?) + [(tr "workspace.assets.rename") handle-rename-typography-clicked]) + (when-not (or multi-typographies? multi-assets?) + [(tr "workspace.assets.edit") handle-edit-typography-clicked]) + [(tr "workspace.assets.delete") handle-delete-typography] + (when-not multi-assets? + [(tr "workspace.assets.group") on-group])])}])]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.css.json b/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.css.json new file mode 100644 index 0000000000..569ecc8249 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.css.json @@ -0,0 +1 @@ +{"button-primary":"assets_typographies_button-primary_njVYq","button-secondary":"assets_typographies_button-secondary_Wzmmw","button-tertiary":"assets_typographies_button-tertiary_k3V5a","assets-btn":"assets_typographies_assets-btn_wCF-m","button-tag":"assets_typographies_button-tag_grTbB","button-icon":"assets_typographies_button-icon_7peoi","button-icon-small":"assets_typographies_button-icon-small_oHD9w","asset-element":"assets_typographies_asset-element_hvNzY","typographies-group":"assets_typographies_typographies-group_iCR4V","assets-list":"assets_typographies_assets-list_wS3At","drop-space":"assets_typographies_drop-space_kGrjB","grid-placeholder":"assets_typographies_grid-placeholder_FvcCI","typography-item":"assets_typographies_typography-item_qkADe","dragging":"assets_typographies_dragging_Ns4o7"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.scss b/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.scss new file mode 100644 index 0000000000..6feea36ab2 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.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"; + +.assets-btn { + @extend .button-tertiary; + height: $s-32; + width: calc($s-24 + $s-4); + padding: 0; + border-radius: $br-8; + svg { + @extend .button-icon; + } +} +.typographies-group { + .assets-list { + padding: 0 0 0 $s-12; + .drop-space { + height: $s-12; + border-radius: $br-8; + background-color: var(--assets-item-background-color); + } + .grid-placeholder { + height: $s-2; + width: 100%; + background-color: var(--color-accent-primary); + } + .typography-item { + position: relative; + display: flex; + align-items: center; + margin-bottom: $s-4; + border-radius: $br-8; + background-color: var(--assets-item-background-color); + } + .dragging { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + border: $s-2 solid var(--assets-item-border-color-drag); + border-radius: $s-8; + background-color: var(--assets-item-background-color-drag); + } + } +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/collapsable_button.cljs b/frontend/src/app/main/ui/workspace/sidebar/collapsable_button.cljs index 86ef4cf188..e266382237 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/collapsable_button.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/collapsable_button.cljs @@ -18,9 +18,9 @@ (mf/defc collapsed-button {::mf/wrap-props false} [] - (let [new-css? (mf/use-ctx ctx/new-css-system) + (let [new-css-system (mf/use-ctx ctx/new-css-system) on-click (mf/use-fn #(st/emit! (dw/toggle-layout-flag :collapse-left-sidebar)))] - (if ^boolean new-css? + (if ^boolean new-css-system [:div {:class (dom/classnames (css :collapsed-sidebar) true)} [:div {:class (dom/classnames (css :collapsed-title) true)} [:button {:class (dom/classnames (css :collapsed-button) true) diff --git a/frontend/src/app/main/ui/workspace/sidebar/collapsable_button.css.json b/frontend/src/app/main/ui/workspace/sidebar/collapsable_button.css.json index e4c9e7e0a4..a3399ec491 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/collapsable_button.css.json +++ b/frontend/src/app/main/ui/workspace/sidebar/collapsable_button.css.json @@ -1 +1 @@ -{"button-primary":"sidebar_collapsable_button_button-primary_qaRce","button-secondary":"sidebar_collapsable_button_button-secondary_OqDpe","button-icon":"sidebar_collapsable_button_button-icon_P4-xy","button-icon-small":"sidebar_collapsable_button_button-icon-small_lQUE3","collapsed-sidebar":"sidebar_collapsable_button_collapsed-sidebar_uQnGJ","collapsed-title":"sidebar_collapsable_button_collapsed-title_Jb62g","collapsed-button":"sidebar_collapsable_button_collapsed-button_LT5ME"} \ No newline at end of file +{"button-primary":"sidebar_collapsable_button_button-primary_qaRce","button-secondary":"sidebar_collapsable_button_button-secondary_OqDpe","button-tertiary":"sidebar_collapsable_button_button-tertiary_NuJrA","button-tag":"sidebar_collapsable_button_button-tag_unQKq","button-icon":"sidebar_collapsable_button_button-icon_P4-xy","button-icon-small":"sidebar_collapsable_button_button-icon-small_lQUE3","asset-element":"sidebar_collapsable_button_asset-element_BboJ7","collapsed-sidebar":"sidebar_collapsable_button_collapsed-sidebar_uQnGJ","collapsed-title":"sidebar_collapsable_button_collapsed-title_Jb62g","collapsed-button":"sidebar_collapsable_button_collapsed-button_LT5ME"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/layer_item.css.json b/frontend/src/app/main/ui/workspace/sidebar/layer_item.css.json index 96e75da537..3b0b58bf05 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layer_item.css.json +++ b/frontend/src/app/main/ui/workspace/sidebar/layer_item.css.json @@ -1 +1 @@ -{"button-primary":"sidebar_layer_item_button-primary_74ST4","button-secondary":"sidebar_layer_item_button-secondary_e4u9V","button-icon":"sidebar_layer_item_button-icon_-D7KH","button-icon-small":"sidebar_layer_item_button-icon-small_1RfDl","layer-row":"sidebar_layer_item_layer-row_KibLX","element-list-body":"sidebar_layer_item_element-list-body_832JO","element-actions":"sidebar_layer_item_element-actions_ACGJI","toggle-element":"sidebar_layer_item_toggle-element_4bhRW","block-element":"sidebar_layer_item_block-element_RhKz-","button-content":"sidebar_layer_item_button-content_bPwop","icon-shape":"sidebar_layer_item_icon-shape_g9Wxn","toggle-content":"sidebar_layer_item_toggle-content_MKhsv","filtered":"sidebar_layer_item_filtered_V5CHf","inverse":"sidebar_layer_item_inverse_zzZ54","absolute":"sidebar_layer_item_absolute_mYIKg","selected":"sidebar_layer_item_selected_O7P-j","element-children":"sidebar_layer_item_element-children_3iA4Q","parent-selected":"sidebar_layer_item_parent-selected_uIIyQ","hidden":"sidebar_layer_item_hidden_JRbJO","type-comp":"sidebar_layer_item_type-comp_FBSRt","tab-indentation":"sidebar_layer_item_tab-indentation_e-2dQ"} \ No newline at end of file +{"button-primary":"sidebar_layer_item_button-primary_74ST4","button-secondary":"sidebar_layer_item_button-secondary_e4u9V","button-tertiary":"sidebar_layer_item_button-tertiary_Mo--6","button-tag":"sidebar_layer_item_button-tag_lFKoD","button-icon":"sidebar_layer_item_button-icon_-D7KH","button-icon-small":"sidebar_layer_item_button-icon-small_1RfDl","layer-row":"sidebar_layer_item_layer-row_KibLX","element-list-body":"sidebar_layer_item_element-list-body_832JO","element-actions":"sidebar_layer_item_element-actions_ACGJI","toggle-element":"sidebar_layer_item_toggle-element_4bhRW","block-element":"sidebar_layer_item_block-element_RhKz-","button-content":"sidebar_layer_item_button-content_bPwop","icon-shape":"sidebar_layer_item_icon-shape_g9Wxn","toggle-content":"sidebar_layer_item_toggle-content_MKhsv","asset-element":"sidebar_layer_item_asset-element_AXTD0","filtered":"sidebar_layer_item_filtered_V5CHf","inverse":"sidebar_layer_item_inverse_zzZ54","absolute":"sidebar_layer_item_absolute_mYIKg","selected":"sidebar_layer_item_selected_O7P-j","element-children":"sidebar_layer_item_element-children_3iA4Q","parent-selected":"sidebar_layer_item_parent-selected_uIIyQ","hidden":"sidebar_layer_item_hidden_JRbJO","type-comp":"sidebar_layer_item_type-comp_FBSRt","tab-indentation":"sidebar_layer_item_tab-indentation_e-2dQ"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/layer_name.css.json b/frontend/src/app/main/ui/workspace/sidebar/layer_name.css.json index 40f1367aaf..4e8c50d740 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layer_name.css.json +++ b/frontend/src/app/main/ui/workspace/sidebar/layer_name.css.json @@ -1 +1 @@ -{"button-primary":"sidebar_layer_name_button-primary_V-6Cp","button-secondary":"sidebar_layer_name_button-secondary_Q14Qj","button-icon":"sidebar_layer_name_button-icon_UQXjw","button-icon-small":"sidebar_layer_name_button-icon-small_At5P8","element-name":"sidebar_layer_name_element-name_hZ-lA","selected":"sidebar_layer_name_selected_MKxdm","type-comp":"sidebar_layer_name_type-comp_TNGM-","hidden":"sidebar_layer_name_hidden_e-K3G","element-name-input":"sidebar_layer_name_element-name-input_Wpnkf"} \ No newline at end of file +{"button-primary":"sidebar_layer_name_button-primary_V-6Cp","button-secondary":"sidebar_layer_name_button-secondary_Q14Qj","button-tertiary":"sidebar_layer_name_button-tertiary_dA-v0","button-tag":"sidebar_layer_name_button-tag_fr2K0","button-icon":"sidebar_layer_name_button-icon_UQXjw","button-icon-small":"sidebar_layer_name_button-icon-small_At5P8","asset-element":"sidebar_layer_name_asset-element_WVekz","element-name":"sidebar_layer_name_element-name_hZ-lA","selected":"sidebar_layer_name_selected_MKxdm","type-comp":"sidebar_layer_name_type-comp_TNGM-","hidden":"sidebar_layer_name_hidden_e-K3G","element-name-input":"sidebar_layer_name_element-name-input_Wpnkf"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/layer_name.scss b/frontend/src/app/main/ui/workspace/sidebar/layer_name.scss index 8e6ede5708..c7f95086e7 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layer_name.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/layer_name.scss @@ -28,6 +28,7 @@ .element-name-input { @include textEllipsis; @include titleTipography; + @include removeInputStyle; flex-grow: 1; height: $s-28; max-width: calc(var(--parent-size) - (var(--depth) * var(--layer-indentation-size))); @@ -35,7 +36,5 @@ padding-left: $s-6; border-radius: $br-8; border: 1px solid var(--input-border-color-focus); - outline: none; - background-color: transparent; color: var(--layer-row-foreground-color); } diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index d5682b96da..99f24fe23b 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -13,7 +13,9 @@ [app.main.data.workspace :as dw] [app.main.refs :as refs] [app.main.store :as st] + [app.main.ui.components.search-bar :refer [search-bar]] [app.main.ui.components.shape-icon-refactor :as sic] + [app.main.ui.components.title-bar :refer [title-bar]] [app.main.ui.context :as ctx] [app.main.ui.hooks :as hooks] [app.main.ui.icons :as i] @@ -128,9 +130,14 @@ (swap! filter-state assoc :search-text "" :num-items 100))) update-search-text - (mf/use-callback + (mf/use-fn + (mf/deps new-css-system) (fn [event] - (let [value (-> event dom/get-target dom/get-value)] + ;; NOTE: When old-css-system is removed this function will recibe value and event + ;; Let won't be necessary any more + (let [value (if new-css-system + event + (dom/get-target-val event))] (swap! filter-state assoc :search-text value :num-items 100)))) toggle-search @@ -226,12 +233,9 @@ (fn [event] (let [enter? (kbd/enter? event) esc? (kbd/esc? event) - input-node (dom/event->target event)] - - (when enter? - (dom/blur! input-node)) - (when esc? - (dom/blur! input-node)))))] + node (dom/event->target event)] + (when ^boolean enter? (dom/blur! node)) + (when ^boolean esc? (dom/blur! node)))))] [filtered-objects handle-show-more @@ -243,33 +247,33 @@ (css :search) true) (dom/classnames :tool-window-bar true :search true))} - [:span {:class (if new-css-system - (dom/classnames (css :search-box) true) - (dom/classnames :search-box true))} - [:button - {:on-click toggle-filters - :class (if new-css-system - (dom/classnames :active active? - (css :filter-button) true) - (dom/classnames :active active? - :filter true))} - (if new-css-system - i/filter-refactor - i/icon-filter)] - [:div {:class (dom/classnames (css :search-input-wrapper) new-css-system)} - [:input {:on-change update-search-text - :value (:search-text @filter-state) - :auto-focus (:show-search-box @filter-state) - :placeholder (tr "workspace.sidebar.layers.search") - :on-key-down handle-key-down}] - (when (not (= "" (:search-text @filter-state))) - [:button {:class (if new-css-system - (dom/classnames (css :clear) true) - (dom/classnames :clear true)) - :on-click clear-search-text} - (if new-css-system - i/delete-text-refactor - i/exclude)])]] + (if new-css-system + [:& search-bar + {:on-change update-search-text + :value (:search-text @filter-state) + :on-clear clear-search-text + :placeholder (tr "workspace.sidebar.layers.search")} + [:button + {:on-click toggle-filters + :class (dom/classnames :active active? + (css :filter-button) true)} + i/filter-refactor]] + + [:span.search-box + [:button.filter + {:on-click toggle-filters + :class (dom/classnames :active active?)} + i/icon-filter] + [:div + [:input {:on-change update-search-text + :value (:search-text @filter-state) + :auto-focus (:show-search-box @filter-state) + :placeholder (tr "workspace.sidebar.layers.search") + :on-key-down handle-key-down}] + (when (not (= "" (:search-text @filter-state))) + [:button.clear {:on-click clear-search-text} + i/exclude])]]) + [:button {:class (dom/classnames (css :close-search) new-css-system) :on-click toggle-search} (if new-css-system @@ -397,20 +401,18 @@ [:span {:on-click (add-filter :text)} i/text (tr "workspace.sidebar.layers.texts")] [:span {:on-click (add-filter :image)} i/image (tr "workspace.sidebar.layers.images")] [:span {:on-click (add-filter :shape)} i/curve (tr "workspace.sidebar.layers.shapes")]]))] - [:div {:class (if new-css-system - (dom/classnames (css :tool-window-bar) true) - (dom/classnames :tool-window-bar true))} - [:span {:class (if new-css-system - (dom/classnames (css :page-name) true) - (dom/classnames :page-name true))} - (:name page)] - [:button {:class (if new-css-system - (dom/classnames (css :icon-search) true) - (dom/classnames :icon-search true)) - :on-click toggle-search} - (if new-css-system - i/search-refactor - i/search)]]))])) + + (if new-css-system + [:div {:class (dom/classnames (css :tool-window-bar) true)} + [:& title-bar {:collapsable? false + :title (:name page) + :on-btn-click toggle-search + :btn-children i/search-refactor}]] + [:div.tool-window-bar + [:span.page-name + (:name page)] + [:button.icon-search {:on-click toggle-search} + i/search]])))])) (mf/defc layers-toolbox {:wrap [mf/memo]} diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.css.json b/frontend/src/app/main/ui/workspace/sidebar/layers.css.json index f9c4de1291..ac87f734c4 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.css.json +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.css.json @@ -1 +1 @@ -{"button-primary":"sidebar_layers_button-primary_q9e2I","layers":"sidebar_layers_layers_87ZOo","tool-window-bar":"sidebar_layers_tool-window-bar_lg54C","search":"sidebar_layers_search_zjs2x","close-search":"sidebar_layers_close-search_baIhK","icon-search":"sidebar_layers_icon-search_6kWUn","button-secondary":"sidebar_layers_button-secondary_H4lpi","active-filters":"sidebar_layers_active-filters_-JMMP","layer-filter":"sidebar_layers_layer-filter_rHZTz","search-box":"sidebar_layers_search-box_JtqOV","search-input-wrapper":"sidebar_layers_search-input-wrapper_oJa-7","clear":"sidebar_layers_clear_sLcl1","button-icon":"sidebar_layers_button-icon_SD8PP","button-icon-small":"sidebar_layers_button-icon-small_v5L-u","filters-container":"sidebar_layers_filters-container_c1Ux9","filter-menu-item":"sidebar_layers_filter-menu-item_aZd0D","filter-menu-item-tick":"sidebar_layers_filter-menu-item-tick_JNdIK","filter-menu-item-name-wrapper":"sidebar_layers_filter-menu-item-name-wrapper_DtGkH","filter-menu-item-icon":"sidebar_layers_filter-menu-item-icon_Oi3Ix","layer-filter-icon":"sidebar_layers_layer-filter-icon_0yKrb","layer-filter-close":"sidebar_layers_layer-filter-close_3mV4i","focus-title":"sidebar_layers_focus-title_35PvQ","back-button-icon":"sidebar_layers_back-button-icon_mHv6g","page-name":"sidebar_layers_page-name_8ZDRR","filter-button":"sidebar_layers_filter-button_KXxHh","focus-name":"sidebar_layers_focus-name_Fderf","focus-mode-tag-wrapper":"sidebar_layers_focus-mode-tag-wrapper_OHXCG","focus-mode-tag":"sidebar_layers_focus-mode-tag_J5ItD","layer-filter-name":"sidebar_layers_layer-filter-name_Y4PuB","filter-menu-item-name":"sidebar_layers_filter-menu-item-name_rxeut","selected":"sidebar_layers_selected_V5Vv3","tool-window-content":"sidebar_layers_tool-window-content_YnpDB","element-list":"sidebar_layers_element-list_nAntB"} \ No newline at end of file +{"button-primary":"sidebar_layers_button-primary_q9e2I","button-secondary":"sidebar_layers_button-secondary_H4lpi","button-tertiary":"sidebar_layers_button-tertiary_5mq-9","layers":"sidebar_layers_layers_87ZOo","tool-window-bar":"sidebar_layers_tool-window-bar_lg54C","search":"sidebar_layers_search_zjs2x","close-search":"sidebar_layers_close-search_baIhK","icon-search":"sidebar_layers_icon-search_6kWUn","button-tag":"sidebar_layers_button-tag_FT7oa","active-filters":"sidebar_layers_active-filters_-JMMP","layer-filter":"sidebar_layers_layer-filter_rHZTz","button-icon":"sidebar_layers_button-icon_SD8PP","button-icon-small":"sidebar_layers_button-icon-small_v5L-u","filters-container":"sidebar_layers_filters-container_c1Ux9","filter-menu-item":"sidebar_layers_filter-menu-item_aZd0D","filter-menu-item-tick":"sidebar_layers_filter-menu-item-tick_JNdIK","filter-menu-item-name-wrapper":"sidebar_layers_filter-menu-item-name-wrapper_DtGkH","filter-menu-item-icon":"sidebar_layers_filter-menu-item-icon_Oi3Ix","layer-filter-icon":"sidebar_layers_layer-filter-icon_0yKrb","layer-filter-close":"sidebar_layers_layer-filter-close_3mV4i","focus-title":"sidebar_layers_focus-title_35PvQ","back-button-icon":"sidebar_layers_back-button-icon_mHv6g","asset-element":"sidebar_layers_asset-element_-dbA7","page-name":"sidebar_layers_page-name_8ZDRR","filter-button":"sidebar_layers_filter-button_KXxHh","focus-name":"sidebar_layers_focus-name_Fderf","focus-mode-tag-wrapper":"sidebar_layers_focus-mode-tag-wrapper_OHXCG","focus-mode-tag":"sidebar_layers_focus-mode-tag_J5ItD","layer-filter-name":"sidebar_layers_layer-filter-name_Y4PuB","filter-menu-item-name":"sidebar_layers_filter-menu-item-name_rxeut","selected":"sidebar_layers_selected_V5Vv3","tool-window-content":"sidebar_layers_tool-window-content_YnpDB","element-list":"sidebar_layers_element-list_nAntB"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.scss b/frontend/src/app/main/ui/workspace/sidebar/layers.scss index 34f76f5740..8d00a150b9 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.scss @@ -20,13 +20,14 @@ min-height: $s-32; margin-top: $s-2; margin-bottom: $s-4; + padding-right: $s-8; .page-name { @include tabTitleTipography; padding: 0 $s-12; color: var(--title-foreground-color); } .icon-search { - @extend .button-primary; + @extend .button-tertiary; height: $s-32; width: calc($s-24 + $s-4); border-radius: $br-8; @@ -38,94 +39,41 @@ } &.search { padding: 0 $s-8 0 $s-12; - .search-box { - display: grid; - grid-template-columns: auto 1fr; - gap: $s-2; + gap: $s-4; + .filter-button { + @include flexCenter; + @include buttonStyle; height: $s-32; - width: 100%; - margin-right: $s-4; - border-radius: $br-8; - background-color: var(--color-background-primary); - .filter-button { - @include flexCenter; - @include buttonStyle; - height: $s-32; - width: $s-32; - margin: 0; - border: 1px solid var(--color-background-tertiary); - border-radius: $br-8 $br-2 $br-2 $br-8; - background-color: var(--color-background-tertiary); + width: $s-32; + margin: 0; + border: 1px solid var(--color-background-tertiary); + border-radius: $br-8 $br-2 $br-2 $br-8; + background-color: var(--color-background-tertiary); + svg { + height: $s-16; + width: $s-16; + stroke: var(--icon-foreground); + } + &:focus { + border: 1px solid var(--input-border-color-focus); + outline: 0; + background-color: var(--input-background-color-active); + color: var(--input-foreground-color-active); svg { - height: $s-16; - width: $s-16; - stroke: var(--icon-foreground); - } - &:focus { - border: 1px solid var(--input-border-color-focus); - outline: 0; background-color: var(--input-background-color-active); - color: var(--input-foreground-color-active); - svg { - background-color: var(--input-background-color-active); - } - } - &:hover { - border: 1px solid var(--input-background-color-hover); - background-color: var(--input-background-color-hover); - svg { - background-color: var(--input-background-color-hover); - stroke: var(--button-foreground-hover); - } } } - .search-input-wrapper { - @include flexCenter; - height: $s-32; - width: 100%; - border: 1px solid var(--color-background-tertiary); - border-radius: $br-2 $br-8 $br-8 $br-2; - background-color: var(--color-background-tertiary); - input { - width: 100%; - margin: $s-8; - border: 0; - background-color: var(--input-background-color); - font-size: $fs-12; - color: var(--input-foreground-color); - &:focus { - outline: none; - } - } - &:hover { - border: 1px solid var(--input-background-color-hover); + &:hover { + border: 1px solid var(--input-background-color-hover); + background-color: var(--input-background-color-hover); + svg { background-color: var(--input-background-color-hover); - input { - background-color: var(--input-background-color-hover); - } - } - &:focus-within { - background-color: var(--input-background-color-active); - color: var(--input-foreground-color-active); - border: 1px solid var(--input-border-color-focus); - input { - background-color: var(--input-background-color-active); - } - } - - .clear { - @extend .button-secondary; - border-radius: $br-8; - height: 100%; - svg { - @extend .button-icon-small; - color: transparent; - } + stroke: var(--button-foreground-hover); } } } .close-search { - @extend .button-primary; + @extend .button-tertiary; height: $s-32; width: $s-28; min-width: $s-28; @@ -184,7 +132,7 @@ gap: $s-4; margin: 0 $s-12; .layer-filter { - @extend .button-secondary; + @extend .button-tag; @include buttonStyle; gap: $s-6; height: $s-24; @@ -296,7 +244,6 @@ flex-direction: column; height: 100%; width: 100%; - border-radius: $br-8; overflow-y: auto; overflow-x: hidden; scrollbar-gutter: stable; diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs index 7b88a06d08..728652f818 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs @@ -5,6 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.workspace.sidebar.options.menus.typography + (:require-macros [app.main.style :refer [css]]) (:require ["react-virtualized" :as rvt] [app.common.data :as d] @@ -19,6 +20,9 @@ [app.main.store :as st] [app.main.ui.components.editable-select :refer [editable-select]] [app.main.ui.components.numeric-input :refer [numeric-input]] + [app.main.ui.components.radio-buttons :refer [nilable-option radio-buttons]] + [app.main.ui.components.search-bar :refer [search-bar]] + [app.main.ui.components.select :refer [select]] [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.ui.workspace.sidebar.options.common :refer [advanced-options]] @@ -57,7 +61,8 @@ {::mf/wrap [mf/memo]} [{:keys [font current? on-click style]}] (let [item-ref (mf/use-ref) - on-click (mf/use-callback (mf/deps font) #(on-click font))] + on-click (mf/use-callback (mf/deps font) #(on-click font)) + new-css-system (mf/use-ctx ctx/new-css-system)] (mf/use-effect (mf/deps current?) @@ -67,12 +72,23 @@ (when-not (dom/is-in-viewport? element) (dom/scroll-into-view! element)))))) - [:div.font-item {:ref item-ref - :style style - :class (when current? "selected") - :on-click on-click} - [:span.icon (when current? i/tick)] - [:span.label (:name font)]])) + (if new-css-system + [:div {:class (css :font-wrapper) + :style style + :ref item-ref + :on-click on-click} + [:div {:class (dom/classnames + (css :font-item) true + (css :selected) current?)} + [:span {:class (css :label)} (:name font)] + [:span {:class (css :icon)} (when current? i/tick-refactor)]]] + + [:div.font-item {:ref item-ref + :style style + :class (when current? "selected") + :on-click on-click} + [:span.icon (when current? i/tick)] + [:span.label (:name font)]]))) (declare row-renderer) @@ -87,25 +103,17 @@ (comp (filter #(contains? backends (:backend %)))))] (into [] xform fonts))) -;; (defn- toggle-backend -;; [backends id] -;; (if (contains? backends id) -;; (disj backends id) -;; (conj backends id))) - (mf/defc font-selector [{:keys [on-select on-close current-font show-recent] :as props}] - (let [selected (mf/use-state current-font) - state (mf/use-state {:term "" :backends #{}}) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + selected (mf/use-state current-font) + state (mf/use-state {:term "" :backends #{}}) - flist (mf/use-ref) - input (mf/use-ref) + flist (mf/use-ref) + input (mf/use-ref) - fonts (mf/use-memo (mf/deps @state) #(filter-fonts @state @fonts/fonts)) - fontsdb (mf/deref fonts/fontsdb) - ;; Filtering deleted fonts - recent-fonts (->> (mf/deref refs/workspace-recent-fonts) - (into [] (filter #(some? (get fontsdb (:id %)))))) + fonts (mf/use-memo (mf/deps @state) #(filter-fonts @state @fonts/fonts)) + recent-fonts (mf/deref refs/workspace-recent-fonts) select-next (mf/use-callback @@ -136,9 +144,11 @@ on-filter-change (mf/use-callback - (mf/deps) + (mf/deps new-css-system) (fn [event] - (let [value (dom/get-target-val event)] + (let [value (if new-css-system + event + (dom/get-target-val event))] (swap! state assoc :term value)))) on-select-and-close @@ -184,60 +194,71 @@ #(let [offset (.getOffsetForRow ^js inst #js {:alignment "center" :index index})] (.scrollToPosition ^js inst offset)))))) - [:div.font-selector - [:div.font-selector-dropdown - [:header - [:input {:placeholder (tr "workspace.options.search-font") - :value (:term @state) - :ref input - :spell-check false - :on-change on-filter-change}] - (when (and recent-fonts show-recent) - [:hr] - [* - [:p.title (tr "workspace.options.recent-fonts")] - (for [[idx font] (d/enumerate recent-fonts)] - [:& font-item {:key (dm/str "font-" idx) - :font font - :style {} - :on-click on-select-and-close - :current? (= (:id font) (:id @selected))}])]) + (if new-css-system + [:div {:class (css :font-selector)} + [:div {:class (css :font-selector-dropdown)} + [:div {:class (css :header)} + [:& search-bar {:on-change on-filter-change + :value (:term @state) + :placeholder (tr "workspace.options.search-font")}] + (when (and recent-fonts show-recent) + [* + [:p {:class :title} (tr "workspace.options.recent-fonts")] + (for [[idx font] (d/enumerate recent-fonts)] + [:& font-item {:key (dm/str "font-" idx) + :font font + :style {} + :on-click on-select-and-close + :current? (= (:id font) (:id @selected))}])])] - #_[:div.options - {:on-click #(swap! state assoc :show-options true) - :class (when (seq (:backends @state)) "active")} - i/picker-hsv] + [:div {:class (css :fonts-list)} + [:> rvt/AutoSizer {} + (fn [props] + (let [width (unchecked-get props "width") + height (unchecked-get props "height") + render #(row-renderer fonts @selected on-select-and-close %)] + (mf/html + [:> rvt/List #js {:height height + :ref flist + :width width + :rowCount (count fonts) + :rowHeight 36 + :rowRenderer render}])))]]]] - #_[:& dropdown {:show (:show-options @state false) - :on-close #(swap! state dissoc :show-options)} - (let [backends (:backends @state)] - [:div.backend-filters.dropdown {:ref ddown} - [:div.backend-filter - {:class (when (backends :custom) "selected") - :on-click #(swap! state update :backends toggle-backend :custom)} - [:div.checkbox-icon i/tick] - [:div.backend-name (tr "labels.custom-fonts")]] - [:div.backend-filter - {:class (when (backends :google) "selected") - :on-click #(swap! state update :backends toggle-backend :google)} - [:div.checkbox-icon i/tick] - [:div.backend-name "Google Fonts"]]])]] + [:div.font-selector + [:div.font-selector-dropdown + [:header + [:input {:placeholder (tr "workspace.options.search-font") + :value (:term @state) + :ref input + :spell-check false + :on-change on-filter-change}] + (when (and recent-fonts show-recent) + [:hr] + [* + [:p.title (tr "workspace.options.recent-fonts")] + (for [[idx font] (d/enumerate recent-fonts)] + [:& font-item {:key (dm/str "font-" idx) + :font font + :style {} + :on-click on-select-and-close + :current? (= (:id font) (:id @selected))}])])] - [:hr] + [:hr] - [:div.fonts-list - [:> rvt/AutoSizer {} - (fn [props] - (let [width (unchecked-get props "width") - height (unchecked-get props "height") - render #(row-renderer fonts @selected on-select-and-close %)] - (mf/html - [:> rvt/List #js {:height height - :ref flist - :width width - :rowCount (count fonts) - :rowHeight 32 - :rowRenderer render}])))]]]])) + [:div.fonts-list + [:> rvt/AutoSizer {} + (fn [props] + (let [width (unchecked-get props "width") + height (unchecked-get props "height") + render #(row-renderer fonts @selected on-select-and-close %)] + (mf/html + [:> rvt/List #js {:height height + :ref flist + :width width + :rowCount (count fonts) + :rowHeight 32 + :rowRenderer render}])))]]]]))) (defn row-renderer [fonts selected on-select props] (let [index (unchecked-get props "index") @@ -259,12 +280,11 @@ font-id (or font-id (:font-id txt/default-text-attrs)) font-size (or font-size (:font-size txt/default-text-attrs)) font-variant-id (or font-variant-id (:font-variant-id txt/default-text-attrs)) + new-css-system (mf/use-ctx ctx/new-css-system) fonts (mf/deref fonts/fontsdb) font (get fonts font-id) - ;; Filtering deleted fonts - recent-fonts (->> (mf/deref refs/workspace-recent-fonts) - (into [] (filter #(some? (get fonts (:id %)))))) + recent-fonts (mf/deref refs/workspace-recent-fonts) last-font (mf/use-ref nil) open-selector? (mf/use-state false) @@ -319,57 +339,110 @@ (when (some? on-blur) (on-blur)) (when (mf/ref-val last-font) - (st/emit! (fts/add-recent-font (mf/ref-val last-font)))) - ))] + (st/emit! (fts/add-recent-font (mf/ref-val last-font))))))] + (if new-css-system + [:* + (when @open-selector? + [:& font-selector + {:current-font font + :on-close on-font-selector-close + :on-select on-font-select + :show-recent show-recent}]) - [:* - (when @open-selector? - [:& font-selector - {:current-font font - :on-close on-font-selector-close - :on-select on-font-select - :show-recent show-recent}]) + [:div + {:class (css :font-option) + :on-click #(reset! open-selector? true)} + (cond + (= :multiple font-id) + "--" - [:div.row-flex - [:div.input-select.font-option - {:on-click #(reset! open-selector? true)} - (cond - (= :multiple font-id) - "--" + (some? font) + [:* + [:span {:class (css :name)} + (:name font)] + [:span {:class (css :icon)} + i/arrow-refactor]] - (some? font) - (:name font) + :else + (tr "dashboard.fonts.deleted-placeholder"))] - :else - (tr "dashboard.fonts.deleted-placeholder"))]] + [:div {:class (css :font-modifiers)} + [:div {:class (css :font-size-options)} + (let [sizes [8 9 10 11 12 14 16 18 24 36 48 72] + basic-size-options (map (fn [number] + {:value (dm/str number) :key (dm/str "size-" number) :label (dm/str number)}) sizes) + size-options (if (= font-size :multiple) + (conj {:value :key :mulitple-sizes :multiple :label "--"} basic-size-options) + basic-size-options)] + + [:& select + {:class (css :font-size-select) + :default-value (attr->string font-size) + :options size-options + :on-change on-font-size-change + :on-blur on-blur}])] + + [:div {:class (css :font-variant-options)} + (let [basic-variant-options (map (fn [variant] + {:value (:id variant) :key (pr-str variant) :label (:name variant)}) (:variants font)) + variant-options (if (= font-size :multiple) + (conj {:value :multiple :key :multiple-variants :label "--"} basic-variant-options) + basic-variant-options)] + ;; TODO Add disabled mode + [:& select + {:class (css :font-variant-select) + :default-value (attr->string font-variant-id) + :options variant-options + :on-change on-font-variant-change + :on-blur on-blur}])]]] + + [:* + (when @open-selector? + [:& font-selector + {:current-font font + :on-close on-font-selector-close + :on-select on-font-select + :show-recent show-recent}]) + + [:div.row-flex + [:div.input-select.font-option + {:on-click #(reset! open-selector? true)} + (cond + (= :multiple font-id) + "--" + + (some? font) + (:name font) + + :else + (tr "dashboard.fonts.deleted-placeholder"))]] - [:div.row-flex - (let [size-options [8 9 10 11 12 14 16 18 24 36 48 72] - size-options (if (= font-size :multiple) (into [""] size-options) size-options)] - [:& editable-select - {:value (attr->string font-size) - :class "input-option size-option" - :options size-options - :type "number" - :placeholder "--" - :min 3 - :max 1000 - :on-change on-font-size-change - :on-blur on-blur}]) - - [:select.input-select.variant-option - {:disabled (= font-id :multiple) - :value (attr->string font-variant-id) - :on-change on-font-variant-change - :on-blur on-blur} - (when (or (= font-id :multiple) (= font-variant-id :multiple)) - [:option {:value ""} "--"]) - (for [variant (:variants font)] - [:option {:value (:id variant) - :key (pr-str variant)} - (:name variant)])]]])) + [:div.row-flex + (let [size-options [8 9 10 11 12 14 16 18 24 36 48 72] + size-options (if (= font-size :multiple) (into [""] size-options) size-options)] + [:& editable-select + {:value (attr->string font-size) + :class "input-option size-option" + :options size-options + :type "number" + :placeholder "--" + :min 3 + :max 1000 + :on-change on-font-size-change + :on-blur on-blur}]) + [:select.input-select.variant-option + {:disabled (= font-id :multiple) + :value (attr->string font-variant-id) + :on-change on-font-variant-change + :on-blur on-blur} + (when (or (= font-id :multiple) (= font-variant-id :multiple)) + [:option {:value ""} "--"]) + (for [variant (:variants font)] + [:option {:value (:id variant) + :key (pr-str variant)} + (:name variant)])]]]))) (mf/defc spacing-options {::mf/wrap-props false} @@ -379,101 +452,243 @@ line-height (or line-height "1.2") letter-spacing (or letter-spacing "0") - + new-css-system (mf/use-ctx ctx/new-css-system) line-height-nillable (if (= (str line-height) "1.2") false true) handle-change (fn [value attr] (on-change {attr (str value)}))] + (if new-css-system + [:div {:class (css :spacing-options)} + [:div {:class (css :line-height)} + [:span {:class (css :icon) + :alt (tr "workspace.options.text-options.line-height")} + i/text-lineheight-refactor] - [:div.spacing-options - [:div.input-icon - [:span.icon-before.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.line-height")} - i/line-height] - [:> numeric-input - {:min -200 - :max 200 - :step 0.1 - :default "1.2" - :value (attr->string line-height) - :placeholder (tr "settings.multiple") - :nillable line-height-nillable - :on-change #(handle-change % :line-height) - :on-blur on-blur}]] + [:> numeric-input + {:min -200 + :max 200 + :step 0.1 + :default "1.2" + :klass (css :line-height-input) + :value (attr->string line-height) + :placeholder (tr "settings.multiple") + :nillable line-height-nillable + :on-change #(handle-change % :line-height) + :on-blur on-blur}]] - [:div.input-icon - [:span.icon-before.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.letter-spacing")} - i/letter-spacing] - [:> numeric-input - {:min -200 - :max 200 - :step 0.1 - :value (attr->string letter-spacing) - :placeholder (tr "settings.multiple") - :on-change #(handle-change % :letter-spacing) - :on-blur on-blur}]]])) + [:div {:class (css :letter-spacing)} + [:span + {:class (css :icon) + :alt (tr "workspace.options.text-options.letter-spacing")} + i/text-letterspacing-refactor] + [:> numeric-input + {:min -200 + :max 200 + :step 0.1 + :klass (css :letter-spacing-input) + :value (attr->string letter-spacing) + :placeholder (tr "settings.multiple") + :on-change #(handle-change % :letter-spacing) + :on-blur on-blur}]]] + + [:div.spacing-options + [:div.input-icon + [:span.icon-before.tooltip.tooltip-bottom + {:alt (tr "workspace.options.text-options.line-height")} + i/line-height] + [:> numeric-input + {:min -200 + :max 200 + :step 0.1 + :default "1.2" + :value (attr->string line-height) + :placeholder (tr "settings.multiple") + :nillable line-height-nillable + :on-change #(handle-change % :line-height) + :on-blur on-blur}]] + + [:div.input-icon + [:span.icon-before.tooltip.tooltip-bottom + {:alt (tr "workspace.options.text-options.letter-spacing")} + i/letter-spacing] + [:> numeric-input + {:min -200 + :max 200 + :step 0.1 + :value (attr->string letter-spacing) + :placeholder (tr "settings.multiple") + :on-change #(handle-change % :letter-spacing) + :on-blur on-blur}]]]))) (mf/defc text-transform-options {::mf/wrap-props false} [{:keys [values on-change on-blur]}] (let [text-transform (or (:text-transform values) "none") + new-css-system (mf/use-ctx ctx/new-css-system) handle-change - (fn [_ type] - (on-change {:text-transform type}) + (fn [type] + (if (= text-transform type) + (on-change {:text-transform "unset"}) + (on-change {:text-transform type})) (when (some? on-blur) (on-blur)))] - [:div.align-icons - [:span.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.none") - :class (dom/classnames :current (= "none" text-transform)) - :on-focus #(dom/prevent-default %) - :on-click #(handle-change % "none")} - i/minus] - [:span.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.uppercase") - :class (dom/classnames :current (= "uppercase" text-transform)) - :on-click #(handle-change % "uppercase")} - i/uppercase] - [:span.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.lowercase") - :class (dom/classnames :current (= "lowercase" text-transform)) - :on-click #(handle-change % "lowercase")} - i/lowercase] - [:span.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.titlecase") - :class (dom/classnames :current (= "capitalize" text-transform)) - :on-click #(handle-change % "capitalize")} - i/titlecase]])) + (if new-css-system + [:div {:class (css :text-transform)} + [:& radio-buttons {:selected text-transform + :on-change handle-change + :name "text-transform"} + [:& nilable-option {:icon (mf/html i/text-uppercase-refactor) + :value "uppercase" + :id :uppercase}] + [:& nilable-option {:icon (mf/html i/text-lowercase-refactor) + :value "lowercase" + :id :lowercase}] + [:& nilable-option {:icon "Aa" + :value "capitalize" + :id :capitalize}]]] + + [:div.align-icons + [:span.tooltip.tooltip-bottom + {:alt (tr "workspace.options.text-options.none") + :class (dom/classnames :current (= "none" text-transform)) + :on-focus #(dom/prevent-default %) + :on-click #(handle-change "none")} + i/minus] + [:span.tooltip.tooltip-bottom + {:alt (tr "workspace.options.text-options.uppercase") + :class (dom/classnames :current (= "uppercase" text-transform)) + :on-click #(handle-change "uppercase")} + i/uppercase] + [:span.tooltip.tooltip-bottom + {:alt (tr "workspace.options.text-options.lowercase") + :class (dom/classnames :current (= "lowercase" text-transform)) + :on-click #(handle-change "lowercase")} + i/lowercase] + [:span.tooltip.tooltip-bottom + {:alt (tr "workspace.options.text-options.titlecase") + :class (dom/classnames :current (= "capitalize" text-transform)) + :on-click #(handle-change "capitalize")} + i/titlecase]]))) (mf/defc typography-options {::mf/wrap-props false} [{:keys [ids editor values on-change on-blur show-recent]}] - (let [opts #js {:editor editor + (let [new-css-system (mf/use-ctx ctx/new-css-system) + opts #js {:editor editor :ids ids :values values :on-change on-change :on-blur on-blur :show-recent show-recent}] - [:div.element-set-content - [:> font-options opts] - [:div.row-flex - [:> spacing-options opts]] - [:div.row-flex - [:> text-transform-options opts]]])) + + (if new-css-system + [:div {:class (css :typography-options)} + [:> font-options opts] + [:div {:class (css :typography-variations)} + [:> spacing-options opts] + [:> text-transform-options opts]]] + + [:div.element-set-content + [:> font-options opts] + [:div.row-flex + [:> spacing-options opts]] + [:div.row-flex + [:> text-transform-options opts]]]))) + + +(mf/defc typography-advanced-options + {::mf/wrap [mf/memo]} + [{:keys [visible? typography editable? name-input-ref on-close on-change on-name-blur local? navigate-to-library]}] + (let [ref (mf/use-ref nil)] + (mf/use-effect + (mf/deps visible?) + (fn [] + (when-let [node (mf/ref-val ref)] + (when visible? + (dom/scroll-into-view-if-needed! node))))) + + (when visible? + [:div {:ref ref + :class (css :advanced-options-wrapper)} + (if ^boolean editable? + [:* + [:div {:class (css :font-name-wrapper)} + [:div + {:class (dom/classnames (css :typography-sample-input) true) + :style {:font-family (:font-family typography) + :font-weight (:font-weight typography) + :font-style (:font-style typography)}} + (tr "workspace.assets.typography.sample")] + + [:input + {:class (css :adv-typography-name) + :type "text" + :ref name-input-ref + :default-value (:name typography) + :on-blur on-name-blur}] + + [:div {:class (css :action-btn) + :on-click on-close} + i/tick-refactor]] + + [:& typography-options {:values typography + :on-change on-change + :show-recent false}]] + + [:div.element-set-content.typography-read-only-data + [:div.row-flex.typography-name + [:span {:title (:name typography)} (:name typography)]] + + [:div.row-flex + [:span.label (tr "workspace.assets.typography.font-id")] + [:span (:font-id typography)]] + + [:div.element-set-actions-button.actions-inside + {:on-click on-close} + i/actions] + + [:div.row-flex + [:span.label (tr "workspace.assets.typography.font-variant-id")] + [:span (:font-variant-id typography)]] + + [:div.row-flex + [:span.label (tr "workspace.assets.typography.font-size")] + [:span (:font-size typography)]] + + [:div.row-flex + [:span.label (tr "workspace.assets.typography.line-height")] + [:span (:line-height typography)]] + + [:div.row-flex + [:span.label (tr "workspace.assets.typography.letter-spacing")] + [:span (:letter-spacing typography)]] + + [:div.row-flex + [:span.label (tr "workspace.assets.typography.text-transform")] + [:span (:text-transform typography)]] + + (when-not local? + [:div.row-flex + [:a.go-to-lib-button + {:on-click navigate-to-library} + (tr "workspace.assets.typography.go-to-edit")]])])]))) + (mf/defc typography-entry {::mf/wrap-props false} - [{:keys [file-id typography local? selected? on-click on-change on-detach on-context-menu editing? focus-name? external-open*]}] + [{:keys [file-id typography local? selected? on-click on-change on-detach on-context-menu editing? renaming? focus-name? external-open*]}] (let [hover-detach* (mf/use-state false) hover-detach? (deref hover-detach*) name-input-ref (mf/use-ref) read-only? (mf/use-ctx ctx/workspace-read-only?) + new-css-system (mf/use-ctx ctx/new-css-system) editable? (and local? (not read-only?)) open* (mf/use-state editing?) open? (deref open*) + font-data (fonts/get-font-data (:font-id typography)) + name-only? (= (:name typography) (:name font-data)) on-name-blur (mf/use-callback @@ -481,7 +696,8 @@ (fn [event] (let [name (dom/get-target-val event)] (when-not (str/blank? name) - (on-change {:name name}))))) + (on-change {:name name}) + (st/emit! #(update % :workspace-global dissoc :rename-typography)))))) on-pointer-enter (mf/use-fn #(reset! hover-detach* true)) @@ -500,9 +716,7 @@ (mf/deps file-id) (fn [] (when file-id - (st/emit! (dw/navigate-to-library file-id))))) - - ] + (st/emit! (dw/navigate-to-library file-id)))))] (mf/with-effect [editing?] (when editing? @@ -518,88 +732,145 @@ #(when-let [node (mf/ref-val name-input-ref)] (dom/focus! node) (dom/select-text! node))))) + (if new-css-system + [:* + [:div {:class (dom/classnames (css :typography-entry) true + (css :selected) ^boolean selected?) + :style {:display (when ^boolean open? "none")}} + (if renaming? + [:div {:class (css :font-name-wrapper)} + [:div + {:class (dom/classnames (css :typography-sample-input) true) + :style {:font-family (:font-family typography) + :font-weight (:font-weight typography) + :font-style (:font-style typography)}} + (tr "workspace.assets.typography.sample")] - [:* - [:div.element-set-options-group.typography-entry - {:class (when ^boolean selected? "selected") - :style {:display (when ^boolean open? "none")}} - [:div.typography-selection-wrapper - {:class (when ^boolean on-click "is-selectable") - :on-click on-click - :on-context-menu on-context-menu} - [:div.typography-sample - {:style {:font-family (:font-family typography) - :font-weight (:font-weight typography) - :font-style (:font-style typography)}} - (tr "workspace.assets.typography.sample")] - [:div.typography-name {:title (:name typography)}(:name typography)]] - [:div.element-set-actions - (when ^boolean on-detach - [:div.element-set-actions-button - {:on-pointer-enter on-pointer-enter - :on-pointer-leave on-pointer-leave - :on-click on-detach} - (if ^boolean hover-detach? i/unchain i/chain)]) - - [:div.element-set-actions-button - {:on-click on-open} - i/actions]]] - - [:& advanced-options {:visible? open? :on-close on-close} - (if ^boolean editable? - [:* - [:div.element-set-content - [:div.row-flex - [:input.element-name.adv-typography-name - {:type "text" + [:input + {:class (css :adv-typography-name) + :type "text" :ref name-input-ref :default-value (:name typography) - :on-blur on-name-blur}] + :on-blur on-name-blur}]] + [:div + {:class (dom/classnames (css :typography-selection-wrapper) true + (css :is-selectable) ^boolean on-click) + :on-click on-click + :on-context-menu on-context-menu} + [:div + {:class (dom/classnames (css :typography-sample) true) + :style {:font-family (:font-family typography) + :font-weight (:font-weight typography) + :font-style (:font-style typography)}} + (tr "workspace.assets.typography.sample")] + [:div {:class (dom/classnames (css :typography-name) true) + :title (:name typography)} (:name typography)] + + (when-not name-only? + [:div {:class (dom/classnames (css :typography-font) true) + :title (:name font-data)} + (:name font-data)])]) + + (when ^boolean on-detach + [:div {:class (dom/classnames (css :element-set-actions) true)} + [:div + {:class (dom/classnames (css :element-set-actions-button) true) + :on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave + :on-click on-detach} + (if ^boolean hover-detach? i/detach-refactor i/chain)]])] + + [:& typography-advanced-options + {:visible? open? :on-close on-close + :typography typography + :editable? editable? + :name-input-ref name-input-ref + :on-change on-change + :on-name-blur on-name-blur + :local? local? + :navigate-to-library navigate-to-library}]] + + + [:* + [:div.element-set-options-group.typography-entry + {:class (when ^boolean selected? "selected") + :style {:display (when ^boolean open? "none")}} + [:div.typography-selection-wrapper + {:class (when ^boolean on-click "is-selectable") + :on-click on-click + :on-context-menu on-context-menu} + [:div.typography-sample + {:style {:font-family (:font-family typography) + :font-weight (:font-weight typography) + :font-style (:font-style typography)}} + (tr "workspace.assets.typography.sample")] + [:div.typography-name {:title (:name typography)} (:name typography)]] + [:div.element-set-actions + (when ^boolean on-detach [:div.element-set-actions-button - {:on-click on-close} - i/actions]]] + {:on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave + :on-click on-detach} + (if ^boolean hover-detach? i/unchain i/chain)]) - [:& typography-options {:values typography - :on-change on-change - :show-recent false}]] + [:div.element-set-actions-button + {:on-click on-open} + i/actions]]] - [:div.element-set-content.typography-read-only-data - [:div.row-flex.typography-name - [:span {:title (:name typography)} (:name typography)]] + [:& advanced-options {:visible? open? :on-close on-close} + (if ^boolean editable? + [:* + [:div.element-set-content + [:div.row-flex + [:input.element-name.adv-typography-name + {:type "text" + :ref name-input-ref + :default-value (:name typography) + :on-blur on-name-blur}] - [:div.row-flex - [:span.label (tr "workspace.assets.typography.font-id")] - [:span (:font-id typography)]] + [:div.element-set-actions-button + {:on-click on-close} + i/actions]]] - [:div.element-set-actions-button.actions-inside - {:on-click on-close} - i/actions] + [:& typography-options {:values typography + :on-change on-change + :show-recent false}]] - [:div.row-flex - [:span.label (tr "workspace.assets.typography.font-variant-id")] - [:span (:font-variant-id typography)]] + [:div.element-set-content.typography-read-only-data + [:div.row-flex.typography-name + [:span {:title (:name typography)} (:name typography)]] - [:div.row-flex - [:span.label (tr "workspace.assets.typography.font-size")] - [:span (:font-size typography)]] - - [:div.row-flex - [:span.label (tr "workspace.assets.typography.line-height")] - [:span (:line-height typography)]] - - [:div.row-flex - [:span.label (tr "workspace.assets.typography.letter-spacing")] - [:span (:letter-spacing typography)]] - - [:div.row-flex - [:span.label (tr "workspace.assets.typography.text-transform")] - [:span (:text-transform typography)]] - - (when-not local? [:div.row-flex - [:a.go-to-lib-button - {:on-click navigate-to-library} - (tr "workspace.assets.typography.go-to-edit")]])] + [:span.label (tr "workspace.assets.typography.font-id")] + [:span (:font-id typography)]] - )]])) + [:div.element-set-actions-button.actions-inside + {:on-click on-close} + i/actions] + + [:div.row-flex + [:span.label (tr "workspace.assets.typography.font-variant-id")] + [:span (:font-variant-id typography)]] + + [:div.row-flex + [:span.label (tr "workspace.assets.typography.font-size")] + [:span (:font-size typography)]] + + [:div.row-flex + [:span.label (tr "workspace.assets.typography.line-height")] + [:span (:line-height typography)]] + + [:div.row-flex + [:span.label (tr "workspace.assets.typography.letter-spacing")] + [:span (:letter-spacing typography)]] + + [:div.row-flex + [:span.label (tr "workspace.assets.typography.text-transform")] + [:span (:text-transform typography)]] + + (when-not local? + [:div.row-flex + [:a.go-to-lib-button + {:on-click navigate-to-library} + (tr "workspace.assets.typography.go-to-edit")]])])]]))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.css.json b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.css.json new file mode 100644 index 0000000000..007aeef5a8 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.css.json @@ -0,0 +1 @@ +{"button-primary":"menus_typography_button-primary_s1c9M","button-secondary":"menus_typography_button-secondary_RjAsk","button-tertiary":"menus_typography_button-tertiary_Qt18f","font-name-wrapper":"menus_typography_font-name-wrapper_Njxb6","action-btn":"menus_typography_action-btn_hCakz","button-tag":"menus_typography_button-tag_32-df","button-icon":"menus_typography_button-icon_jucwh","advanced-options-wrapper":"menus_typography_advanced-options-wrapper_k3FD6","typography-options":"menus_typography_typography-options_j1u8l","font-modifiers":"menus_typography_font-modifiers_P8cSa","font-variant-options":"menus_typography_font-variant-options_qlgLr","icon":"menus_typography_icon_eDU2Z","font-size-options":"menus_typography_font-size-options_sKQdL","font-option":"menus_typography_font-option_7mgxF","button-icon-small":"menus_typography_button-icon-small_g3fsU","font-selector":"menus_typography_font-selector_TzuGa","font-selector-dropdown":"menus_typography_font-selector-dropdown_4s6s8","font-wrapper":"menus_typography_font-wrapper_GPkHS","font-item":"menus_typography_font-item_YQffA","typography-variations":"menus_typography_typography-variations_wbNM3","spacing-options":"menus_typography_spacing-options_RUpAK","line-height":"menus_typography_line-height_S7zsF","letter-spacing":"menus_typography_letter-spacing_8R6p2","asset-element":"menus_typography_asset-element_-LlIX","text-transform":"menus_typography_text-transform_U7Y3U","typography-entry":"menus_typography_typography-entry_Y6lvA","typography-selection-wrapper":"menus_typography_typography-selection-wrapper_W6ewx","is-selectable":"menus_typography_is-selectable_O6-D2","typography-sample":"menus_typography_typography-sample_6ruld","typography-name":"menus_typography_typography-name_b14xj","typography-font":"menus_typography_typography-font_hJIgO","selected":"menus_typography_selected_Ka-O9","typography-sample-input":"menus_typography_typography-sample-input_u2i8b","adv-typography-name":"menus_typography_adv-typography-name_PvB1X","name":"menus_typography_name_1SJal","font-size-select":"menus_typography_font-size-select_yexav","font-variant-select":"menus_typography_font-variant-select_-OQsO","line-height-input":"menus_typography_line-height-input_SG7be","letter-spacing-input":"menus_typography_letter-spacing-input_LShRc","header":"menus_typography_header_e-rUh","title":"menus_typography_title_uPejx","fonts-list":"menus_typography_fonts-list_ki8X-","label":"menus_typography_label_S-7jI"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss new file mode 100644 index 0000000000..0763cbf918 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss @@ -0,0 +1,304 @@ +// 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"; + +.typography-entry { + display: flex; + flex-direction: row; + align-items: center; + height: $s-32; + width: 100%; + border-radius: $br-8; + + .typography-selection-wrapper { + display: grid; + grid-template-columns: $s-24 auto 1fr; + flex: 1; + height: 100%; + width: 100%; + padding: 0 $s-12; + + &.is-selectable { + cursor: pointer; + } + + .typography-sample { + @include flexCenter; + min-width: $s-24; + color: var(--assets-item-name-foreground-color-hover); + } + .typography-name, + .typography-font { + @include titleTipography; + @include textEllipsis; + display: flex; + align-items: center; + justify-content: flex-start; + margin-left: $s-6; + color: var(--assets-item-name-foreground-color-hover); + } + .typography-font { + display: flex; + align-items: center; + justify-content: flex-start; + min-width: 0; + color: var(--assets-item-name-foreground-color); + } + } + + &.selected { + border: $s-1 solid var(--assets-item-border-color); + } + + &:hover { + background-color: var(--assets-item-background-color-hover); + } +} + +.font-name-wrapper { + @include titleTipography; + display: flex; + align-items: center; + height: $s-32; + width: 100%; + border-radius: $br-8; + border: $s-1 solid transparent; + box-sizing: border-box; + background-color: var(--assets-item-background-color); + margin-bottom: $s-4; + padding: $s-8 $s-0 $s-8 $s-12; + + .typography-sample-input { + @include flexCenter; + width: $s-24; + height: 100%; + font-size: $fs-16; + } + .adv-typography-name { + @include removeInputStyle; + color: var(--input-foreground-color); + flex-grow: 1; + margin: 0; + } + .action-btn { + @extend .button-tertiary; + @include flexCenter; + width: $s-28; + height: $s-28; + svg { + @extend .button-icon-small; + } + } + &:focus-within { + border: $s-1 solid var(--input-border-color-active); + .adv-typography-name { + color: var(--input-foreground-color-active); + } + } + &:hover { + background-color: var(--assets-item-background-color-hover); + } +} + +.advanced-options-wrapper { + height: 100%; + width: 100%; + background-color: var(--title-background-color); + + .typography-options { + position: relative; + .font-option { + @include titleTipography; + @extend .asset-element; + padding-right: 0; + cursor: pointer; + .name { + flex-grow: 1; + } + .icon { + @include flexCenter; + height: $s-28; + width: $s-28; + svg { + @extend .button-icon; + transform: rotate(90deg); + } + } + } + .font-modifiers { + display: flex; + gap: $s-4; + .font-size-options { + @extend .asset-element; + @include titleTipography; + padding: 0; + flex-grow: 1; + .icon { + @include flexCenter; + height: $s-28; + min-width: $s-28; + svg { + @extend .button-icon; + transform: rotate(90deg); + } + } + .font-size-select { + @include removeInputStyle; + @include titleTipography; + height: 100%; + width: 100%; + margin: 0; + padding: 0; + } + } + .font-variant-options { + @extend .asset-element; + @include titleTipography; + padding: 0; + flex-grow: 2; + .icon { + @include flexCenter; + height: $s-28; + min-width: $s-28; + svg { + @extend .button-icon; + transform: rotate(90deg); + } + } + .font-variant-select { + @include removeInputStyle; + @include titleTipography; + height: 100%; + width: 100%; + margin: 0; + color: var(--assets-item-name-foreground-color); + option { + color: var(--assets-item-name-foreground-color); + } + &:hover { + color: var(--assets-item-name-foreground-color-hover); + option { + color: var(--assets-item-name-foreground-color-hover); + } + } + } + } + } + .typography-variations { + display: flex; + gap: $s-4; + align-items: center; + .spacing-options { + display: flex; + align-items: center; + gap: $s-4; + .line-height, + .letter-spacing { + @extend .asset-element; + .icon { + @include flexCenter; + width: $s-28; + svg { + @extend .button-icon-small; + } + } + .line-height-input, + .letter-spacing-input { + @include removeInputStyle; + @include titleTipography; + height: 100%; + width: 100%; + margin: 0; + color: var(--assets-item-name-foreground-color); + &:hover, + &:active, + &:focus { + color: var(--assets-item-name-foreground-color-hover); + } + } + } + } + .text-transform { + @extend .asset-element; + width: fit-content; + padding: 0; + } + } + } +} + +.font-selector { + @include flexCenter; + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + z-index: $z-index-10; + + .font-selector-dropdown { + display: flex; + flex-direction: column; + flex-grow: 1; + height: 100%; + .header { + display: flex; + flex-direction: column; + position: relative; + margin-bottom: $s-2; + .title { + @include tabTitleTipography; + margin: 9px 17px; + } + } + .fonts-list { + position: absolute; + top: $s-36; + left: 0; + display: flex; + flex-direction: column; + flex: 1 1 auto; + min-height: 100%; + height: $s-216; + width: 100%; + padding: $s-2; + border-radius: $br-8; + background-color: var(--dropdown-background-color); + box-shadow: 0px 0px $s-12 0px var(--menu-shadow-color); + } + .font-wrapper { + padding-bottom: $s-4; + cursor: pointer; + .font-item { + @extend .asset-element; + border-radius: $br-8; + display: flex; + .icon { + @include flexCenter; + height: $s-28; + width: $s-28; + svg { + @extend .button-icon-small; + } + } + &.selected { + color: var(--assets-item-name-foreground-color-hover); + .icon { + svg { + stroke: var(--assets-item-name-foreground-color-hover); + } + } + } + + .label { + @include titleTipography; + flex-grow: 1; + } + } + } + } +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs b/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs index 26d554ffe2..65b37b1e10 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs @@ -18,6 +18,7 @@ [app.main.data.workspace.path.shortcuts] [app.main.data.workspace.shortcuts] [app.main.store :as st] + [app.main.ui.components.search-bar :refer [search-bar]] [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] @@ -280,7 +281,7 @@ (for [command-translate sorted-filtered] (let [sc-by-translate (first (filter #(= (:translation (second %)) command-translate) elements)) [command comand-info] sc-by-translate - content (or (:show-command comand-info)(:command comand-info))] + content (or (:show-command comand-info) (:command comand-info))] [:li {:class (css :shortcuts-name) :key command-translate} [:span {:class (css :command-name)} @@ -326,7 +327,7 @@ :filter-term filter-term :match-section? match-section? :match-subsection? true}]) - + [:ul {:class (dom/classnames (css :subsection-menu) new-css-system :subsection-menu (not new-css-system))} (for [sub-translated sorted-translations] @@ -365,7 +366,7 @@ translations (map #(translation-keyname :sc %) (keys subs-bodys)) match-shortcut? (some #(matches-search % @filter-term) translations) visible? (some #(= % section-id) @open-sections)] - + (when (or match-section? match-subsection? match-shortcut?) [:div {:class (css :section) :on-click (manage-sections section-id)} @@ -502,6 +503,11 @@ (manage-sections-on-search value) (reset! filter-term value)))) + on-search-term-change-2 + (mf/use-callback + (fn [value] + (manage-sections-on-search value) + (reset! filter-term value))) on-search-clear-click (mf/use-callback (fn [_] @@ -528,31 +534,17 @@ (if new-css-system [:div {:class (css :shortcuts)} [:div {:class (css :shortcuts-header)} - [:div {:class (css :shortcuts-title)} "Keyboard Shortcuts"] + [:div {:class (css :shortcuts-title)} (tr "shortcuts.title")] [:div {:class (css :shortcuts-close-button) :on-click close-fn} i/close-refactor]] - ;; TODO Change this for search bar component [:div {:class (css :search-field)} - [:div {:class (css :search-box)} - [:span {:class (css :icon-wrapper)} - i/search-refactor] - [:input {:class (dom/classnames (css :input-text) true) - :id "shortcut-search" - :placeholder (tr "shortcuts.title") - :type "text" - :value @filter-term - :on-change on-search-term-change - :auto-complete "off" - :on-key-down manage-key-down}] - (when (not (str/empty? @filter-term)) - [:button - {:class (css :clear-btn) - :on-click on-search-clear-click - :on-key-down on-key-down} - [:span {:class (css :clear-icon)} - i/delete-text-refactor]])]] + [:& search-bar {:on-change on-search-term-change-2 + :clear-action on-search-clear-click + :value @filter-term + :placeholder (tr "shortcuts.title") + :icon (mf/html [:span {:class (css :search-icon)} i/search-refactor])}]] (if match-any? [:div {:class (dom/classnames (css :shortcuts-list) true)} diff --git a/frontend/src/app/main/ui/workspace/sidebar/shortcuts.css.json b/frontend/src/app/main/ui/workspace/sidebar/shortcuts.css.json index c2e9635b92..cba898593b 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/shortcuts.css.json +++ b/frontend/src/app/main/ui/workspace/sidebar/shortcuts.css.json @@ -1 +1 @@ -{"button-primary":"sidebar_shortcuts_button-primary_aIZ1F","shortcuts":"sidebar_shortcuts_shortcuts_cOJNo","shortcuts-header":"sidebar_shortcuts_shortcuts-header_0SZ19","shortcuts-close-button":"sidebar_shortcuts_shortcuts-close-button_gT7kn","button-secondary":"sidebar_shortcuts_button-secondary_dtWEN","button-icon":"sidebar_shortcuts_button-icon_rCHmV","button-icon-small":"sidebar_shortcuts_button-icon-small_9BnNh","shortcuts-list":"sidebar_shortcuts_shortcuts-list_z7osI","section-title":"sidebar_shortcuts_section-title_Dv7S-","collapsed-shortcuts":"sidebar_shortcuts_collapsed-shortcuts_XrOj5","subsection-title":"sidebar_shortcuts_subsection-title_--5j4","search-field":"sidebar_shortcuts_search-field_cDecA","search-box":"sidebar_shortcuts_search-box_vmYAl","clear-btn":"sidebar_shortcuts_clear-btn_vRbGu","clear-icon":"sidebar_shortcuts_clear-icon_ZL4ae","icon-wrapper":"sidebar_shortcuts_icon-wrapper_XaR8m","shortcuts-title":"sidebar_shortcuts_shortcuts-title_P38o9","input-text":"sidebar_shortcuts_input-text_e9n1x","section":"sidebar_shortcuts_section_Jxkqa","open":"sidebar_shortcuts_open_SxghD","subsection-name":"sidebar_shortcuts_subsection-name_rWvFY","section-name":"sidebar_shortcuts_section-name_SyF9-","subsection-menu":"sidebar_shortcuts_subsection-menu_FdH9L","sub-menu":"sidebar_shortcuts_sub-menu_95jTY","shortcuts-name":"sidebar_shortcuts_shortcuts-name_hPkq6","command-name":"sidebar_shortcuts_command-name_Cujed","keys":"sidebar_shortcuts_keys_-pUnF","key":"sidebar_shortcuts_key_QyU8q","space":"sidebar_shortcuts_space_aODdu","not-found":"sidebar_shortcuts_not-found_bKEb0"} \ No newline at end of file +{"button-primary":"sidebar_shortcuts_button-primary_aIZ1F","button-secondary":"sidebar_shortcuts_button-secondary_dtWEN","button-tertiary":"sidebar_shortcuts_button-tertiary_3VDIw","shortcuts":"sidebar_shortcuts_shortcuts_cOJNo","shortcuts-header":"sidebar_shortcuts_shortcuts-header_0SZ19","shortcuts-close-button":"sidebar_shortcuts_shortcuts-close-button_gT7kn","button-tag":"sidebar_shortcuts_button-tag_3LImZ","button-icon":"sidebar_shortcuts_button-icon_rCHmV","button-icon-small":"sidebar_shortcuts_button-icon-small_9BnNh","shortcuts-list":"sidebar_shortcuts_shortcuts-list_z7osI","section-title":"sidebar_shortcuts_section-title_Dv7S-","collapsed-shortcuts":"sidebar_shortcuts_collapsed-shortcuts_XrOj5","subsection-title":"sidebar_shortcuts_subsection-title_--5j4","search-field":"sidebar_shortcuts_search-field_cDecA","search-icon":"sidebar_shortcuts_search-icon_NSAwd","search-box":"sidebar_shortcuts_search-box_vmYAl","clear-btn":"sidebar_shortcuts_clear-btn_vRbGu","clear-icon":"sidebar_shortcuts_clear-icon_ZL4ae","icon-wrapper":"sidebar_shortcuts_icon-wrapper_XaR8m","asset-element":"sidebar_shortcuts_asset-element_-zk6N","shortcuts-title":"sidebar_shortcuts_shortcuts-title_P38o9","input-text":"sidebar_shortcuts_input-text_e9n1x","section":"sidebar_shortcuts_section_Jxkqa","open":"sidebar_shortcuts_open_SxghD","subsection-name":"sidebar_shortcuts_subsection-name_rWvFY","section-name":"sidebar_shortcuts_section-name_SyF9-","subsection-menu":"sidebar_shortcuts_subsection-menu_FdH9L","sub-menu":"sidebar_shortcuts_sub-menu_95jTY","shortcuts-name":"sidebar_shortcuts_shortcuts-name_hPkq6","command-name":"sidebar_shortcuts_command-name_Cujed","keys":"sidebar_shortcuts_keys_-pUnF","key":"sidebar_shortcuts_key_QyU8q","space":"sidebar_shortcuts_space_aODdu","not-found":"sidebar_shortcuts_not-found_bKEb0"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/shortcuts.scss b/frontend/src/app/main/ui/workspace/sidebar/shortcuts.scss index 42c3b385d3..e8527b9c66 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/shortcuts.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/shortcuts.scss @@ -24,7 +24,7 @@ } .shortcuts-close-button { - @extend .button-primary; + @extend .button-tertiary; position: absolute; right: $s-2; top: $s-2; @@ -45,7 +45,6 @@ border-radius: $br-8; font-family: "worksans", sans-serif; background-color: var(--color-background-tertiary); - .search-box { align-items: center; display: flex; @@ -59,6 +58,7 @@ } .input-text { + @include removeInputStyle; height: $s-32; width: 100%; margin: 0; @@ -66,7 +66,6 @@ border: 0; font-size: $fs-12; color: var(--color-foreground-primary); - background-color: transparent; &::placeholder { color: var(--color-foreground-secondary); } @@ -87,6 +86,13 @@ } } } + .search-icon { + @include flexCenter; + width: $s-28; + svg { + @extend .button-icon-small; + } + } } .section { diff --git a/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs b/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs index 0872288152..c016b0cc17 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs @@ -13,6 +13,7 @@ [app.main.data.workspace :as dw] [app.main.refs :as refs] [app.main.store :as st] + [app.main.ui.components.title-bar :refer [title-bar]] [app.main.ui.context :as ctx] [app.main.ui.hooks :as hooks] [app.main.ui.hooks.resize :refer [use-resize-hook]] @@ -239,22 +240,20 @@ [:div {:class (dom/classnames (css :sitemap) true) :ref parent-ref :style #js {"--height" (str size "px")}} - [:div {:class (dom/classnames (css :pages-tool-bar) true)} - [:button {:class (dom/classnames (css :page-tool-bar-title) true) - :on-click toggle-pages} - [:span {:class (dom/classnames (css :collapsable-button) true) - :style {:transform (when (not @show-pages?) "rotate(-90deg)")}} - i/arrow-refactor] - (tr "workspace.sidebar.sitemap")] + [:& title-bar {:collapsable? true + :collapsed? (not @show-pages?) + :on-collapsed toggle-pages + :title (tr "workspace.sidebar.sitemap") + :klass :title-spacing-sitemap} + (if workspace-read-only? [:div {:class (dom/classnames (css :view-only-mode) true)} (tr "labels.view-only")] - [:* - [:button {:class (dom/classnames (css :add-page) true) - :on-click create} - i/add-refactor]])] + [:button {:class (dom/classnames (css :add-page) true) + :on-click create} + i/add-refactor])] [:div {:class (dom/classnames (css :tool-window-content) true)} [:& pages-list {:file file :key (:id file)}]] diff --git a/frontend/src/app/main/ui/workspace/sidebar/sitemap.css.json b/frontend/src/app/main/ui/workspace/sidebar/sitemap.css.json index 46ae1745fb..0aec9ae5c2 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/sitemap.css.json +++ b/frontend/src/app/main/ui/workspace/sidebar/sitemap.css.json @@ -1 +1 @@ -{"button-primary":"sidebar_sitemap_button-primary_Z-bKW","sitemap":"sidebar_sitemap_sitemap_kvKKx","pages-tool-bar":"sidebar_sitemap_pages-tool-bar_n1yfA","add-page":"sidebar_sitemap_add-page_r8Ibb","button-secondary":"sidebar_sitemap_button-secondary_a56LZ","button-icon":"sidebar_sitemap_button-icon_MkibT","page-tool-bar-title":"sidebar_sitemap_page-tool-bar-title_D7h-S","collapsable-button":"sidebar_sitemap_collapsable-button_Xt79y","button-icon-small":"sidebar_sitemap_button-icon-small_Mhipv","tool-window-content":"sidebar_sitemap_tool-window-content_G-Nut","pages-list":"sidebar_sitemap_pages-list_cb1Mx","page-element":"sidebar_sitemap_page-element_iR9wf","element-list-body":"sidebar_sitemap_element-list-body_OIVac","page-actions":"sidebar_sitemap_page-actions_QTuKw","page-icon":"sidebar_sitemap_page-icon_ujSjM","view-only-mode":"sidebar_sitemap_view-only-mode_JrsYg","resize-area":"sidebar_sitemap_resize-area_JgdjZ","dnd-over-top":"sidebar_sitemap_dnd-over-top_kGfcb","dnd-over-bot":"sidebar_sitemap_dnd-over-bot_352W2","dnd-over":"sidebar_sitemap_dnd-over_Sf5e2","page-name":"sidebar_sitemap_page-name_601Ii","element-name":"sidebar_sitemap_element-name_iMex0","on-drag":"sidebar_sitemap_on-drag_v3GM8","selected":"sidebar_sitemap_selected_mCOlT","hidden":"sidebar_sitemap_hidden_viFSn"} \ No newline at end of file +{"button-primary":"sidebar_sitemap_button-primary_Z-bKW","button-secondary":"sidebar_sitemap_button-secondary_a56LZ","button-tertiary":"sidebar_sitemap_button-tertiary_E2hzd","sitemap":"sidebar_sitemap_sitemap_kvKKx","add-page":"sidebar_sitemap_add-page_r8Ibb","button-tag":"sidebar_sitemap_button-tag_u1NAz","button-icon":"sidebar_sitemap_button-icon_MkibT","button-icon-small":"sidebar_sitemap_button-icon-small_Mhipv","tool-window-content":"sidebar_sitemap_tool-window-content_G-Nut","pages-list":"sidebar_sitemap_pages-list_cb1Mx","page-element":"sidebar_sitemap_page-element_iR9wf","element-list-body":"sidebar_sitemap_element-list-body_OIVac","page-actions":"sidebar_sitemap_page-actions_QTuKw","page-icon":"sidebar_sitemap_page-icon_ujSjM","asset-element":"sidebar_sitemap_asset-element_I1-m4","view-only-mode":"sidebar_sitemap_view-only-mode_JrsYg","resize-area":"sidebar_sitemap_resize-area_JgdjZ","dnd-over-top":"sidebar_sitemap_dnd-over-top_kGfcb","dnd-over-bot":"sidebar_sitemap_dnd-over-bot_352W2","dnd-over":"sidebar_sitemap_dnd-over_Sf5e2","page-name":"sidebar_sitemap_page-name_601Ii","element-name":"sidebar_sitemap_element-name_iMex0","on-drag":"sidebar_sitemap_on-drag_v3GM8","selected":"sidebar_sitemap_selected_mCOlT","hidden":"sidebar_sitemap_hidden_viFSn"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/sitemap.scss b/frontend/src/app/main/ui/workspace/sidebar/sitemap.scss index 037a925f39..51c8984dd9 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/sitemap.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/sitemap.scss @@ -14,63 +14,26 @@ width: 100%; height: var(--height, 200px); - .pages-tool-bar { - display: flex; - align-items: center; - min-height: $s-32; - padding: 0 $s-8 0 0; - margin: $s-2 0; - .page-tool-bar-title { - @include flexCenter; - @include tabTitleTipography; - @include buttonStyle; - flex-grow: 1; - gap: $s-4; - justify-content: flex-start; - padding: 0; - margin: 0; - color: var(--title-foreground-color); - .collapsable-button { - @include flexCenter; - height: $s-24; - width: $s-24; - padding: 0 $s-4 0 $s-8; - border-radius: $br-8; - svg { - @extend .button-icon; - height: $s-12; - width: $s-12; - transform: rotate(90deg); - } - } - &:hover { - color: var(--title-foreground-color-hover); - svg { - stroke: var(--icon-foreground-hover); - } - } - } - .add-page { - @extend .button-primary; - height: $s-32; - width: calc($s-24 + $s-4); - padding: 0; - border-radius: $br-8; - svg { - @extend .button-icon; - transform: rotate(90deg); - } - } - .view-only-mode { - @include flexCenter; - @include titleTipography; - height: $s-20; - padding: $s-4 $s-6; - border: 1px solid var(--tag-background-color); - border-radius: $br-6; - color: var(--tag-background-color); + .add-page { + @extend .button-tertiary; + height: $s-32; + width: calc($s-24 + $s-4); + padding: 0; + border-radius: $br-8; + svg { + @extend .button-icon; + transform: rotate(90deg); } } + .view-only-mode { + @include flexCenter; + @include titleTipography; + height: $s-20; + padding: $s-4 $s-6; + border: 1px solid var(--tag-background-color); + border-radius: $br-6; + color: var(--tag-background-color); + } .resize-area { position: absolute; bottom: -8px; @@ -163,6 +126,7 @@ input.element-name { @include textEllipsis; @include titleTipography; + @include removeInputStyle; flex-grow: 1; height: $s-28; max-width: calc(var(--parent-size) - (var(--depth) * var(--layer-indentation-size))); @@ -170,8 +134,6 @@ padding-left: $s-6; border-radius: $br-8; border: 1px solid var(--input-border-color-focus); - outline: none; - background-color: transparent; color: var(--layer-row-foreground-color); } } @@ -257,4 +219,7 @@ } } } + :global(.title-spacing-sitemap) { + padding-right: $s-8; + } } diff --git a/frontend/src/app/main/ui/workspace/text_palette.css.json b/frontend/src/app/main/ui/workspace/text_palette.css.json index 236e19b66e..bf4e65ccca 100644 --- a/frontend/src/app/main/ui/workspace/text_palette.css.json +++ b/frontend/src/app/main/ui/workspace/text_palette.css.json @@ -1 +1 @@ -{"button-primary":"workspace_text_palette_button-primary_1umSD","button-secondary":"workspace_text_palette_button-secondary_VOIWz","button-icon":"workspace_text_palette_button-icon_bcydd","text-palette":"workspace_text_palette_text-palette_0yeGp","left-arrow":"workspace_text_palette_left-arrow_iSjPL","right-arrow":"workspace_text_palette_right-arrow_cWHr6","button-icon-small":"workspace_text_palette_button-icon-small_wGyH7","disabled":"workspace_text_palette_disabled_EF36J","text-palette-content":"workspace_text_palette_text-palette-content_anJb5","text-palette-inside":"workspace_text_palette_text-palette-inside_LgHnf","typography-item":"workspace_text_palette_typography-item_d0vFL","typography-name":"workspace_text_palette_typography-name_NVBRv","typography-font":"workspace_text_palette_typography-font_paqmC","typography-data":"workspace_text_palette_typography-data_eKyme","mid-item":"workspace_text_palette_mid-item_uTcD2","small-item":"workspace_text_palette_small-item_1Y6mx"} \ No newline at end of file +{"button-primary":"workspace_text_palette_button-primary_1umSD","button-secondary":"workspace_text_palette_button-secondary_VOIWz","button-tertiary":"workspace_text_palette_button-tertiary_4AWFN","button-tag":"workspace_text_palette_button-tag_TMcKw","button-icon":"workspace_text_palette_button-icon_bcydd","text-palette":"workspace_text_palette_text-palette_0yeGp","left-arrow":"workspace_text_palette_left-arrow_iSjPL","right-arrow":"workspace_text_palette_right-arrow_cWHr6","button-icon-small":"workspace_text_palette_button-icon-small_wGyH7","asset-element":"workspace_text_palette_asset-element_edxQB","disabled":"workspace_text_palette_disabled_EF36J","text-palette-content":"workspace_text_palette_text-palette-content_anJb5","text-palette-inside":"workspace_text_palette_text-palette-inside_LgHnf","typography-item":"workspace_text_palette_typography-item_d0vFL","typography-name":"workspace_text_palette_typography-name_NVBRv","typography-font":"workspace_text_palette_typography-font_paqmC","typography-data":"workspace_text_palette_typography-data_eKyme","mid-item":"workspace_text_palette_mid-item_uTcD2","small-item":"workspace_text_palette_small-item_1Y6mx"} \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/text_palette_ctx_menu.css.json b/frontend/src/app/main/ui/workspace/text_palette_ctx_menu.css.json index 3629792d0a..d3f82dd944 100644 --- a/frontend/src/app/main/ui/workspace/text_palette_ctx_menu.css.json +++ b/frontend/src/app/main/ui/workspace/text_palette_ctx_menu.css.json @@ -1 +1 @@ -{"button-primary":"workspace_text_palette_ctx_menu_button-primary_bkGXB","button-secondary":"workspace_text_palette_ctx_menu_button-secondary_mbPs7","button-icon":"workspace_text_palette_ctx_menu_button-icon_oklnh","button-icon-small":"workspace_text_palette_ctx_menu_button-icon-small_ebriD","workspace-context-menu":"workspace_text_palette_ctx_menu_workspace-context-menu_OShZn","palette-library":"workspace_text_palette_ctx_menu_palette-library_pDyi5","selected":"workspace_text_palette_ctx_menu_selected_k3kOd","icon-wrapper":"workspace_text_palette_ctx_menu_icon-wrapper_Xoj9o","file-library":"workspace_text_palette_ctx_menu_file-library_t-25M","library-name":"workspace_text_palette_ctx_menu_library-name_TGs9Z"} \ No newline at end of file +{"button-primary":"workspace_text_palette_ctx_menu_button-primary_bkGXB","button-secondary":"workspace_text_palette_ctx_menu_button-secondary_mbPs7","button-tertiary":"workspace_text_palette_ctx_menu_button-tertiary_Z74wM","button-tag":"workspace_text_palette_ctx_menu_button-tag_OmlzA","button-icon":"workspace_text_palette_ctx_menu_button-icon_oklnh","button-icon-small":"workspace_text_palette_ctx_menu_button-icon-small_ebriD","workspace-context-menu":"workspace_text_palette_ctx_menu_workspace-context-menu_OShZn","palette-library":"workspace_text_palette_ctx_menu_palette-library_pDyi5","selected":"workspace_text_palette_ctx_menu_selected_k3kOd","icon-wrapper":"workspace_text_palette_ctx_menu_icon-wrapper_Xoj9o","file-library":"workspace_text_palette_ctx_menu_file-library_t-25M","asset-element":"workspace_text_palette_ctx_menu_asset-element_-ynNV","library-name":"workspace_text_palette_ctx_menu_library-name_TGs9Z"} \ No newline at end of file