♻️ Improve export UI

This commit is contained in:
Eva Marco 2026-07-22 14:48:29 +02:00
parent ae6ca9e115
commit c7a00c6829
5 changed files with 259 additions and 297 deletions

View File

@ -17,7 +17,7 @@
[potok.v2.core :as ptk]))
(def valid-types
(d/ordered-set :all :merge :detach))
(d/ordered-set :all :merge :detach :link-later))
(def valid-formats
#{:binfile-v1 :binfile-v3 :legacy-zip})

View File

@ -13,8 +13,13 @@
[app.main.data.exports.files :as fexp]
[app.main.data.modal :as modal]
[app.main.store :as st]
[app.main.ui.ds.buttons.button :refer [button*]]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]]
[app.main.ui.ds.foundations.typography :as t]
[app.main.ui.ds.foundations.typography.heading :refer [heading*]]
[app.main.ui.ds.foundations.typography.text :refer [text*]]
[app.main.ui.ds.product.loader :refer [loader*]]
[app.main.ui.icons :as deprecated-icon]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[beicon.v2.core :as rx]
@ -55,13 +60,20 @@
[:div {:class (stl/css :file-name)}
(if (:loading file)
[:> loader* {:width 16
[:> loader* {:width 26
:title (tr "labels.loading")}]
[:span {:class (stl/css :file-icon)}
(cond (:export-success? file) deprecated-icon/tick
(:export-error? file) deprecated-icon/close)])
(cond (:export-success? file)
[:> icon* {:icon-id i/tick
:class (stl/css :file-icon)
:size "s"}]
(:export-error? file)
[:> icon* {:icon-id i/close
:class (stl/css :file-icon)
:size "s"}]))
[:div {:class (stl/css :file-name-label)}
[:> text* {:class (stl/css :file-name-label)
:as "span"
:typography t/body-large}
(:name file)]]])
(mf/defc export-dialog
@ -119,38 +131,59 @@
[:div {:class (stl/css :modal-overlay)}
[:div {:class (stl/css :modal-container)}
[:div {:class (stl/css :modal-header)}
[:h2 {:class (stl/css :modal-title)}
[:> heading* {:level 2
:typography t/headline-large
:class (stl/css :modal-title)}
(tr "files-download-modal.title")]
[:button {:class (stl/css :modal-close-btn)
:on-click on-cancel} deprecated-icon/close]]
[:> icon-button* {:variant "ghost"
:aria-label (tr "labels.close")
:on-click on-cancel
:class (stl/css :modal-close-btn)
:icon i/close}]]
(cond
(= status :prepare)
[:*
[:div {:class (stl/css :modal-content)}
[:p {:class (stl/css :modal-msg)} (tr "files-download-modal.description-1")]
[:p {:class (stl/css :modal-scd-msg)} (tr "files-download-modal.description-2")]
;; TODO: Add translation
[:> text* {:as "p" :typography t/body-large :class (stl/css :modal-msg)}
"What do you want to do with linked libraries?"]
(for [type fexp/valid-types]
[:div {:class (stl/css :export-option true)
:key (name type)}
[:label {:for (str "export-" type)
:class (stl/css-case :global/checked (= selected type))}
:class (stl/css :export-option-label)}
;; Execution time translation strings:
;; (tr "files-download-modal.options.all.message")
;; (tr "files-download-modal.options.all.title")
;; (tr "files-download-modal.options.detach.message")
;; (tr "files-download-modal.options.detach.title")
;; (tr "files-download-modal.options.merge.message")
;; (tr "files-download-modal.options.merge.title")
[:span {:class (stl/css-case :global/checked (= selected type))}
;; (tr "files-export-modal.options.all.title")
;; (tr "files-export-modal.options.all.message")
;; (tr "files-export-modal.options.merge.title")
;; (tr "files-export-modal.options.merge.message")
;; (tr "files-export-modal.options.detach.title")
;; (tr "files-export-modal.options.detach.message")
;; (tr "files-export-modal.options.link-later.title")
;; (tr "files-export-modal.options.link-later.message")
[:span {:class (stl/css-case
:option-icon-wrapper true
:checked (= selected type))}
(when (= selected type)
deprecated-icon/status-tick)]
[:svg {:class (stl/css :option-icon)
:viewBox "0 0 8 8"
:width 8
:height 8
:aria-hidden true}
[:circle {:cx 4 :cy 4 :r 4}]])]
[:div {:class (stl/css :option-content)}
[:h3 {:class (stl/css :modal-subtitle)}
(tr (dm/str "files-download-modal.options." (d/name type) ".title"))]
[:p {:class (stl/css :modal-msg)}
(tr (dm/str "files-download-modal.options." (d/name type) ".message"))]]
[:> heading* {:level 3
:typography t/body-large
:class (stl/css :option-title)}
(tr (dm/str "files-export-modal.options." (d/name type) ".title"))]
[:> text* {:as "p" :typography t/body-large :class (stl/css :modal-msg)}
(tr (dm/str "files-export-modal.options." (d/name type) ".message"))]]
[:input {:type "radio"
:class (stl/css :option-input)
@ -162,15 +195,15 @@
[:div {:class (stl/css :modal-footer)}
[:div {:class (stl/css :action-buttons)}
[:input {:class (stl/css :cancel-button)
:type "button"
:value (tr "labels.cancel")
:on-click on-cancel}]
[:> button* {:variant "secondary"
:type "button"
:on-click on-cancel}
(tr "labels.cancel")]
[:input {:class (stl/css :accept-btn)
:type "button"
:value (tr "labels.continue")
:on-click on-accept}]]]]
[:> button* {:variant "primary"
:type "button"
:on-click on-accept}
(tr "labels.continue")]]]]
(= status :exporting)
(let [in-progress? (->> state :files (some :loading))]
@ -180,15 +213,15 @@
[:> export-entry* {:file file :key (dm/str (:id file))}])
(when in-progress?
[:div {:class (stl/css :status-message)
:role "status"
:aria-live "polite"}
[:> text* {:as "span" :typography t/body-large :class (stl/css :status-message)
:role "status"
:aria-live "polite"}
(tr "labels.downloading-file")])]
[:div {:class (stl/css :modal-footer)}
[:div {:class (stl/css :action-buttons)}
[:input {:class (stl/css :accept-btn)
:type "button"
:value (tr "labels.close")
:disabled in-progress?
:on-click on-cancel}]]]]))]]))
[:> button* {:variant "primary"
:type "button"
:disabled in-progress?
:on-click on-cancel}
(tr "labels.close")]]]]))]]))

View File

@ -4,289 +4,205 @@
//
// Copyright (c) KALEIDOS INC Sucursal en España SL
@use "refactor/common-refactor.scss" as deprecated;
@use "ds/typography.scss" as *;
@use "ds/_borders.scss" as *;
@use "ds/spacing.scss" as *;
@use "ds/_sizes.scss" as *;
@use "ds/_utils.scss" as *;
@use "ds/z-index.scss" as *;
// EXPORT MODAL
.modal-overlay {
@extend %modal-overlay-base;
&.transparent {
background-color: transparent;
}
display: flex;
justify-content: center;
align-items: center;
position: fixed;
inset-inline-start: 0;
inset-block-start: 0;
block-size: 100%;
inline-size: 100%;
z-index: var(--z-index-set);
background-color: var(--overlay-color);
}
.modal-container {
@extend %modal-container-base;
max-height: calc(10 * deprecated.$s-80);
}
.modal-header {
margin-bottom: deprecated.$s-24;
}
.modal-title {
@include deprecated.headline-medium-typography;
color: var(--modal-title-foreground-color);
}
.modal-close-btn {
@extend %modal-close-btn-base;
position: relative;
display: flex;
flex-direction: column;
gap: var(--sp-xxxl);
padding: var(--sp-xxxl);
border-radius: $br-8;
background-color: var(--color-background-primary);
border: $b-2 solid var(--color-background-quaternary);
min-block-size: $sz-192;
inline-size: $sz-512;
max-block-size: calc(10 * px2rem(80));
}
.modal-content {
@include deprecated.body-small-typography;
display: flex;
flex-direction: column;
gap: var(--sp-l);
margin-block-end: var(--sp-xxl);
}
margin-bottom: deprecated.$s-24;
.modal-content-extended {
gap: var(--sp-xxl);
}
.modal-link {
@include deprecated.body-large-typography;
.modal-title {
color: var(--color-foreground-primary);
}
text-decoration: none;
cursor: pointer;
color: var(--modal-link-foreground-color);
}
.modal-close-btn {
position: absolute;
inset-block-start: var(--sp-m);
inset-inline-end: var(--sp-m);
}
.selection-header {
@include deprecated.flex-row;
.modal-msg {
color: var(--color-foreground-secondary);
margin: 0;
}
height: deprecated.$s-32;
margin-bottom: deprecated.$s-4;
.selection-btn {
@include deprecated.button-style;
@extend %input-checkbox;
@include deprecated.flex-center;
height: deprecated.$s-24;
width: deprecated.$s-24;
padding: 0;
margin-left: deprecated.$s-16;
span {
@extend %checkbox-icon;
}
}
.selection-title {
@include deprecated.body-large-typography;
color: var(--modal-text-foreground-color);
}
}
.selection-wrapper {
position: relative;
width: 100%;
height: fit-content;
}
.selection-shadow {
width: 100%;
height: 100%;
&::after {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 50px;
background: linear-gradient(to top, rgb(24 24 26 / 1) 0%, rgb(24 24 26 / 0) 100%);
content: "";
pointer-events: none;
}
}
.selection-list {
@include deprecated.flex-column;
max-height: deprecated.$s-400;
overflow-y: auto;
padding-bottom: deprecated.$s-12;
.selection-row {
@include deprecated.flex-row;
background-color: var(--entry-background-color);
min-height: deprecated.$s-40;
border-radius: deprecated.$br-8;
.selection-btn {
@include deprecated.button-style;
display: grid;
grid-template-columns: min-content auto 1fr auto auto;
align-items: center;
width: 100%;
height: 10%;
gap: deprecated.$s-8;
padding: 0 deprecated.$s-16;
.checkbox-wrapper {
@extend %input-checkbox;
@include deprecated.flex-center;
height: deprecated.$s-24;
width: deprecated.$s-24;
padding: 0;
.checkobox-tick {
@extend %checkbox-icon;
}
}
.selection-name {
@include deprecated.body-large-typography;
@include deprecated.text-ellipsis;
flex-grow: 1;
color: var(--modal-text-foreground-color);
text-align: start;
}
.selection-scale {
@include deprecated.body-large-typography;
@include deprecated.text-ellipsis;
min-width: deprecated.$s-108;
padding: deprecated.$s-12;
color: var(--modal-text-foreground-color);
}
.selection-extension {
@include deprecated.body-large-typography;
@include deprecated.text-ellipsis;
min-width: deprecated.$s-72;
padding: deprecated.$s-12;
color: var(--modal-text-foreground-color);
}
}
.image-wrapper {
@include deprecated.flex-center;
min-height: deprecated.$s-32;
min-width: deprecated.$s-32;
background-color: var(--app-white);
border-radius: deprecated.$br-6;
margin: auto 0;
img,
svg {
object-fit: contain;
max-height: deprecated.$s-40;
}
}
}
}
.option-content {
display: flex;
flex-direction: column;
}
.status-message {
@include deprecated.body-small-typography;
color: var(--modal-title-foreground-color);
color: var(--color-foreground-primary);
font-style: italic;
}
.action-buttons {
@extend %modal-action-btns;
}
.cancel-button {
@extend %modal-cancel-btn;
}
.accept-btn {
@extend %modal-accept-btn;
&.danger {
@extend %modal-danger-btn;
}
}
.modal-scd-msg,
.modal-subtitle,
.modal-msg {
@include deprecated.body-large-typography;
color: var(--modal-text-foreground-color);
display: flex;
justify-content: flex-end;
gap: var(--sp-s);
}
.export-option {
@extend %input-checkbox;
width: 100%;
display: flex;
align-items: flex-start;
inline-size: 100%;
}
label {
align-items: flex-start;
.export-option-label {
--input-border-color: var(--input-checkbox-border-color-rest);
--input-icon-color: var(--color-background-primary);
.modal-subtitle {
@include deprecated.body-large-typography;
display: flex;
align-items: flex-start;
gap: px2rem(6);
cursor: pointer;
color: var(--color-foreground-primary);
color: var(--modal-title-foreground-color);
padding: 0.25rem 0;
}
&:hover {
--input-border-color: var(--color-accent-primary-muted);
}
span {
margin-top: deprecated.$s-8;
&:focus,
&:focus-within {
--input-border-color: var(--color-accent-primary);
}
}
.option-content {
@include deprecated.flex-column;
@include deprecated.body-large-typography;
.option-icon-wrapper {
--icon-display: none;
--background-color: var(--color-background-quaternary);
display: flex;
justify-content: center;
align-items: center;
inline-size: px2rem(16);
min-inline-size: px2rem(16);
block-size: px2rem(16);
margin-block-start: px2rem(10);
background-color: var(--background-color);
border: px2rem(1) solid var(--input-border-color);
border-radius: $br-circle;
&.checked {
--icon-display: block;
--input-border-color: var(--color-background-quaternary);
--input-icon-color: var(--color-background-primary);
--background-color: var(--color-accent-primary);
}
&:hover {
--input-border-color: var(--color-accent-primary-muted);
}
&:focus {
--input-border-color: var(--color-accent-primary);
}
}
.option-icon {
inline-size: px2rem(8);
block-size: px2rem(8);
display: var(--icon-display);
fill: var(--input-icon-color);
}
.option-input {
margin: 0;
}
.file-entry {
.file-name {
@include deprecated.flex-row;
.file-icon {
@include deprecated.flex-center;
height: deprecated.$s-16;
width: deprecated.$s-16;
svg {
@extend %button-icon-small;
stroke: var(--input-foreground);
}
}
.file-name-label {
@include deprecated.body-large-typography;
@include deprecated.text-ellipsis;
}
}
--file-entry-color: var(--color-foreground-secondary);
&.loading {
.file-name {
color: var(--modal-text-foreground-color);
color: var(--color-foreground-secondary);
}
}
&.error {
.file-name {
color: var(--modal-text-foreground-color);
color: var(--color-foreground-secondary);
.file-icon svg {
stroke: var(--modal-text-foreground-color);
stroke: var(--color-foreground-secondary);
}
}
}
&.success {
.file-name {
color: var(--modal-text-foreground-color);
color: var(--color-foreground-secondary);
.file-icon svg {
stroke: var(--modal-text-foreground-color);
stroke: var(--color-foreground-secondary);
}
}
}
}
.file-name {
display: flex;
align-items: center;
gap: var(--sp-m);
.file-icon {
display: flex;
justify-content: center;
align-items: center;
block-size: px2rem(16);
inline-size: px2rem(16);
color: var(--color-foreground-secondary);
}
.file-name-label {
display: block;
max-inline-size: 99%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.icon-status-tick {
fill: none;
stroke: var(--color-accent-primary);
}

View File

@ -2116,34 +2116,40 @@ msgid "files-download-modal.description-2"
msgstr "* Might include components, graphics, colors and/or typographies."
#: src/app/main/ui/exports/files.cljs:140
msgid "files-download-modal.options.all.message"
msgid "files-export-modal.options.all.message"
msgstr ""
"Files with shared libraries will be included in the export, maintaining "
"their linkage."
"Files with linked libraries will be included in the export, maintaining "
"their linkage. "
#: src/app/main/ui/exports/files.cljs:141
msgid "files-download-modal.options.all.title"
msgstr "Export shared libraries"
msgid "files-export-modal.options.all.title"
msgstr "Export file + libraries"
#: src/app/main/ui/exports/files.cljs:142
msgid "files-download-modal.options.detach.message"
msgstr ""
"Shared libraries will not be included in the export and no assets will be "
"added to the library. "
msgid "files-export-modal.options.detach.message"
msgstr "Linked library assets won't be included in the file."
#: src/app/main/ui/exports/files.cljs
msgid "files-export-modal.options.link-later.title"
msgstr "Link matching libraries on import"
#: src/app/main/ui/exports/files.cljs
msgid "files-export-modal.options.link-later.message"
msgstr "When imported, you'll be able to link existing libraries with matching names."
#: src/app/main/ui/exports/files.cljs:143
msgid "files-download-modal.options.detach.title"
msgstr "Treat shared library assets as basic objects"
msgid "files-export-modal.options.detach.title"
msgstr "Treat assets as basic objects"
#: src/app/main/ui/exports/files.cljs:144
msgid "files-download-modal.options.merge.message"
msgid "files-export-modal.options.merge.message"
msgstr ""
"Your file will be exported with all external assets merged into the file "
"Your file will be exported with library asset merged into the local "
"library."
#: src/app/main/ui/exports/files.cljs:145
msgid "files-download-modal.options.merge.title"
msgstr "Include shared library assets in file libraries"
msgid "files-export-modal.options.merge.title"
msgstr "Embed library assets in the file"
#: src/app/main/ui/exports/files.cljs:123
msgid "files-download-modal.title"

View File

@ -2063,33 +2063,40 @@ msgid "files-download-modal.description-2"
msgstr "* Pueden incluir components, gráficos, colores y/o tipografias."
#: src/app/main/ui/exports/files.cljs:140
msgid "files-download-modal.options.all.message"
msgid "files-export-modal.options.all.message"
msgstr ""
"Ficheros con librerias compartidas se inclurán en el paquete de exportación "
"Los ficheros con librerias compartidas se inclurán en el paquete de exportación "
"y mantendrán los enlaces."
#: src/app/main/ui/exports/files.cljs:141
msgid "files-download-modal.options.all.title"
msgstr "Exportar librerias compartidas"
msgid "files-export-modal.options.all.title"
msgstr "Exportar archivo + librerías"
#: src/app/main/ui/exports/files.cljs:142
msgid "files-download-modal.options.detach.message"
msgid "files-export-modal.options.detach.message"
msgstr ""
"Las biblioteca compartidas no se incluirán en la exportación y ningún "
"recurso será incluido en la biblioteca. "
"Las recursos de las bibliotecas compartidas no se incluirán en la exportación."
#: src/app/main/ui/exports/files.cljs
msgid "files-export-modal.options.link-later.title"
msgstr "Vincular librerías al importar"
#: src/app/main/ui/exports/files.cljs
msgid "files-export-modal.options.link-later.message"
msgstr "Al importar, podrás vincular bibliotecas existentes con el mismo nombre."
#: src/app/main/ui/exports/files.cljs:143
msgid "files-download-modal.options.detach.title"
msgstr "Usar los recursos como objetos básicos"
msgid "files-export-modal.options.detach.title"
msgstr "Tratar los recursos como objetos básicos"
#: src/app/main/ui/exports/files.cljs:144
msgid "files-download-modal.options.merge.message"
msgid "files-export-modal.options.merge.message"
msgstr ""
"Tu fichero será exportado con todos los recursos dentro de la libreria del "
"Tu fichero será exportado con todos los recursos externos dentro de la libreria del "
"propio fichero."
#: src/app/main/ui/exports/files.cljs:145
msgid "files-download-modal.options.merge.title"
msgid "files-export-modal.options.merge.title"
msgstr "Incluir librerias compartidas dentro de las librerias del fichero"
#: src/app/main/ui/exports/files.cljs:123