Merge remote-tracking branch 'origin/staging' into develop

This commit is contained in:
Andrey Antukh 2024-02-27 12:45:59 +01:00
commit 2a6589ab01
95 changed files with 2490 additions and 2036 deletions

View file

@ -2,7 +2,7 @@
{org.clojure/clojure {:mvn/version "1.11.1"}
org.clojure/data.json {:mvn/version "2.5.0"}
org.clojure/tools.cli {:mvn/version "1.0.219"}
org.clojure/clojurescript {:mvn/version "1.11.60"}
org.clojure/clojurescript {:mvn/version "1.11.132"}
org.clojure/test.check {:mvn/version "1.1.1"}
org.clojure/data.fressian {:mvn/version "1.0.0"}
@ -19,7 +19,7 @@
criterium/criterium {:mvn/version "0.4.6"}
metosin/jsonista {:mvn/version "0.3.8"}
metosin/malli {:mvn/version "0.13.0"}
metosin/malli {:mvn/version "0.14.0"}
expound/expound {:mvn/version "0.9.0"}
com.cognitect/transit-clj {:mvn/version "1.0.333"}
@ -63,7 +63,7 @@
{:dev
{:extra-deps
{org.clojure/tools.namespace {:mvn/version "RELEASE"}
thheller/shadow-cljs {:mvn/version "2.26.2"}
thheller/shadow-cljs {:mvn/version "2.27.4"}
com.clojure-goes-fast/clj-async-profiler {:mvn/version "RELEASE"}
com.bhauman/rebel-readline {:mvn/version "RELEASE"}
criterium/criterium {:mvn/version "RELEASE"}

View file

@ -15,7 +15,7 @@
"sax": "^1.2.4"
},
"devDependencies": {
"shadow-cljs": "2.26.2",
"shadow-cljs": "2.27.4",
"source-map-support": "^0.5.21",
"ws": "^8.13.0"
},

View file

@ -150,6 +150,22 @@
:else
(get-head-shape objects (get objects (:parent-id shape)) options))))
(defn get-parent-heads
"Get all component heads that are ancestors of the shape, in top-down order
(include self if it's also a head)."
[objects shape]
(->> (cfh/get-parents-with-self objects (:id shape))
(filter ctk/instance-head?)
(reverse)))
(defn get-parent-copy-heads
"Get all component heads that are ancestors of the shape, in top-down order,
excluding mains (include self if it's also a head)."
[objects shape]
(->> (cfh/get-parents-with-self objects (:id shape))
(filter #(and (ctk/instance-head? %) (ctk/in-component-copy? %)))
(reverse)))
(defn get-instance-root
"Get the parent shape at the top of the component instance (main or copy)."
[objects shape]
@ -392,7 +408,7 @@
(has-any-copy-parent? objects (:parent-id shape))))))
(defn has-any-main?
"Check if the shape has any children or parent that is a main component."
"Check if the shape is a main component or has any children or parent that is a main component."
[objects shape]
(let [children (cfh/get-children-with-self objects (:id shape))
parents (cfh/get-parents objects (:id shape))]
@ -400,6 +416,12 @@
(some ctk/main-instance? children)
(some ctk/main-instance? parents))))
(defn has-any-main-children?
"Check if the shape is a main component or has any children that is a main component."
[objects shape]
(let [children (cfh/get-children-with-self objects (:id shape))]
(some ctk/main-instance? children)))
(defn valid-shape-for-component?
"Check if a main component can be generated from this shape in terms of nested components:
- A main can't be the ancestor of another main
@ -412,7 +434,7 @@
(defn- invalid-structure-for-component?
"Check if the structure generated nesting children in parent is invalid in terms of nested components"
[objects parent children]
(let [selected-main-instance? (some true? (map #(has-any-main? objects %) children))
(let [selected-main-instance? (some true? (map #(has-any-main-children? objects %) children))
parent-in-component? (in-any-component? objects parent)
comps-nesting-loop? (not (->> children
(map #(cfh/components-nesting-loop? objects (:id %) (:id parent)))

View file

@ -117,6 +117,12 @@
[libraries component-id & {:keys [include-deleted?] :or {include-deleted? false}}]
(some #(ctkl/get-component (:data %) component-id include-deleted?) (vals libraries)))
(defn find-component-file
[file libraries component-file]
(if (and (some? file) (= component-file (:id file)))
file
(get libraries component-file)))
(defn get-component
"Retrieve a component from a library."
[libraries library-id component-id & {:keys [include-deleted?] :or {include-deleted? false}}]
@ -188,21 +194,30 @@
"Locate the nearest component in the local file or libraries, and retrieve the shape
referenced by the instance shape."
[file page libraries shape & {:keys [include-deleted?] :or {include-deleted? false}}]
(let [parent-heads (->> (cfh/get-parents-with-self (:objects page) (:id shape))
(filter ctk/instance-head?)
(reverse))
find-ref-shape-in-head
(let [find-ref-shape-in-head
(fn [head-shape]
(let [head-file (if (and (some? file) (= (:component-file head-shape) (:id file)))
file
(get libraries (:component-file head-shape)))
(let [head-file (find-component-file file libraries (:component-file head-shape))
head-component (when (some? head-file)
(ctkl/get-component (:data head-file) (:component-id head-shape) include-deleted?))]
(when (some? head-component)
(get-ref-shape (:data head-file) head-component shape))))]
(d/seek find-ref-shape-in-head parent-heads)))
(some find-ref-shape-in-head (ctn/get-parent-heads (:objects page) shape))))
(defn find-ref-component
"Locate the nearest component in the local file or libraries that is referenced by the
instance shape."
[file page libraries shape & {:keys [include-deleted?] :or {include-deleted? false}}]
(let [find-ref-component-in-head
(fn [head-shape]
(let [head-file (find-component-file file libraries (:component-file head-shape))
head-component (when (some? head-file)
(ctkl/get-component (:data head-file) (:component-id head-shape) include-deleted?))]
(when (some? head-component)
(when (get-ref-shape (:data head-file) head-component shape)
head-component))))]
(some find-ref-component-in-head (ctn/get-parent-copy-heads (:objects page) shape))))
(defn find-remote-shape
"Recursively go back by the :shape-ref of the shape until find the correct shape of the original component"
@ -229,6 +244,13 @@
remote-shape
(find-remote-shape component-container libraries remote-shape)))))
(defn direct-copy?
"Check if the shape is in a direct copy of the component (i.e. the shape-ref points to shapes inside
the component)."
[shape component page file libraries]
(let [ref-component (find-ref-component file page libraries shape :include-deleted? true)]
(true? (= (:id component) (:id ref-component)))))
(defn get-component-shapes
"Retrieve all shapes of the component"
[file-data component]

View file

@ -176,7 +176,7 @@
(contains? event-types event-type))
(dm/assert!
"The `:after-delay` event type incompatible with frame shapes"
"The `:after-delay` event type incompatible with not frame shapes"
(or (not= event-type :after-delay)
(cfh/frame-shape? shape)))

View file

@ -352,7 +352,7 @@ __metadata:
dependencies:
luxon: "npm:^3.4.2"
sax: "npm:^1.2.4"
shadow-cljs: "npm:2.26.2"
shadow-cljs: "npm:2.27.4"
source-map-support: "npm:^0.5.21"
ws: "npm:^8.13.0"
languageName: unknown
@ -1437,9 +1437,9 @@ __metadata:
languageName: node
linkType: hard
"shadow-cljs@npm:2.26.2":
version: 2.26.2
resolution: "shadow-cljs@npm:2.26.2"
"shadow-cljs@npm:2.27.4":
version: 2.27.4
resolution: "shadow-cljs@npm:2.27.4"
dependencies:
node-libs-browser: "npm:^2.2.1"
readline-sync: "npm:^1.4.7"
@ -1449,7 +1449,7 @@ __metadata:
ws: "npm:^7.4.6"
bin:
shadow-cljs: cli/runner.js
checksum: d504969ea28bcf3d5fc879c8111cb630a8ae910ea692bbfb0d73097fb336e13e642116db9fcc91524686a6824e71d439ef0df31941eabb6331feb4aa4146e830
checksum: bae23e71df9c2b2979259a0cde8747c923ee295f58ab4637c9d6b103d82542b40ef39172d4be2dbb94af2e6458a177d1ec96c1eb1e73b1d8f3a4ddb5eaaba7d4
languageName: node
linkType: hard

View file

@ -15,7 +15,7 @@
:dev
{:extra-deps
{thheller/shadow-cljs {:mvn/version "2.26.2"}}}
{thheller/shadow-cljs {:mvn/version "2.27.4"}}}
:shadow-cljs
{:main-opts ["-m" "shadow.cljs.devtools.cli"]}

View file

@ -22,7 +22,7 @@
"xregexp": "^5.1.1"
},
"devDependencies": {
"shadow-cljs": "2.26.2",
"shadow-cljs": "2.27.4",
"source-map-support": "^0.5.21"
},
"scripts": {

View file

@ -735,7 +735,7 @@ __metadata:
luxon: "npm:^3.4.4"
playwright: "npm:^1.40.1"
raw-body: "npm:^2.5.2"
shadow-cljs: "npm:2.26.2"
shadow-cljs: "npm:2.27.4"
source-map-support: "npm:^0.5.21"
xml-js: "npm:^1.6.11"
xregexp: "npm:^5.1.1"
@ -1842,9 +1842,9 @@ __metadata:
languageName: node
linkType: hard
"shadow-cljs@npm:2.26.2":
version: 2.26.2
resolution: "shadow-cljs@npm:2.26.2"
"shadow-cljs@npm:2.27.4":
version: 2.27.4
resolution: "shadow-cljs@npm:2.27.4"
dependencies:
node-libs-browser: "npm:^2.2.1"
readline-sync: "npm:^1.4.7"
@ -1854,7 +1854,7 @@ __metadata:
ws: "npm:^7.4.6"
bin:
shadow-cljs: cli/runner.js
checksum: d504969ea28bcf3d5fc879c8111cb630a8ae910ea692bbfb0d73097fb336e13e642116db9fcc91524686a6824e71d439ef0df31941eabb6331feb4aa4146e830
checksum: bae23e71df9c2b2979259a0cde8747c923ee295f58ab4637c9d6b103d82542b40ef39172d4be2dbb94af2e6458a177d1ec96c1eb1e73b1d8f3a4ddb5eaaba7d4
languageName: node
linkType: hard

View file

@ -19,8 +19,8 @@
:git/url "https://github.com/funcool/beicon.git"}
funcool/rumext
{:git/tag "v2.10"
:git/sha "d96ea18"
{:git/tag "v2.11.1"
:git/sha "c9197b0"
:git/url "https://github.com/funcool/rumext.git"}
instaparse/instaparse {:mvn/version "1.4.12"}
@ -41,7 +41,7 @@
:dev
{:extra-paths ["dev"]
:extra-deps
{thheller/shadow-cljs {:mvn/version "2.26.2"}
{thheller/shadow-cljs {:mvn/version "2.27.4"}
org.clojure/tools.namespace {:mvn/version "RELEASE"}
cider/cider-nrepl {:mvn/version "0.44.0"}}}

View file

@ -39,53 +39,53 @@
"storybook:build": "npm run storybook:compile && storybook build"
},
"devDependencies": {
"@storybook/addon-essentials": "^7.6.7",
"@storybook/addon-interactions": "^7.6.7",
"@storybook/addon-links": "^7.6.7",
"@storybook/addon-onboarding": "^1.0.10",
"@storybook/blocks": "^7.6.7",
"@storybook/react": "^7.6.7",
"@storybook/react-vite": "^7.6.7",
"@storybook/addon-essentials": "^7.6.17",
"@storybook/addon-interactions": "^7.6.17",
"@storybook/addon-links": "^7.6.17",
"@storybook/addon-onboarding": "^1.0.11",
"@storybook/blocks": "^7.6.17",
"@storybook/react": "^7.6.17",
"@storybook/react-vite": "^7.6.17",
"@storybook/testing-library": "^0.2.2",
"@types/node": "^20.10.6",
"@types/node": "^20.11.20",
"animate.css": "^4.1.1",
"autoprefixer": "^10.4.16",
"autoprefixer": "^10.4.17",
"concurrently": "^8.2.2",
"draft-js": "git+https://github.com/penpot/draft-js.git",
"fancy-log": "^2.0.0",
"gettext-parser": "^7.0.1",
"gettext-parser": "^8.0.0",
"gulp": "4.0.2",
"gulp-concat": "^2.6.1",
"gulp-gzip": "^1.4.2",
"gulp-mustache": "^5.0.0",
"gulp-postcss": "^9.0.1",
"gulp-postcss": "^10.0.0",
"gulp-rename": "^2.0.0",
"gulp-sass": "^5.1.0",
"gulp-sourcemaps": "^3.0.0",
"gulp-svg-sprite": "^2.0.3",
"jsdom": "^23.1.0",
"jsdom": "^24.0.0",
"map-stream": "0.0.7",
"marked": "^7.0.5",
"marked": "^12.0.0",
"mkdirp": "^3.0.1",
"nodemon": "^3.0.2",
"nodemon": "^3.1.0",
"npm-run-all": "^4.1.5",
"postcss": "^8.4.33",
"postcss": "^8.4.35",
"postcss-clean": "^1.2.2",
"prettier": "^3.1.1",
"prettier": "^3.2.5",
"prop-types": "^15.8.1",
"rimraf": "^5.0.5",
"sass": "^1.69.7",
"shadow-cljs": "2.26.2",
"storybook": "^7.6.7",
"sass": "^1.71.1",
"shadow-cljs": "2.27.4",
"storybook": "^7.6.17",
"typescript": "^5.3.3",
"vite": "^5.0.11",
"vitest": "^1.1.3"
"vite": "^5.1.4",
"vitest": "^1.3.1"
},
"dependencies": {
"date-fns": "^2.30.0",
"draft-js": "^0.11.7",
"eventsource-parser": "^1.1.1",
"date-fns": "^3.3.1",
"eventsource-parser": "^1.1.2",
"highlight.js": "^11.9.0",
"js-beautify": "^1.14.11",
"js-beautify": "^1.15.1",
"jszip": "^3.10.1",
"luxon": "^3.4.4",
"mousetrap": "^1.6.5",
@ -95,7 +95,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-virtualized": "^9.22.5",
"rxjs": "8.0.0-alpha.13",
"rxjs": "8.0.0-alpha.14",
"sax": "^1.3.0",
"source-map-support": "^0.5.21",
"tdigest": "^0.1.2",

View file

@ -57,16 +57,19 @@
--la-quaternary: #ff6fe0;
// STATUS COLOR
--status-color-success-50: #f0f8ff;
--status-color-success-200: #a7e8d9;
--status-color-success-500: #2d9f8f;
--status-color-success-950: #0a2927;
--status-color-warning-50: #fff4ed;
--status-color-warning-200: #ffc8a8;
--status-color-warning-500: #fe4811;
--status-color-warning-950: #440806;
--status-color-error-50: #fff0f3;
--status-color-error-200: #ffcada;
--status-color-error-500: #ff3277;
--status-color-error-950: #500124;
--status-color-info-50: #f0f8ff;
--status-color-info-200: #bae3fd;
--status-color-info-500: #0e9be9;
--status-color-info-950: #082c49;
// Status color default will change with theme and will be defined on theme files

View file

@ -311,15 +311,30 @@
--modal-separator-backogrund-color: var(--color-background-quaternary);
// ALERTS NOTIFICATION TOAST & STATUS WIDGET
--alert-background-color-success: var(--status-color-success-500);
--alert-foreground-color-success: var(--db-secondary); // We don't want this color to change with theme
--alert-background-color-warning: var(--status-color-warning-500);
--alert-foreground-color-warning: var(--app-white); // We don't want this color to change with theme
--alert-background-color-error: var(--status-color-error-500);
--alert-foreground-color-error: var(--app-white); // We don't want this color to change with theme
--alert-background-color-neutral: var(--color-background-quaternary);
--alert-foreground-color-neutral: var(--color-foreground-secondary);
--alert-foreground-color-neutral-active: var(--color-foreground-primary);
--alert-background-color-success: var(--color-success-background);
--alert-text-foreground-color-success: var(--color-foreground-primary);
--alert-icon-foreground-color-success: var(--color-success-foreground);
--alert-border-color-success: var(--color-success-foreground);
--alert-background-color-warning: var(--color-warning-background);
--alert-text-foreground-color-warning: var(--color-foreground-primary);
--alert-icon-foreground-color-warning: var(--color-warning-foreground);
--alert-border-color-warning: var(--color-warning-foreground);
--alert-background-color-error: var(--color-error-background);
--alert-text-foreground-color-error: var(--color-foreground-primary);
--alert-icon-foreground-color-error: var(--color-error-foreground);
--alert-border-color-error: var(--color-error-foreground);
--alert-background-color-info: var(--color-info-background);
--alert-text-foreground-color-info: var(--color-foreground-primary);
--alert-icon-foreground-color-info: var(--color-info-foreground);
--alert-border-color-info: var(--color-info-foreground);
--alert-background-color-default: var(--color-background-primary);
--alert-text-foreground-color-default: var(--color-foreground-primary);
--alert-icon-foreground-color-default: var(--color-foreground-primary);
--alert-border-color-default: var(--color-background-quaternary);
--notification-background-color-success: var();
--notification-foreground-color-success: var();

View file

@ -24,6 +24,18 @@
--color-accent-quaternary: var(--da-quaternary);
--color-component-highlight: var(--da-secondary);
--color-success-background: var(--status-color-success-950);
--color-success-foreground: var(--status-color-success-500);
--color-warning-background: var(--status-color-warning-950);
--color-warning-foreground: var(--status-color-warning-500);
--color-error-background: var(--status-color-error-950);
--color-error-foreground: var(--status-color-error-500);
--color-info-background: var(--status-color-info-950);
--color-info-foreground: var(--status-color-info-500);
--overlay-color: var(--db-primary-60);
--shadow-color: var(--db-secondary-30);

View file

@ -24,6 +24,18 @@
--color-accent-quaternary: var(--la-quaternary);
--color-component-highlight: var(--la-secondary);
--color-success-background: var(--status-color-success-200);
--color-success-foreground: var(--status-color-success-500);
--color-warning-background: var(--status-color-warning-200);
--color-warning-foreground: var(--status-color-warning-500);
--color-error-background: var(--status-color-error-200);
--color-error-foreground: var(--status-color-error-500);
--color-info-background: var(--status-color-info-200);
--color-info-foreground: var(--status-color-info-500);
--overlay-color: var(--lb-primary-60);
--shadow-color: var(--lf-secondary-40);
--radio-button-box-shadow: 0 0 0 1px var(--lb-secondary) inset;

View file

@ -70,6 +70,7 @@
(rx/of (msg/dialog
:content (tr "notifications.by-code.upgrade-version")
:controls :inline-actions
:notification-type :inline
:type level
:actions [{:label "Refresh" :callback force-reload!}]
:tag :notification)))

View file

@ -8,7 +8,7 @@
(:require
[app.common.exceptions :as ex]
[app.common.media :as cm]
[app.main.data.messages :as dm]
[app.main.data.messages :as msg]
[app.main.store :as st]
[app.util.i18n :refer [tr]]
[beicon.v2.core :as rx]
@ -46,13 +46,13 @@
(defn notify-start-loading
[]
(st/emit! (dm/show {:content (tr "media.loading")
:type :info
:timeout nil})))
(st/emit! (msg/show {:content (tr "media.loading")
:type :info
:timeout nil})))
(defn notify-finished-loading
[]
(st/emit! dm/hide))
(st/emit! msg/hide))
(defn process-error
[error]
@ -68,4 +68,4 @@
:else
(tr "errors.unexpected-error"))]
(rx/of (dm/error msg))))
(rx/of (msg/error msg))))

View file

@ -16,7 +16,7 @@
(declare show)
(def default-animation-timeout 600)
(def default-timeout 5000)
(def default-timeout 7000)
(def ^:private
schema:message
@ -27,6 +27,8 @@
[::sm/one-of #{:visible :hide}]]
[:position {:optional true}
[::sm/one-of #{:fixed :floating :inline}]]
[:notification-type {:optional true}
[::sm/one-of #{:inline :context :toast}]]
[:controls {:optional true}
[::sm/one-of #{:none :close :inline-actions :bottom-actions}]]
[:tag {:optional true}
@ -93,18 +95,18 @@
(rx/of hide))))))
(defn error
([content] (error content {}))
([content {:keys [timeout] :or {timeout default-timeout}}]
([content]
(show {:content content
:type :error
:position :fixed
:timeout timeout})))
:notification-type :toast
:position :fixed})))
(defn info
([content] (info content {}))
([content {:keys [timeout] :or {timeout default-timeout}}]
(show {:content content
:type :info
:notification-type :toast
:position :fixed
:timeout timeout})))
@ -113,6 +115,7 @@
([content {:keys [timeout] :or {timeout default-timeout}}]
(show {:content content
:type :success
:notification-type :toast
:position :fixed
:timeout timeout})))
@ -121,6 +124,7 @@
([content {:keys [timeout] :or {timeout default-timeout}}]
(show {:content content
:type :warning
:notification-type :toast
:position :fixed
:timeout timeout})))
@ -142,6 +146,7 @@
{:content content
:type :info
:position :floating
:notification-type :inline
:controls controls
:links links
:actions actions

View file

@ -609,24 +609,29 @@
(ptk/reify ::detach-selected-components
ptk/WatchEvent
(watch [it state _]
(let [page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
file (wsh/get-local-file state)
container (cfh/get-container file :page page-id)
libraries (wsh/get-libraries state)
selected (->> state
(wsh/lookup-selected)
(cfh/clean-loops objects))
(let [page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
file (wsh/get-local-file state)
container (cfh/get-container file :page page-id)
libraries (wsh/get-libraries state)
selected (->> state
(wsh/lookup-selected)
(cfh/clean-loops objects))
selected-objects (map #(get objects %) selected)
copies (filter ctk/in-component-copy? selected-objects)
can-detach? (and (seq copies)
(every? #(not (ctn/has-any-copy-parent? objects %)) selected-objects))
changes (when can-detach?
(reduce
(fn [changes id]
(dwlh/generate-detach-instance changes container libraries id))
(-> (pcb/empty-changes it)
(pcb/with-container container)
(pcb/with-objects objects))
selected))]
changes (reduce
(fn [changes id]
(dwlh/generate-detach-instance changes libraries container id))
(-> (pcb/empty-changes it)
(pcb/with-container container)
(pcb/with-objects objects))
selected)]
(rx/of (dch/commit-changes changes))))))
(rx/of (when can-detach?
(dch/commit-changes changes)))))))
(defn nav-to-component-file
[file-id component]
@ -1126,12 +1131,12 @@
:controls :inline-actions
:links [{:label (tr "workspace.updates.more-info")
:callback do-more-info}]
:actions [{:label (tr "workspace.updates.update")
:type :primary
:callback do-update}
{:label (tr "workspace.updates.dismiss")
:actions [{:label (tr "workspace.updates.dismiss")
:type :secondary
:callback do-dismiss}]
:callback do-dismiss}
{:label (tr "workspace.updates.update")
:type :primary
:callback do-update}]
:tag :sync-dialog)))))))
(defn component-changed

View file

@ -599,15 +599,10 @@
library (dm/get-in libraries [(:component-file shape-inst) :data])
component (or (ctkl/get-component library (:component-id shape-inst))
(and reset?
(ctkl/get-deleted-component library (:component-id shape-inst))))
component-shape (ctn/get-component-shape (:objects container) shape-inst)]
(ctkl/get-deleted-component library (:component-id shape-inst))))]
(if (and (ctk/in-component-copy? shape-inst)
(or (= (:id component) (:component-id component-shape)) reset?)) ; In a normal sync, we don't want to sync remote mains, only near
(or (ctf/direct-copy? shape-inst component container nil libraries) reset?)) ; In a normal sync, we don't want to sync remote mains, only direct/near
(let [redirect-shaperef (partial redirect-shaperef container libraries)
library (dm/get-in libraries [(:component-file shape-inst) :data])
component (or (ctkl/get-component library (:component-id shape-inst))
(and reset?
(ctkl/get-deleted-component library (:component-id shape-inst))))
shape-main (when component
(if (and reset? components-v2)

View file

@ -82,13 +82,14 @@
(defn get-snap-points [page-id frame-id remove-snap? zoom point coord]
(let [value (get point coord)
vbox @refs/vbox]
vbox @refs/vbox
ranges [[(- value (/ 0.5 zoom)) (+ value (/ 0.5 zoom))]]]
(->> (uw/ask! {:cmd :snaps/range-query
:page-id page-id
:frame-id frame-id
:axis coord
:bounds vbox
:ranges [[(- value (/ 0.5 zoom)) (+ value (/ 0.5 zoom))]]})
:ranges ranges})
(rx/take 1)
(rx/map (remove-from-snap-points remove-snap?)))))

View file

@ -173,6 +173,6 @@
(if edata
[:& static/exception-page {:data edata}]
[:*
[:& msgs/notifications]
[:& msgs/notifications-hub]
(when route
[:& main-page {:route route :profile profile}])])]]))

View file

@ -8,13 +8,13 @@
.auth-form {
width: 100%;
padding-bottom: $s-16;
padding-block-end: $s-16;
form {
display: flex;
flex-direction: column;
gap: $s-12;
margin-bottom: $s-24;
margin-block-end: $s-24;
}
}
@ -23,6 +23,10 @@
margin: $s-24 0;
}
.error-wrapper {
padding-block-end: $s-8;
}
.auth-title {
@include bigTitleTipography;
color: $df-primary;

View file

@ -11,7 +11,7 @@
[app.common.logging :as log]
[app.common.spec :as us]
[app.config :as cf]
[app.main.data.messages :as dm]
[app.main.data.messages :as msg]
[app.main.data.users :as du]
[app.main.repo :as rp]
[app.main.store :as st]
@ -19,7 +19,7 @@
[app.main.ui.components.forms :as fm]
[app.main.ui.components.link :as lk]
[app.main.ui.icons :as i]
[app.main.ui.messages :as msgs]
[app.main.ui.notifications.context-notification :refer [context-notification]]
[app.util.dom :as dom]
[app.util.i18n :refer [tr]]
[app.util.keyboard :as k]
@ -48,10 +48,10 @@
(cond
(and (= type :restriction)
(= code :provider-not-configured))
(st/emit! (dm/error (tr "errors.auth-provider-not-configured")))
(st/emit! (msg/error (tr "errors.auth-provider-not-configured")))
:else
(st/emit! (dm/error (tr "errors.generic"))))))))
(st/emit! (msg/error (tr "errors.generic"))))))))
(defn- login-with-ldap
[event params]
@ -67,13 +67,13 @@
(cond
(and (= type :restriction)
(= code :ldap-not-initialized))
(st/emit! (dm/error (tr "errors.ldap-disabled")))
(st/emit! (msg/error (tr "errors.ldap-disabled")))
(fn? on-error)
(on-error error)
:else
(st/emit! (dm/error (tr "errors.generic")))))))))
(st/emit! (msg/error (tr "errors.generic")))))))))
(s/def ::email ::us/email)
(s/def ::password ::us/not-empty-string)
@ -157,12 +157,12 @@
[:*
(when-let [message @error]
[:& msgs/inline-banner
{:type :warning
:content message
:on-close #(reset! error nil)
:data-test "login-banner"
:role "alert"}])
[:div {:class (stl/css :error-wrapper)}
[:& context-notification
{:type :warning
:content message
:data-test "login-banner"
:role "alert"}]])
[:& fm/form {:on-submit on-submit :form form}
[:div {:class (stl/css :fields-row)}

View file

@ -8,7 +8,7 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.spec :as us]
[app.main.data.messages :as dm]
[app.main.data.messages :as msg]
[app.main.data.users :as du]
[app.main.store :as st]
[app.main.ui.components.forms :as fm]
@ -39,11 +39,11 @@
(defn- on-error
[_form _error]
(st/emit! (dm/error (tr "auth.notifications.invalid-token-error"))))
(st/emit! (msg/error (tr "auth.notifications.invalid-token-error"))))
(defn- on-success
[_]
(st/emit! (dm/info (tr "auth.notifications.password-changed-successfully"))
(st/emit! (msg/info (tr "auth.notifications.password-changed-successfully"))
(rt/nav :auth-login)))
(defn- on-submit

View file

@ -9,7 +9,7 @@
(:require
[app.common.data :as d]
[app.common.spec :as us]
[app.main.data.messages :as dm]
[app.main.data.messages :as msg]
[app.main.data.users :as du]
[app.main.store :as st]
[app.main.ui.components.forms :as fm]
@ -37,7 +37,7 @@
:initial {})
submitted (mf/use-state false)
default-success-finish #(st/emit! (dm/info (tr "auth.notifications.recovery-token-sent")))
default-success-finish #(st/emit! (msg/info (tr "auth.notifications.recovery-token-sent")))
on-success
(mf/use-callback
@ -53,13 +53,13 @@
(reset! submitted false)
(case code
:profile-not-verified
(rx/of (dm/error (tr "auth.notifications.profile-not-verified") {:timeout nil}))
(rx/of (msg/error (tr "auth.notifications.profile-not-verified")))
:profile-is-muted
(rx/of (dm/error (tr "errors.profile-is-muted")))
(rx/of (msg/error (tr "errors.profile-is-muted")))
:email-has-permanent-bounces
(rx/of (dm/error (tr "errors.email-has-permanent-bounces" (:email data))))
(rx/of (msg/error (tr "errors.email-has-permanent-bounces" (:email data))))
(rx/throw error))))

View file

@ -10,7 +10,7 @@
[app.common.data :as d]
[app.common.spec :as us]
[app.config :as cf]
[app.main.data.messages :as dm]
[app.main.data.messages :as msg]
[app.main.data.users :as du]
[app.main.repo :as rp]
[app.main.store :as st]
@ -18,7 +18,7 @@
[app.main.ui.components.forms :as fm]
[app.main.ui.components.link :as lk]
[app.main.ui.icons :as i]
[app.main.ui.messages :as msgs]
[app.main.ui.notifications.context-notification :refer [context-notification]]
[app.util.i18n :refer [tr tr-html]]
[app.util.router :as rt]
[beicon.v2.core :as rx]
@ -28,7 +28,7 @@
(mf/defc demo-warning
[_]
[:div {:class (stl/css :banner)}
[:& msgs/inline-banner
[:& context-notification
{:type :warning
:content (tr "auth.demo-warning")}]])
@ -61,14 +61,14 @@
[form {:keys [type code] :as cause}]
(condp = [type code]
[:restriction :registration-disabled]
(st/emit! (dm/error (tr "errors.registration-disabled")))
(st/emit! (msg/error (tr "errors.registration-disabled")))
[:restriction :profile-blocked]
(st/emit! (dm/error (tr "errors.profile-blocked")))
(st/emit! (msg/error (tr "errors.profile-blocked")))
[:validation :email-has-permanent-bounces]
(let [email (get @form [:data :email])]
(st/emit! (dm/error (tr "errors.email-has-permanent-bounces" email))))
(st/emit! (msg/error (tr "errors.email-has-permanent-bounces" email))))
[:validation :email-already-exists]
(swap! form assoc-in [:errors :email]
@ -78,7 +78,7 @@
(swap! form assoc-in [:errors :password]
{:message "errors.email-as-password"})
(st/emit! (dm/error (tr "errors.generic")))))
(st/emit! (msg/error (tr "errors.generic")))))
(defn- handle-prepare-register-success
[params]
@ -182,7 +182,7 @@
(do
(println (:explain error))
(st/emit! (dm/error (tr "errors.generic"))))))
(st/emit! (msg/error (tr "errors.generic"))))))
(defn- handle-register-success
[data]

View file

@ -7,7 +7,7 @@
(ns app.main.ui.auth.verify-token
(:require-macros [app.main.style :as stl])
(:require
[app.main.data.messages :as dm]
[app.main.data.messages :as msg]
[app.main.data.users :as du]
[app.main.repo :as rp]
[app.main.store :as st]
@ -25,13 +25,13 @@
(defmethod handle-token :verify-email
[data]
(let [msg (tr "dashboard.notifications.email-verified-successfully")]
(ts/schedule 1000 #(st/emit! (dm/success msg)))
(ts/schedule 1000 #(st/emit! (msg/success msg)))
(st/emit! (du/login-from-token data))))
(defmethod handle-token :change-email
[_data]
(let [msg (tr "dashboard.notifications.email-changed-successfully")]
(ts/schedule 100 #(st/emit! (dm/success msg)))
(ts/schedule 100 #(st/emit! (msg/success msg)))
(st/emit! (rt/nav :settings-profile)
(du/fetch-profile))))
@ -44,7 +44,7 @@
(case (:state tdata)
:created
(st/emit!
(dm/success (tr "auth.notifications.team-invitation-accepted"))
(msg/success (tr "auth.notifications.team-invitation-accepted"))
(du/fetch-profile)
(rt/nav :dashboard-projects {:team-id (:team-id tdata)}))
@ -57,7 +57,7 @@
[_tdata]
(st/emit!
(rt/nav :auth-login)
(dm/warn (tr "errors.unexpected-token"))))
(msg/warn (tr "errors.unexpected-token"))))
(mf/defc verify-token
[{:keys [route] :as props}]
@ -79,17 +79,17 @@
(= :email-already-exists code)
(let [msg (tr "errors.email-already-exists")]
(ts/schedule 100 #(st/emit! (dm/error msg)))
(ts/schedule 100 #(st/emit! (msg/error msg)))
(st/emit! (rt/nav :auth-login)))
(= :email-already-validated code)
(let [msg (tr "errors.email-already-validated")]
(ts/schedule 100 #(st/emit! (dm/warn msg)))
(ts/schedule 100 #(st/emit! (msg/warn msg)))
(st/emit! (rt/nav :auth-login)))
:else
(let [msg (tr "errors.generic")]
(ts/schedule 100 #(st/emit! (dm/error msg)))
(ts/schedule 100 #(st/emit! (msg/error msg)))
(st/emit! (rt/nav :auth-login))))))))
(if @bad-token

View file

@ -296,19 +296,13 @@
(mf/defc submit-button*
{::mf/wrap-props false}
[{:keys [on-click children label form class name disabled large?] :as props}]
[{:keys [on-click children label form class name disabled] :as props}]
(let [form (or form (mf/use-ctx form-ctx))
disabled? (or (and (some? form) (not (:valid @form)))
(true? disabled))
large? (d/nilv large? true)
class (dm/str (d/nilv class "btn-primary")
" "
(if large? "btn-large" "")
" "
(if disabled? (stl/css :btn-disabled) ""))
class (d/nilv class (stl/css :button-submit))
name (d/nilv name "submit")

View file

@ -257,7 +257,11 @@
}
// SUBMIT-BUTTON
.btn-disabled {
.button-submit {
@extend .button-primary;
}
:disabled {
@extend .button-disabled;
}
@ -329,10 +333,10 @@
&.invalid {
background-color: var(--status-widget-background-color-error);
.text {
color: var(--alert-foreground-color-error);
color: var(--alert-text-foreground-color-error);
}
.icon svg {
stroke: var(--alert-foreground-color-error);
stroke: var(--alert-icon-foreground-color-error);
}
}
}
@ -422,6 +426,7 @@
fill: none;
}
}
//TEXTAREA
.textarea-label {

View file

@ -18,7 +18,7 @@
(mf/defc radio-button
{::mf/props :obj}
[{:keys [icon id value disabled title unique-key icon-class type]}]
[{:keys [icon id value disabled title icon-class type]}]
(let [context (mf/use-ctx context)
allow-empty (unchecked-get context "allow-empty")
type (if ^boolean type
@ -39,7 +39,6 @@
[:label {:html-for id
:title title
:key unique-key
:class (stl/css-case
:radio-icon true
:checked checked?
@ -88,9 +87,10 @@
(dom/blur! input))))
context-value
(mf/spread-obj props {:on-change on-change'
:encode-fn encode-fn
:decode-fn decode-fn})]
(mf/spread props
:on-change on-change'
:encode-fn encode-fn
:decode-fn decode-fn)]
[:& (mf/provider context) {:value context-value}
[:div {:class (dm/str class " " (stl/css :radio-btn-wrapper))

View file

@ -9,7 +9,7 @@
[app.main.data.common :as dcm]
[app.main.data.dashboard :as dd]
[app.main.data.events :as ev]
[app.main.data.messages :as dm]
[app.main.data.messages :as msg]
[app.main.data.modal :as modal]
[app.main.repo :as rp]
[app.main.store :as st]
@ -88,12 +88,12 @@
on-duplicate
(fn [_]
(apply st/emit! (map dd/duplicate-file files))
(st/emit! (dm/success (tr "dashboard.success-duplicate-file" (i18n/c (count files))))))
(st/emit! (msg/success (tr "dashboard.success-duplicate-file" (i18n/c (count files))))))
on-delete-accept
(fn [_]
(apply st/emit! (map dd/delete-file files))
(st/emit! (dm/success (tr "dashboard.success-delete-file" (i18n/c (count files))))
(st/emit! (msg/success (tr "dashboard.success-delete-file" (i18n/c (count files))))
(dd/clear-selected-files)))
on-delete
@ -126,21 +126,36 @@
on-move-success
(fn [team-id project-id]
(if multi?
(st/emit! (dm/success (tr "dashboard.success-move-files")))
(st/emit! (dm/success (tr "dashboard.success-move-file"))))
(st/emit! (msg/success (tr "dashboard.success-move-files")))
(st/emit! (msg/success (tr "dashboard.success-move-file"))))
(if (or navigate? (not= team-id current-team-id))
(st/emit! (dd/go-to-files team-id project-id))
(st/emit! (dd/fetch-recent-files team-id)
(dd/clear-selected-files))))
on-move-accept
(fn [params team-id project-id]
(st/emit! (dd/move-files
(with-meta params
{:on-success #(on-move-success team-id project-id)}))))
on-move
(fn [team-id project-id]
(let [params {:ids (into #{} (map :id) files)
:project-id project-id}]
(fn []
(st/emit! (dd/move-files
(with-meta params
{:on-success #(on-move-success team-id project-id)}))))))
(let [num-shared (filter #(:is-shared %) files)]
(if (and (< 0 (count num-shared))
(not= team-id current-team-id))
(st/emit! (modal/show
{:type :delete-shared-libraries
:origin :move
:ids (into #{} (map :id) files)
:on-accept #(on-move-accept params team-id project-id)
:count-libraries (count num-shared)}))
(on-move-accept params team-id project-id))))))
add-shared
#(st/emit! (dd/set-file-shared (assoc file :is-shared true)))

View file

@ -14,10 +14,10 @@
[app.main.refs :as refs]
[app.main.repo :as rp]
[app.main.store :as st]
[app.main.ui.components.context-menu-a11y :refer [context-menu-a11y]]
[app.main.ui.components.file-uploader :refer [file-uploader]]
[app.main.ui.icons :as i]
[app.main.ui.notifications.context-notification :refer [context-notification]]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd]
@ -131,18 +131,14 @@
:ref input-ref
:on-selected handle-selected}]]
[:div {:class (stl/css :banner)}
[:div {:class (stl/css :icon)} i/msg-neutral-refactor]
[:div {:class (stl/css :content)}
[:& i18n/tr-html {:tag-name "span"
:label "dashboard.fonts.hero-text2"}]]]
[:& context-notification {:content (tr "dashboard.fonts.hero-text2")
:type :default
:is-html true}]
(when problematic-fonts?
[:div {:class (stl/css :banner :warning)}
[:div {:class (stl/css :icon)} i/msg-warning-refactor]
[:div {:class (stl/css :content)}
[:& i18n/tr-html {:tag-name "span"
:label "dashboard.fonts.warning-text"}]]])]]
[:& context-notification {:content (tr "dashboard.fonts.warning-text")
:type :warning
:is-html true}])]]
[:*
(when (some? (vals fonts))
@ -176,7 +172,7 @@
[:div {:class (stl/css :table-field :options)}
(when (:height-warning? item)
[:span {:class (stl/css :icon :failure)} i/msg-warning-refactor])
[:span {:class (stl/css :icon :failure)} i/msg-neutral-refactor])
[:button {:on-click #(on-upload item)
:class (stl/css-case :btn-primary true

View file

@ -255,47 +255,6 @@
}
}
.banner {
overflow: hidden;
display: grid;
grid-template-columns: $s-40 1fr;
background-color: $db-primary;
border-radius: $br-12;
border: $s-1 solid $db-quaternary;
color: $df-primary;
font-size: $fs-12;
&:not(:last-child) {
margin-bottom: $s-12;
}
.icon {
display: flex;
align-items: flex-start;
justify-content: center;
padding-top: $s-12;
svg {
stroke: $df-secondary;
fill: none;
height: $s-20;
width: $s-20;
}
}
.content {
margin: $s-12;
a {
color: $da-primary;
}
}
&.warning {
background-color: $db-quaternary;
.icon svg {
stroke: var(--element-foreground-warning);
}
}
}
.btn-primary {
flex-shrink: 0;
}

View file

@ -19,6 +19,7 @@
[app.main.store :as st]
[app.main.ui.components.file-uploader :refer [file-uploader]]
[app.main.ui.icons :as i]
[app.main.ui.notifications.context-notification :refer [context-notification]]
[app.main.worker :as uw]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
@ -385,14 +386,12 @@
(when (and (= :importing (:status @state)) (not pending-import?))
(if (> warning-files 0)
[:div {:class (stl/css-case :feedback-banner true
:warning true)}
[:div {:class (stl/css :icon)} i/msg-warning-refactor]
[:div {:class (stl/css :message)} (tr "dashboard.import.import-warning" warning-files success-files)]]
[:div {:class (stl/css :feedback-banner)}
[:div {:class (stl/css :icon)} i/msg-success-refactor]
[:div {:class (stl/css :message)} (tr "dashboard.import.import-message" (i18n/c (if (some? template) 1 success-files)))]]))
[:& context-notification
{:type :warning
:content (tr "dashboard.import.import-warning" warning-files success-files)}]
[:& context-notification
{:type :success
:content (tr "dashboard.import.import-message" (i18n/c (if (some? template) 1 success-files)))}]))
(for [file files]
(let [editing? (and (some? (:file-id file))

View file

@ -29,39 +29,12 @@
.modal-content {
@include bodyMedTipography;
display: grid;
grid-template-columns: 1fr;
gap: $s-16;
margin-bottom: $s-24;
}
.feedback-banner {
@include flexRow;
height: $s-32;
width: 100%;
margin-bottom: $s-24;
border-radius: $br-8;
background-color: var(--alert-background-color-success);
color: var(--alert-foreground-color-success);
.icon {
@include flexCenter;
height: $s-24;
width: $s-24;
svg {
@extend .button-icon;
stroke: var(--alert-foreground-color-success);
}
}
.message {
@include bodyMedTipography;
}
&.warning {
background-color: var(--alert-background-color-warning);
color: var(--alert-foreground-color-warning);
.icon svg {
stroke: var(--alert-foreground-color-warning);
}
}
}
.action-buttons {
@extend .modal-action-btns;
}

View file

@ -608,7 +608,7 @@
height: $s-24;
svg {
@extend .button-icon;
stroke: var(--alert-foreground-color-error);
stroke: var(--alert-icon-foreground-color-error);
}
}
.message {
@ -632,7 +632,7 @@
height: $s-24;
svg {
@extend .button-icon;
stroke: var(--alert-foreground-color-warning);
stroke: var(--alert-icon-foreground-color-warning);
}
}
.message {

View file

@ -9,7 +9,7 @@
(:require
[app.common.spec :as us]
[app.main.data.dashboard :as dd]
[app.main.data.messages :as dm]
[app.main.data.messages :as msg]
[app.main.data.modal :as modal]
[app.main.store :as st]
[app.main.ui.components.forms :as fm]
@ -29,22 +29,22 @@
(defn- on-create-success
[_form response]
(let [msg "Team created successfully"]
(st/emit! (dm/success msg)
(st/emit! (msg/success msg)
(modal/hide)
(rt/nav :dashboard-projects {:team-id (:id response)}))))
(defn- on-update-success
[_form _response]
(let [msg "Team created successfully"]
(st/emit! (dm/success msg)
(st/emit! (msg/success msg)
(modal/hide))))
(defn- on-error
[form _response]
(let [id (get-in @form [:clean-data :id])]
(if id
(rx/of (dm/error "Error on updating team."))
(rx/of (dm/error "Error on creating team.")))))
(rx/of (msg/error "Error on updating team."))
(rx/of (msg/error "Error on creating team.")))))
(defn- on-create-submit
[form]
@ -117,6 +117,6 @@
{:label (if team
(tr "labels.update-team")
(tr "labels.create-team"))
:className (stl/css :accept-btn)}]]]]]]))
:class (stl/css :accept-btn)}]]]]]]))

View file

@ -35,28 +35,26 @@
cancel-label (tr "labels.cancel")
accept-style (or accept-style :danger)
is-delete? (= origin :delete)
count-files (count (keys references))
title (if ^boolean is-delete?
(tr "modals.delete-shared-confirm.title" (i18n/c count-libraries))
(tr "modals.unpublish-shared-confirm.title" (i18n/c count-libraries)))
title (case origin
:delete (tr "modals.delete-shared-confirm.title" (i18n/c count-libraries))
:unpublish (tr "modals.unpublish-shared-confirm.title" (i18n/c count-libraries))
:move (tr "modals.move-shared-confirm.title" (i18n/c count-libraries)))
subtitle (if ^boolean is-delete?
(tr "modals.delete-shared-confirm.message" (i18n/c count-libraries))
(tr "modals.unpublish-shared-confirm.message" (i18n/c count-libraries)))
subtitle (case origin
:delete (tr "modals.delete-shared-confirm.message" (i18n/c count-libraries))
:unpublish (tr "modals.unpublish-shared-confirm.message" (i18n/c count-libraries))
:move (tr "modals.move-shared-confirm.message" (i18n/c count-libraries)))
accept-label (if ^boolean is-delete?
(tr "modals.delete-shared-confirm.accept" (i18n/c count-libraries))
(tr "modals.unpublish-shared-confirm.accept" (i18n/c count-libraries)))
accept-label (case origin
:delete (tr "modals.delete-shared-confirm.accept" (i18n/c count-libraries))
:unpublish (tr "modals.unpublish-shared-confirm.accept" (i18n/c count-libraries))
:move (tr "modals.move-shared-confirm.accept" (i18n/c count-libraries)))
no-files-msg (if ^boolean is-delete?
(tr "modals.delete-shared-confirm.activated.no-files-message" (i18n/c count-libraries))
(tr "modals.unpublish-shared-confirm.activated.no-files-message" (i18n/c count-libraries)))
no-files-msg (tr "modals.delete-shared-confirm.activated.no-files-message" (i18n/c count-libraries))
scd-msg (if ^boolean is-delete?
(tr "modals.delete-shared-confirm.activated.scd-message" (i18n/c count-libraries))
(tr "modals.unpublish-shared-confirm.activated.scd-message" (i18n/c count-libraries)))
scd-msg (tr "modals.delete-shared-confirm.activated.scd-message" (i18n/c count-libraries))
hint (tr "modals.delete-unpublish-shared-confirm.activated.hint" (i18n/c count-files))

View file

@ -5,93 +5,43 @@
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.messages
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.uuid :as uuid]
[app.main.data.messages :as dmsg]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.link-button :as lb]
[app.main.ui.icons :as i]
[app.main.ui.notifications.context-notification :refer [context-notification]]
[app.main.ui.notifications.inline-notification :refer [inline-notification]]
[app.main.ui.notifications.toast-notification :refer [toast-notification]]
[rumext.v2 :as mf]))
(mf/defc banner
[{:keys [type position status controls content links actions on-close data-test role] :as props}]
[:div {:class (stl/css-case :banner true
:warning (= type :warning)
:error (= type :error)
:success (= type :success)
:info (= type :info)
:fixed (= position :fixed)
:floating (= position :floating)
:inline (= position :inline)
:hide (= status :hide))}
[:div {:class (stl/css :wrapper)}
[:div {:class (stl/css :icon)}
(case type
:warning i/msg-warning-refactor
:error i/msg-error-refactor
:success i/msg-success-refactor
:info i/msg-neutral-refactor
i/msg-error-refactor)]
[:div {:class (stl/css-case :content true
:inline-actions (= controls :inline-actions)
:bottom-actions (= controls :bottom-actions))
:data-test data-test
:role role}
[:span {:class (stl/css :text)}
content
(for [[index link] (d/enumerate links)]
[:* {:key (dm/str "link-" index)}
" " [:& lb/link-button {:class (stl/css :link)
:on-click (:callback link)
:value (:label link)}]])]
(when (or (= controls :bottom-actions) (= controls :inline-actions))
[:div {:class (stl/css :actions)}
(for [action actions]
[:button {:key (uuid/next)
:class (stl/css-case :action-btn true
:primary (= :primary (:type action))
:secondary (= :secondary (:type action))
:danger (= :danger (:type action)))
:on-click (:callback action)}
(:label action)])])]
(when (= controls :close)
[:button {:class (stl/css :btn-close)
:on-click on-close} i/close-refactor])]])
(mf/defc notifications
(mf/defc notifications-hub
[]
(let [message (mf/deref refs/message)
on-close #(st/emit! dmsg/hide)]
on-close #(st/emit! dmsg/hide)
toast-message {:type (or (:type message) :info)
:links (:links message)
:on-close on-close
:content (:content message)}
inline-message {:actions (:actions message)
:links (:links message)
:content (:content message)}
context-message {:actions (:actions message)
:links (:links message)
:content (:content message)}
;; TODO review this options
is-toast-msg (or (= :toast (:notification-type message)) (some? (:timeout message)))
is-inline-msg (or (= :inline (:notification-type message)) (and (some? (:position message)) (= :floating (:position message))))]
(when message
[:& banner (assoc message
:position (or (:position message) :fixed)
:controls (if (some? (:controls message))
(:controls message)
:close)
:on-close on-close)])))
(mf/defc inline-banner
{::mf/wrap [mf/memo]}
[{:keys [type content on-close actions data-test role] :as props}]
[:& banner {:type type
:position :inline
:status :visible
:controls (if (some? on-close)
:close
(if (some? actions)
:bottom-actions
:none))
:content content
:on-close on-close
:actions actions
:data-test data-test
:role role}])
(cond
is-toast-msg
[:& toast-notification toast-message]
is-inline-msg
[:& inline-notification inline-message]
:else
[:& context-notification context-message]))))

View file

@ -1,150 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
@import "refactor/common-refactor.scss";
.banner {
--bg-color: var(--alert-background-color-error);
--fg-color: var(--alert-foreground-color-error);
position: relative;
display: flex;
align-items: center;
border-radius: $br-8;
background-color: var(--bg-color);
color: var(--fg-color);
}
.warning {
--bg-color: var(--alert-background-color-warning);
--fg-color: var(--alert-foreground-color-warning);
}
.success {
--bg-color: var(--alert-background-color-success);
--fg-color: var(--alert-foreground-color-success);
}
.info {
--bg-color: var(--alert-background-color-neutral);
--fg-color: var(--alert-foreground-color-neutral-active);
}
.banner.info .icon {
--fg-color: var(--alert-foreground-color-neutral);
}
.banner.info:hover .icon {
--fg-color: var(--alert-foreground-color-neutral);
}
.wrapper {
display: flex;
align-items: center;
padding: $s-8 $s-8 $s-8 $s-16;
gap: $s-8;
height: 100%;
width: 100%;
}
.icon {
@include flexCenter;
height: 100%;
width: $s-16;
svg {
@extend .button-icon;
stroke: var(--fg-color);
}
}
.fixed {
@include alertShadow;
position: fixed;
top: $s-16;
right: $s-16;
display: flex;
align-items: center;
height: $s-48;
min-width: $s-500;
max-width: calc(10 * $s-100);
padding-left: $s-16;
z-index: $z-index-alert;
}
.floating {
@include alertShadow;
position: absolute;
min-height: $s-32;
top: $s-72;
left: 0;
right: 0;
width: $s-640;
margin-left: auto;
margin-right: auto;
z-index: $z-index-modal;
}
.inline {
min-height: $s-40;
width: 100%;
}
.hide {
display: none;
}
.content {
@include flexRow;
gap: $s-8;
flex-grow: 1;
}
.text {
@include bodyMedTipography;
flex-grow: 1;
}
.link {
@include bodyMedTipography;
color: var(--modal-link-foreground-color);
margin: 0;
}
.actions {
@include flexRow;
gap: $s-8;
}
.action-btn {
@extend .button-tertiary;
@include uppercaseTitleTipography;
min-height: $s-32;
min-width: $s-32;
svg {
@extend .button-icon-small;
}
&.primary {
@extend .button-primary;
padding: $s-8 $s-24;
}
&.secondary {
@extend .button-secondary;
padding: $s-8 $s-24;
}
&.danger {
@extend .modal-danger-btn;
padding: $s-8 $s-24;
}
}
.btn-close {
@extend .button-tertiary;
height: $s-32;
width: $s-32;
svg {
@extend .button-icon-small;
stroke: var(--icon-foreground);
}
}

View file

@ -0,0 +1,69 @@
;; 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.notifications.context-notification
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.main.ui.components.link-button :as lb]
[app.main.ui.icons :as i]
[rumext.v2 :as mf]))
(def ^:private neutral-icon
(i/icon-xref :msg-neutral-refactor (stl/css :icon)))
(def ^:private error-icon
(i/icon-xref :delete-text-refactor (stl/css :icon)))
(def ^:private success-icon
(i/icon-xref :status-tick-refactor (stl/css :icon)))
(def ^:private info-icon
(i/icon-xref :help-refactor (stl/css :icon)))
(defn get-icon-by-type
[type]
(case type
:warning neutral-icon
:error error-icon
:success success-icon
:info info-icon
neutral-icon))
(mf/defc context-notification
"They are persistent, informative and non-actionable.
They are contextual messages in specific areas off the app"
{::mf/props :obj}
[{:keys [type content links is-html] :as props}]
[:aside {:class (stl/css-case :context-notification true
:contain-html is-html
:warning (= type :warning)
:error (= type :error)
:success (= type :success)
:info (= type :info))}
(get-icon-by-type type)
;; The content can arrive in markdown format, in these cases
;; we will use the prop is-html to true to indicate it and
;; that the html injection is performed and the necessary css classes are applied.
[:div {:class (stl/css :context-text)
:dangerouslySetInnerHTML (when is-html #js {:__html content})}
(when-not is-html
content)]
(when (some? links)
[:nav {:class (stl/css :link-nav)}
(for [[index link] (d/enumerate links)]
;; TODO Review this component
[:& lb/link-button {:class (stl/css :link)
:on-click (:callback link)
:value (:label link)
:key (dm/str "link-" index)}])])])

View file

@ -0,0 +1,90 @@
// 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";
.context-notification {
---context-notification-bg-color: var(--alert-background-color-default);
--context-notification-fg-color: var(--alert-text-foreground-color-default);
--context-notification-icon-color: var(--alert-icon-foreground-color-default);
--context-notification-border-color: var(--alert-border-color-default);
display: grid;
grid-template-columns: $s-16 auto 1fr;
gap: $s-8;
min-height: $s-32;
width: 100%;
padding: $s-8 $s-8 $s-8 $s-16;
border: $s-1 solid var(--context-notification-border-color);
border-radius: $br-8;
background-color: var(--context-notification-bg-color);
}
.warning {
--context-notification-bg-color: var(--alert-background-color-warning);
--context-notification-fg-color: var(--alert-text-foreground-color-warning);
--context-notification-icon-color: var(--alert-icon-foreground-color-warning);
--context-notification-border-color: var(--alert-border-color-warning);
}
.success {
--context-notification-bg-color: var(--alert-background-color-success);
--context-notification-fg-color: var(--alert-text-foreground-color-success);
--context-notification-icon-color: var(--alert-icon-foreground-color-success);
--context-notification-border-color: var(--alert-border-color-success);
}
.info {
--context-notification-bg-color: var(--alert-background-color-info);
--context-notification-fg-color: var(--alert-text-foreground-color-info);
--context-notification-icon-color: var(--alert-icon-foreground-color-info);
--context-notification-border-color: var(--alert-border-color-info);
}
.default {
--context-notification-bg-color: var(--alert-background-color-default);
--context-notification-fg-color: var(--alert-text-foreground-color-default);
--context-notification-icon-color: var(--alert-icon-foreground-color-default);
--context-notification-border-color: var(--alert-border-color-default);
}
.error {
--context-notification-bg-color: var(--alert-background-color-error);
--context-notification-fg-color: var(--alert-text-foreground-color-error);
--context-notification-icon-color: var(--alert-icon-foreground-color-error);
--context-notification-border-color: var(--alert-border-color-error);
}
.icon {
@extend .button-icon;
align-self: self-start;
stroke: var(--context-notification-icon-color);
}
.link-nav {
align-self: center;
height: $s-24;
margin: 0;
}
.context-text {
@include bodyMedTipography;
align-self: center;
color: var(--context-notification-fg-color);
margin: auto 0;
}
// The second rule only applies when the element receives embedded
// links within the content by means of the dangerouslySetInnerHTML.
// Only in this case the contain-html class will be used.
.link,
.contain-html .context-text a {
@include bodyMedTipography;
align-self: center;
height: $s-16;
margin: 0;
color: var(--modal-link-foreground-color);
}

View file

@ -0,0 +1,49 @@
;; 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.notifications.inline-notification
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.uuid :as uuid]
[app.main.ui.components.link-button :as lb]
[app.main.ui.icons :as i]
[rumext.v2 :as mf]))
(def ^:private neutral-icon
(i/icon-xref :msg-neutral-refactor (stl/css :icon)))
(mf/defc inline-notification
"They are persistent messages and report a special situation
of the application and require user interaction to disappear."
{::mf/props :obj}
[{:keys [content actions links] :as props}]
[:aside {:class (stl/css :inline-notification)}
neutral-icon
[:div {:class (stl/css :inline-text)}
content]
(when (some? links)
[:nav {:class (stl/css :link-nav)}
(for [[index link] (d/enumerate links)]
[:& lb/link-button {:key (dm/str "link-" index)
:class (stl/css :link)
:on-click (:callback link)
:value (:label link)}])])
[:div {:class (stl/css :actions)}
(for [action actions]
[:button {:key (uuid/next)
:class (stl/css-case :action-btn true
:primary (= :primary (:type action))
:secondary (= :secondary (:type action))
:danger (= :danger (:type action)))
:on-click (:callback action)}
(:label action)])]])

View file

@ -0,0 +1,79 @@
// 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";
.inline-notification {
--inline-notification-bg-color: var(--alert-background-color-default);
--inline-notification-fg-color: var(--alert-text-foreground-color-default);
--inline-notification-icon-color: var(--alert-icon-foreground-color-default);
--inline-notification-border-color: var(--alert-border-color-default);
@include alertShadow;
position: absolute;
top: $s-72;
left: 0;
right: 0;
display: grid;
grid-template-columns: $s-16 auto 1fr auto;
gap: $s-8;
min-height: $s-48;
min-width: $s-640;
max-width: $s-712;
padding: $s-8 $s-8 $s-8 $s-16;
margin-inline: auto;
border: $s-1 solid var(--inline-notification-border-color);
border-radius: $br-8;
z-index: $z-index-modal;
background-color: var(--inline-notification-bg-color);
color: var(--inline-notification-fg-color);
}
.icon {
@extend .button-icon;
height: 100%;
stroke: var(--inline-notification-icon-color);
}
.inline-text {
@include bodyMedTipography;
align-self: center;
}
.link {
@include bodyMedTipography;
margin: 0;
height: 100%;
color: var(--modal-link-foreground-color);
}
.actions {
display: grid;
grid-template-columns: none;
grid-auto-flow: column;
gap: $s-8;
align-self: center;
}
.action-btn {
@extend .button-tertiary;
@include uppercaseTitleTipography;
min-height: $s-32;
min-width: $s-32;
padding: $s-8 $s-24;
border: $s-1 solid transparent;
}
.action-btn.primary {
@extend .button-primary;
}
.action-btn.secondary {
@extend .button-secondary;
}
.action-btn.danger {
@extend .modal-danger-btn;
}

View file

@ -0,0 +1,73 @@
;; 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.notifications.toast-notification
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.main.ui.components.link-button :as lb]
[app.main.ui.icons :as i]
[rumext.v2 :as mf]))
(def ^:private neutral-icon
(i/icon-xref :msg-neutral-refactor (stl/css :icon)))
(def ^:private error-icon
(i/icon-xref :delete-text-refactor (stl/css :icon)))
(def ^:private success-icon
(i/icon-xref :status-tick-refactor (stl/css :icon)))
(def ^:private info-icon
(i/icon-xref :help-refactor (stl/css :icon)))
(def ^:private close-icon
(i/icon-xref :close-refactor (stl/css :close-icon)))
(defn get-icon-by-type
[type]
(case type
:warning neutral-icon
:error error-icon
:success success-icon
:info info-icon
neutral-icon))
(mf/defc toast-notification
"These are ephemeral elements that disappear when
the close button is pressed,
the page is refreshed,
the page is navigated to another page or
after 7 seconds, which is enough time to be read,
except for error messages that require user interaction."
{::mf/props :obj}
[{:keys [type content on-close links] :as props}]
[:aside {:class (stl/css-case :toast-notification true
:with-links (some? links)
:warning (= type :warning)
:error (= type :error)
:success (= type :success)
:info (= type :info))}
(get-icon-by-type type)
[:div {:class (stl/css :text)}
content]
(when (some? links)
[:nav {:class (stl/css :link-nav)}
(for [[index link] (d/enumerate links)]
[:& lb/link-button {:key (dm/str "link-" index)
:class (stl/css :link)
:on-click (:callback link)
:value (:label link)}])])
[:button {:class (stl/css :btn-close)
:on-click on-close}
close-icon]])

View file

@ -0,0 +1,103 @@
// 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";
.toast-notification {
--toast-notification-bg-color: var(--alert-background-color-default);
--toast-notification-fg-color: var(--alert-text-foreground-color-default);
--toast-notification-icon-color: var(--alert-icon-foreground-color-default);
--toast-notification-border-color: var(--alert-border-color-default);
@include alertShadow;
position: fixed;
top: $s-16;
right: $s-16;
display: grid;
grid-template-columns: $s-16 1fr auto;
gap: $s-8;
min-height: $s-32;
min-width: $s-500;
max-width: calc(10 * $s-100);
padding: $s-8 $s-8 $s-8 $s-16;
border: $s-1 solid var(--toast-notification-border-color);
background-color: var(--toast-notification-bg-color);
border-radius: $br-8;
color: var(--toast-notification-fg-color);
z-index: $z-index-alert;
}
.with-links {
grid-template-columns: $s-16 auto 1fr auto;
}
.warning {
--toast-notification-bg-color: var(--alert-background-color-warning);
--toast-notification-fg-color: var(--alert-text-foreground-color-warning);
--toast-notification-icon-color: var(--alert-icon-foreground-color-warning);
--toast-notification-border-color: var(--alert-border-color-warning);
}
.success {
--toast-notification-bg-color: var(--alert-background-color-success);
--toast-notification-fg-color: var(--alert-text-foreground-color-success);
--toast-notification-icon-color: var(--alert-icon-foreground-color-success);
--toast-notification-border-color: var(--alert-border-color-success);
}
.info {
--bg-color: var(--alert-background-color-info);
--fg-color: var(--alert-text-foreground-color-info);
--icon-color: var(--alert-icon-foreground-color-info);
--border-color: var(--alert-border-color-info);
}
.default {
--toast-notification-bg-color: var(--alert-background-color-default);
--toast-notification-fg-color: var(--alert-text-foreground-color-default);
--toast-notification-icon-color: var(--alert-icon-foreground-color-default);
--toast-notification-border-color: var(--alert-border-color-default);
}
.error {
--toast-notification-bg-color: var(--alert-background-color-error);
--toast-notification-fg-color: var(--alert-text-foreground-color-error);
--toast-notification-icon-color: var(--alert-icon-foreground-color-error);
--toast-notification-border-color: var(--alert-border-color-error);
}
.link-nav {
height: $s-24;
}
.link {
@include bodyMedTipography;
color: var(--modal-link-foreground-color);
margin: 0;
}
.icon {
@extend .button-icon;
height: 100%;
stroke: var(--toast-notification-icon-color);
}
.text {
@include bodyMedTipography;
align-self: center;
}
.btn-close {
@include buttonStyle;
@include flexCenter;
height: 100%;
min-width: $s-32;
background-color: transparent;
}
.close-icon {
@extend .button-icon;
stroke: var(--toast-notification-icon-color);
}

View file

@ -7,7 +7,7 @@
(ns app.main.ui.onboarding.newsletter
(:require-macros [app.main.style :as stl])
(:require
[app.main.data.messages :as dm]
[app.main.data.messages :as msg]
[app.main.data.modal :as modal]
[app.main.data.users :as du]
[app.main.store :as st]
@ -33,7 +33,7 @@
(mf/deps @newsletter-updates @newsletter-news)
(fn []
(st/emit! (when (or @newsletter-updates @newsletter-news)
(dm/success message))
(msg/success message))
(modal/show {:type :onboarding-team})
(du/update-profile-props {:newsletter-updates @newsletter-updates :newsletter-news @newsletter-news}))))]

View file

@ -11,7 +11,7 @@
[app.common.spec :as us]
[app.main.data.dashboard :as dd]
[app.main.data.events :as ev]
[app.main.data.messages :as dm]
[app.main.data.messages :as msg]
[app.main.data.modal :as modal]
[app.main.refs :as refs]
[app.main.store :as st]
@ -169,7 +169,7 @@
on-error
(mf/use-fn
(fn [_form _response]
(st/emit! (dm/error "Error on creating team."))))
(st/emit! (msg/error "Error on creating team."))))
;; The SKIP branch only creates the team, without invitations
on-invite-later

View file

@ -8,7 +8,7 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.spec :as us]
[app.main.data.messages :as dm]
[app.main.data.messages :as msg]
[app.main.data.modal :as modal]
[app.main.data.users :as du]
[app.main.store :as st]
@ -66,7 +66,7 @@
(fn [_]
(let [message (tr "dashboard.access-tokens.create.success")]
(st/emit! (du/fetch-access-tokens)
(dm/success message)
(msg/success message)
(reset! created? true)))))
on-close
@ -79,7 +79,7 @@
on-error
(mf/use-fn
(fn [_]
(st/emit! (dm/error (tr "errors.generic"))
(st/emit! (msg/error (tr "errors.generic"))
(modal/hide))))
on-submit
@ -101,9 +101,10 @@
(fn [event]
(dom/prevent-default event)
(wapi/write-to-clipboard (:token created))
(st/emit! (dm/show {:type :info
:content (tr "dashboard.access-tokens.copied-success")
:timeout 1000}))))]
(st/emit! (msg/show {:type :info
:notification-type :toast
:content (tr "dashboard.access-tokens.copied-success")
:timeout 7000}))))]
[:div {:class (stl/css :modal-overlay)}
[:div {:class (stl/css :modal-container)}

View file

@ -10,14 +10,14 @@
[app.common.data :as d]
[app.common.data.macros :as dma]
[app.common.spec :as us]
[app.main.data.messages :as dm]
[app.main.data.messages :as msg]
[app.main.data.modal :as modal]
[app.main.data.users :as du]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.forms :as fm]
[app.main.ui.icons :as i]
[app.main.ui.messages :as msgs]
[app.main.ui.notifications.context-notification :refer [context-notification]]
[app.util.i18n :as i18n :refer [tr]]
[beicon.v2.core :as rx]
[cljs.spec.alpha :as s]
@ -47,11 +47,11 @@
(assoc-in data [:errors :email-1] error))))
:profile-is-muted
(rx/of (dm/error (tr "errors.profile-is-muted")))
(rx/of (msg/error (tr "errors.profile-is-muted")))
:email-has-permanent-bounces
(let [email (get @form [:data :email-1])]
(rx/of (dm/error (tr "errors.email-has-permanent-bounces" email))))
(rx/of (msg/error (tr "errors.email-has-permanent-bounces" email))))
(rx/throw error)))
@ -61,7 +61,7 @@
(st/emit! (du/fetch-profile)
(modal/hide))
(let [message (tr "notifications.validation-email-sent" (:email profile))]
(st/emit! (dm/info message)
(st/emit! (msg/info message)
(modal/hide)))))
(defn- on-submit
@ -110,7 +110,7 @@
:on-click on-close} i/close-refactor]]
[:div {:class (stl/css :modal-content)}
[:& msgs/inline-banner
[:& context-notification
{:type :info
:content (tr "modals.change-email.info" (:email profile))}]

View file

@ -7,12 +7,12 @@
(ns app.main.ui.settings.delete-account
(:require-macros [app.main.style :as stl])
(:require
[app.main.data.messages :as dm]
[app.main.data.messages :as msg]
[app.main.data.modal :as modal]
[app.main.data.users :as du]
[app.main.store :as st]
[app.main.ui.icons :as i]
[app.main.ui.messages :as msgs]
[app.main.ui.notifications.context-notification :refer [context-notification]]
[app.util.i18n :as i18n :refer [tr]]
[beicon.v2.core :as rx]
[rumext.v2 :as mf]))
@ -21,7 +21,7 @@
[{:keys [code] :as error}]
(if (= :owner-teams-with-people code)
(let [msg (tr "notifications.profile-deletion-not-allowed")]
(rx/of (dm/error msg)))
(rx/of (msg/error msg)))
(rx/throw error)))
(mf/defc delete-account-modal
@ -47,7 +47,7 @@
:on-click on-close} i/close-refactor]]
[:div {:class (stl/css :modal-content)}
[:& msgs/inline-banner
[:& context-notification
{:type :warning
:content (tr "modals.delete-account.info")}]]

View file

@ -9,7 +9,7 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.spec :as us]
[app.main.data.messages :as dm]
[app.main.data.messages :as msg]
[app.main.refs :as refs]
[app.main.repo :as rp]
[app.main.store :as st]
@ -39,7 +39,7 @@
(mf/deps profile)
(fn [_]
(reset! loading false)
(st/emit! (dm/success (tr "labels.feedback-sent")))
(st/emit! (msg/success (tr "labels.feedback-sent")))
(swap! form assoc :data {} :touched {} :errors {})))
on-error
@ -48,8 +48,8 @@
(fn [{:keys [code] :as error}]
(reset! loading false)
(if (= code :feedback-disabled)
(st/emit! (dm/error (tr "labels.feedback-disabled")))
(st/emit! (dm/error (tr "errors.generic"))))))
(st/emit! (msg/error (tr "labels.feedback-disabled")))
(st/emit! (msg/error (tr "errors.generic"))))))
on-submit
(mf/use-callback

View file

@ -8,7 +8,7 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.spec :as us]
[app.main.data.messages :as dm]
[app.main.data.messages :as msg]
[app.main.data.users :as du]
[app.main.refs :as refs]
[app.main.store :as st]
@ -26,7 +26,7 @@
(defn- on-success
[_]
(st/emit! (dm/success (tr "notifications.profile-saved"))))
(st/emit! (msg/success (tr "notifications.profile-saved"))))
(defn- on-submit
[form _event]

View file

@ -8,7 +8,7 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.spec :as us]
[app.main.data.messages :as dm]
[app.main.data.messages :as msg]
[app.main.data.users :as udu]
[app.main.store :as st]
[app.main.ui.components.forms :as fm]
@ -28,7 +28,7 @@
{:message (tr "errors.email-as-password")})
(let [msg (tr "generic.error")]
(st/emit! (dm/error msg)))))
(st/emit! (msg/error msg)))))
(defn- on-success
[form]
@ -37,7 +37,7 @@
msg (tr "dashboard.notifications.password-saved")]
(dom/clean-value! password-old-node)
(dom/focus! password-old-node)
(st/emit! (dm/success msg))))
(st/emit! (msg/success msg))))
(defn- on-submit
[form event]

View file

@ -9,7 +9,7 @@
(:require
[app.common.spec :as us]
[app.config :as cf]
[app.main.data.messages :as dm]
[app.main.data.messages :as msg]
[app.main.data.modal :as modal]
[app.main.data.users :as du]
[app.main.refs :as refs]
@ -29,7 +29,7 @@
(defn- on-success
[_]
(st/emit! (dm/success (tr "notifications.profile-saved"))))
(st/emit! (msg/success (tr "notifications.profile-saved"))))
(defn- on-submit
[form _event]
@ -80,7 +80,7 @@
[:> fm/submit-button*
{:label (tr "dashboard.save-settings")
:disabled (empty? (:touched @form))
:className (stl/css :btn-primary)}]
:class (stl/css :btn-primary)}]
[:div {:class (stl/css :links)}
[:div {:class (stl/css :link-item)}

View file

@ -58,6 +58,7 @@
}
.viewer-section {
@extend .new-scrollbar;
grid-row: 1 / span 2;
grid-column: 1 / span 1;
display: flex;
@ -94,7 +95,7 @@
}
.viewer-go-next.comment-sidebar {
right: $s-264;
right: $s-280;
}
.viewer-go-prev {

View file

@ -8,6 +8,7 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.geom.rect :as grc]
@ -26,16 +27,20 @@
[rumext.v2 :as mf]))
(mf/defc comments-menu
{::mf/wrap [mf/memo]
::mf/wrap-props false}
{::mf/props :obj
::mf/memo true}
[]
(let [{cmode :mode cshow :show show-sidebar? :show-sidebar?} (mf/deref refs/comments-local)
(let [state (mf/deref refs/comments-local)
cmode (:mode state)
cshow (:show state)
show-sidebar? (:show-sidebar? state false)
show-dropdown? (mf/use-state false)
toggle-dropdown (mf/use-fn #(swap! show-dropdown? not))
hide-dropdown (mf/use-fn #(reset! show-dropdown? false))
update-mode
(mf/use-callback
(mf/use-fn
(fn [event]
(let [mode (-> (dom/get-current-target event)
(dom/get-data "value")
@ -43,78 +48,76 @@
(st/emit! (dcm/update-filters {:mode mode})))))
update-show
(mf/use-callback
(mf/use-fn
(fn [event]
(let [mode (-> (dom/get-current-target event)
(dom/get-data "value")
(d/read-string))]
(keyword))
mode (if (= :pending mode) :all :pending)]
(st/emit! (dcm/update-filters {:show mode})))))
update-options
(mf/use-callback
(mf/deps show-sidebar?)
(mf/use-fn
(fn [event]
(let [mode (-> (dom/get-target event)
(let [mode (-> (dom/get-current-target event)
(dom/get-data "value")
(boolean))]
(parse-boolean))]
(st/emit! (dcm/update-options {:show-sidebar? (not mode)})))))]
[:div {:class (stl/css :view-options)
:on-click toggle-dropdown}
[:span {:class (stl/css :dropdown-title)}
(tr "labels.comments")]
[:span {:class (stl/css :icon-dropdown)}
i/arrow-refactor]
[:span {:class (stl/css :dropdown-title)} (tr "labels.comments")]
[:span {:class (stl/css :icon-dropdown)} i/arrow-refactor]
[:& dropdown {:show @show-dropdown?
:on-close hide-dropdown}
[:ul {:class (stl/css :dropdown)}
[:li {:class (stl/css-case :dropdown-element true
:selected (or (= :all cmode) (nil? cmode)))
[:li {:class (stl/css-case
:dropdown-element true
:selected (or (= :all cmode) (nil? cmode)))
:data-value "all"
:on-click update-mode}
[:span {:class (stl/css :label)} (tr "labels.show-all-comments")]
(when (or (= :all cmode) (nil? cmode))
[:span {:class (stl/css :icon)} i/tick-refactor])]
[:li {:class (stl/css-case :dropdown-element true
:selected (= :yours cmode))
[:li {:class (stl/css-case
:dropdown-element true
:selected (= :yours cmode))
:data-value "yours"
:on-click update-mode}
[:span {:class (stl/css :label)}
(tr "labels.show-your-comments")]
[:span {:class (stl/css :label)} (tr "labels.show-your-comments")]
(when (= :yours cmode)
[:span {:class (stl/css :icon)}
i/tick-refactor])]
[:li {:class (stl/css :separator)}]
[:li {:class (stl/css-case :dropdown-element true
:selected (= :pending cshow))
:data-value (if (= :pending cshow) "all" "pending")
[:li {:class (stl/css-case
:dropdown-element true
:selected (= :pending cshow))
:data-value (d/name cshow)
:on-click update-show}
[:span {:class (stl/css :label)}
(tr "labels.hide-resolved-comments")]
[:span {:class (stl/css :label)} (tr "labels.hide-resolved-comments")]
(when (= :pending cshow)
[:span {:class (stl/css :icon)}
i/tick-refactor])]
[:li {:class (stl/css :separator)}]
[:li {:class (stl/css-case :dropdown-element true
:selected show-sidebar?)
:data-value (str show-sidebar?)
[:li {:class (stl/css-case
:dropdown-element true
:selected show-sidebar?)
:data-value (dm/str show-sidebar?)
:on-click update-options}
[:span {:class (stl/css :label)} (tr "labels.show-comments-list")]
(when show-sidebar?
[:span {:class (stl/css :icon)} i/tick-refactor])]]]]))
(defn- update-thread-position [positions {:keys [id] :as thread}]
(defn- update-thread-position
[positions {:keys [id] :as thread}]
(if-let [data (get positions id)]
(-> thread
(assoc :position (:position data))
@ -122,7 +125,8 @@
thread))
(mf/defc comments-layer
[{:keys [zoom file users frame page] :as props}]
{::mf/props :obj}
[{:keys [zoom file users frame page]}]
(let [profile (mf/deref refs/profile)
local (mf/deref refs/comments-local)

View file

@ -104,7 +104,7 @@
position: absolute;
right: 0;
top: $s-44;
width: $s-256;
width: $s-276;
height: calc(100vh - $s-48);
z-index: $z-index-10;
background-color: var(--panel-background-color);

View file

@ -34,7 +34,8 @@
(modal/show! :login-register {}))
(mf/defc zoom-widget
{::mf/wrap [mf/memo]}
{::mf/memo true
::mf/props :obj}
[{:keys [zoom
on-increase
on-decrease
@ -102,38 +103,38 @@
[:span {:class (stl/css :shortcuts)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-zoom-style))]
[:span {:class (stl/css :shortcut-key)
:key (str "zoom-fit-" sc)} sc])]]
:key (dm/str "zoom-fit-" sc)} sc])]]
[:li {:class (stl/css :zoom-option)
:on-click on-zoom-fill}
(tr "workspace.header.zoom-fill")
[:span {:class (stl/css :shortcuts)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-zoom-style))]
[:span {:class (stl/css :shortcut-key)
:key (str "zoom-fill-" sc)} sc])]]
:key (dm/str "zoom-fill-" sc)} sc])]]
[:li {:class (stl/css :zoom-option)
:on-click on-fullscreen}
(tr "workspace.header.zoom-full-screen")
[:span {:class (stl/css :shortcuts)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-fullscreen))]
[:span {:class (stl/css :shortcut-key)
:key (str "zoom-fullscreen-" sc)} sc])]]]]]))
:key (dm/str "zoom-fullscreen-" sc)} sc])]]]]]))
(mf/defc header-options
[{:keys [section zoom page file index permissions interactions-mode]}]
(let [fullscreen? (mf/deref fullscreen-ref)
toggle-fullscreen
(mf/use-callback
(mf/use-fn
(fn [] (st/emit! dv/toggle-fullscreen)))
go-to-workspace
(mf/use-callback
(mf/use-fn
(mf/deps page)
(fn []
(st/emit! (dv/go-to-workspace (:id page)))))
open-share-dialog
(mf/use-callback
(mf/use-fn
(mf/deps page)
(fn []
(modal/show! :share-link {:page page :file file})
@ -209,22 +210,22 @@
show-dropdown? (mf/use-state false)
toggle-thumbnails
(mf/use-callback
(mf/use-fn
(fn []
(st/emit! dv/toggle-thumbnails-panel)))
open-dropdown
(mf/use-callback
(mf/use-fn
(fn []
(reset! show-dropdown? true)))
close-dropdown
(mf/use-callback
(mf/use-fn
(fn []
(reset! show-dropdown? false)))
navigate-to
(mf/use-callback
(mf/use-fn
(fn [page-id]
(st/emit! (dv/go-to-page page-id))
(reset! show-dropdown? false)))]
@ -245,8 +246,8 @@
(for [id (get-in file [:data :pages])]
[:li {:class (stl/css-case :dropdown-element true
:selected (= page-id id))
:id (str id)
:key (str id)
:id (dm/str id)
:key (dm/str id)
:on-click (partial navigate-to id)}
[:span {:class (stl/css :label)}
(get-in file [:data :pages-index id :name])]

View file

@ -26,6 +26,8 @@
:align-content
:justify-items
:justify-content
:row-gap
:column-gap
:gap
:padding])

View file

@ -184,6 +184,7 @@
set-markup
(mf/use-callback
(mf/deps markup-type*)
(fn [value]
(reset! markup-type* value)))

View file

@ -20,6 +20,10 @@
}
}
.viewer-code {
padding: 0 $s-8;
}
.tool-windows {
height: 100%;
display: flex;

View file

@ -27,7 +27,7 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(mf/defc sidebar-options
[]
[{:keys [from-viewer]}]
(let [{cmode :mode cshow :show} (mf/deref refs/comments-local)
update-mode
(mf/use-fn
@ -44,7 +44,8 @@
(let [mode (if (= :pending cshow) :all :pending)]
(st/emit! (dcm/update-filters {:show mode})))))]
[:ul {:class (stl/css :comment-mode-dropdown)}
[:ul {:class (stl/css-case :comment-mode-dropdown true
:viewer-dropdown from-viewer)}
[:li {:class (stl/css-case :dropdown-item true
:selected (or (= :all cmode) (nil? cmode)))
:data-value "all"
@ -115,8 +116,10 @@
(dwcm/center-to-comment-thread thread)
(-> (dcm/open-thread thread)
(with-meta {::ev/origin "workspace"})))))))]
[:div {:class (stl/css :comments-section)}
[:div {:class (stl/css :comments-section-title)}
[:div {:class (stl/css-case :comments-section true
:from-viewer from-viewer)}
[:div {:class (stl/css-case :comments-section-title true
:viewer-title from-viewer)}
[:span (tr "labels.comments")]
[:button {:class (stl/css :close-button)
:on-click close-section}
@ -128,11 +131,11 @@
[:span {:class (stl/css :mode-label)} (case (:mode local)
(nil :all) (tr "labels.show-all-comments")
:yours (tr "labels.show-your-comments"))]
[:div {:class (stl/css :icon)} i/arrow-refactor]]
[:div {:class (stl/css :arrow-icon)} i/arrow-refactor]]
[:& dropdown {:show options?
:on-close #(reset! state* false)}
[:& sidebar-options {:local local}]]
[:& sidebar-options {:local local :from-viewer from-viewer}]]
[:div {:class (stl/css :comments-section-content)}

View file

@ -13,6 +13,10 @@
grid-template-rows: $s-40 $s-48 1fr;
}
.from-viewer {
padding: 0 $s-8;
}
.comments-section-title {
@include flexCenter;
@include uppercaseTitleTipography;
@ -29,6 +33,11 @@
}
}
.viewer-title {
margin: 0;
margin-block-start: $s-8;
}
.close-button {
@extend .button-tertiary;
position: absolute;
@ -48,7 +57,8 @@
@extend .asset-element;
background-color: var(--color-background-tertiary);
display: flex;
width: $s-256;
width: 100%;
max-width: $s-256;
height: $s-32;
padding: $s-8;
border-radius: $br-8;
@ -64,9 +74,8 @@
justify-content: flex-start;
}
.icon {
.arrow-icon {
@include flexCenter;
padding-right: 8px;
height: $s-24;
width: $s-24;
svg {
@ -78,9 +87,14 @@
.comment-mode-dropdown {
@extend .dropdown-wrapper;
top: $s-80;
top: $s-92;
left: $s-12;
width: $s-256;
max-width: $s-256;
width: 100%;
}
.viewer-dropdown {
left: $s-8;
}
.dropdown-item {

View file

@ -355,6 +355,9 @@
(not (ctn/has-any-copy-parent? objects shape))
(cfh/component-touched? objects (:id shape)))))
can-detach? (and (seq copies)
(every? #(not (ctn/has-any-copy-parent? objects %)) copies))
do-detach-component
#(st/emit! (dwl/detach-components (map :id copies)))
@ -420,7 +423,7 @@
(when (and (not multi) main-instance? local-component? lacks-annotation? components-v2)
{:msg "workspace.shape.menu.create-annotation"
:action do-create-annotation})
(when (seq copies)
(when can-detach?
{:msg (if (> (count copies) 1)
"workspace.shape.menu.detach-instances-in-bulk"
"workspace.shape.menu.detach-instance")

View file

@ -155,7 +155,8 @@
children]))
(mf/defc layer-item
{::mf/wrap-props false}
{::mf/wrap-props false
::mf/wrap [mf/memo]}
[{:keys [index item selected objects sortable? filtered? depth parent-size component-child? highlighted]}]
(let [id (:id item)
blocked? (:blocked item)

View file

@ -21,19 +21,51 @@
[app.main.ui.icons :as i]
[app.main.ui.workspace.sidebar.layer-item :refer [layer-item]]
[app.util.dom :as dom]
[app.util.globals :as globals]
[app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd]
[app.util.object :as obj]
[app.util.timers :as ts]
[beicon.v2.core :as rx]
[cuerdas.core :as str]
[rumext.v2 :as mf]))
[goog.events :as events]
[rumext.v2 :as mf])
(:import goog.events.EventType))
;; This components is a piece for sharding equality check between top
;; level frames and try to avoid rerender frames that are does not
;; affected by the selected set.
(mf/defc frame-wrapper
{::mf/wrap-props false
::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]}
{::mf/wrap-props false}
[props]
[:> layer-item props])
(let [selected (obj/get props "selected")
callback (mf/use-var false)
pending-selected (mf/use-var selected)
current-selected (mf/use-state selected)
props
(-> props
(obj/clone)
(obj/set! "selected" @current-selected))]
(mf/use-effect
(mf/deps selected)
(fn []
;; Change in selected we schedule a idle-then-raf
;; following changes will update the pending but not create
;; a new callbacks
(reset! pending-selected selected)
(when (not @callback)
(reset!
callback
(ts/idle-then-raf
(fn []
(reset! current-selected @pending-selected)
(reset! callback nil)))))
(fn []
(when @callback
(rx/dispose! @callback)))))
[:> layer-item props]))
(mf/defc layers-tree
{::mf/wrap [mf/memo #(mf/throttle % 200)]
@ -158,6 +190,21 @@
(mf/use-fn
#(swap! state* update :show-menu not))
on-toggle-filters-click
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(toggle-filters)))
hide-menu
(mf/use-fn
#(swap! state* assoc :show-menu false))
on-key-down
(mf/use-fn
(fn [event]
(when (kbd/esc? event) (hide-menu))))
update-search-text
(mf/use-fn
(fn [value _event]
@ -190,6 +237,7 @@
add-filter
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(let [key (-> (dom/get-current-target event)
(dom/get-data "filter")
(keyword))]
@ -226,6 +274,11 @@
(when (<= current-items filtered-objects-total)
(swap! state* update :num-items + 100))))]
(mf/with-effect []
(let [keys [(events/listen globals/document EventType.KEYDOWN on-key-down)
(events/listen globals/document EventType.CLICK hide-menu)]]
(fn [] (doseq [key keys] (events/unlistenByKey key)))))
[filtered-objects
handle-show-more
#(mf/html
@ -236,7 +289,7 @@
:value current-search
:on-clear clear-search-text
:placeholder (tr "workspace.sidebar.layers.search")}
[:button {:on-click toggle-filters
[:button {:on-click on-toggle-filters-click
:class (stl/css-case
:filter-button true
:opened show-menu?
@ -459,7 +512,7 @@
(mf/use-fn
#(st/emit! (dw/toggle-focus-mode)))]
[:div#layers
[:div#layers {:class (stl/css :layers)}
(if (d/not-empty? focus)
[:div {:class (stl/css :tool-window-bar)}
[:button {:class (stl/css :focus-title)

View file

@ -12,7 +12,7 @@
justify-content: space-between;
height: $s-32;
min-height: $s-32;
margin: $s-4 0 $s-4 $s-8;
margin: $s-8 0 $s-4 $s-8;
padding-right: $s-8;
&.search {
@ -164,10 +164,14 @@
color: var(--pill-foreground-color);
}
.layers {
position: relative;
}
.filters-container {
@extend .menu-dropdown;
top: $s-44;
left: $s-12;
position: absolute;
left: $s-20;
width: $s-192;
.filter-menu-item {
@include bodyMedTipography;

View file

@ -522,7 +522,8 @@
(when open?
[:div {:class (stl/css :element-content)}
[:div {:class (stl/css-case :component-wrapper true
:with-actions show-menu?)}
:with-actions show-menu?
:without-actions (not show-menu?))}
[:button {:class (stl/css-case :component-name-wrapper true
:with-main (and can-swap? (not multi))
:swappeable (and can-swap? (not swap-opened?)))

View file

@ -51,6 +51,15 @@
grid-template-columns: 1fr $s-28;
gap: $s-2;
}
&.without-actions {
padding-right: 0.5rem;
.component-name-wrapper {
width: 100%;
border-radius: $br-8;
}
}
}
.component-name-wrapper {

View file

@ -357,13 +357,14 @@
(update-interaction index #(ctsi/set-offset-effect % value)))))
event-type-options [{:value :click :label (tr "workspace.options.interaction-on-click")}
;; TODO: need more UX research
;; :mouse-over (tr "workspace.options.interaction-while-hovering")
;; :mouse-press (tr "workspace.options.interaction-while-pressing")
{:value :mouse-enter :label (tr "workspace.options.interaction-mouse-enter")}
{:value :mouse-leave :label (tr "workspace.options.interaction-mouse-leave")}
{:value :after-delay :label (tr "workspace.options.interaction-after-delay")}]
event-type-options (-> [{:value :click :label (tr "workspace.options.interaction-on-click")}
;; TODO: need more UX research
;; :mouse-over (tr "workspace.options.interaction-while-hovering")
;; :mouse-press (tr "workspace.options.interaction-while-pressing")
{:value :mouse-enter :label (tr "workspace.options.interaction-mouse-enter")}
{:value :mouse-leave :label (tr "workspace.options.interaction-mouse-leave")}]
(cond-> (cfh/frame-shape? shape)
(conj {:value :after-delay :label (tr "workspace.options.interaction-after-delay")})))
action-type-options [{:value :navigate :label (tr "workspace.options.interaction-navigate-to")}
{:value :open-overlay :label (tr "workspace.options.interaction-open-overlay")}

View file

@ -20,8 +20,7 @@
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [get-layout-flex-icon]]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[rumext.v2 :as mf]
[rumext.v2.props :as-alias mf.props]))
[rumext.v2 :as mf]))
(def layout-item-attrs
[:layout-item-margin ;; {:m1 0 :m2 0 :m3 0 :m4 0}
@ -46,14 +45,14 @@
(mf/defc margin-simple
{::mf/props :obj}
[{:keys [margin on-change on-blur]}]
(let [m1 (:m1 margin)
m2 (:m2 margin)
m3 (:m3 margin)
m4 (:m4 margin)
[{:keys [value on-change on-blur]}]
(let [m1 (:m1 value)
m2 (:m2 value)
m3 (:m3 value)
m4 (:m4 value)
m1 (when (and (not= margin :multiple) (= m1 m3)) m1)
m2 (when (and (not= margin :multiple) (= m2 m4)) m2)
m1 (when (and (not= value :multiple) (= m1 m3)) m1)
m2 (when (and (not= value :multiple) (= m2 m4)) m2)
on-focus
(mf/use-fn
@ -106,11 +105,11 @@
(mf/defc margin-multiple
{::mf/props :obj}
[{:keys [margin on-change on-blur]}]
(let [m1 (:m1 margin)
m2 (:m2 margin)
m3 (:m3 margin)
m4 (:m4 margin)
[{:keys [value on-change on-blur]}]
(let [m1 (:m1 value)
m2 (:m2 value)
m3 (:m3 value)
m4 (:m4 value)
on-focus
(mf/use-fn
@ -186,11 +185,11 @@
(mf/defc margin-section
{::mf/props :obj
::mf/private true
::mf.props/expect #{:margin :type :on-type-change :on-change}}
::mf/expect-props #{:value :type :on-type-change :on-change}}
[{:keys [type on-type-change] :as props}]
(let [type (d/nilv type :simple)
on-blur (mf/use-fn #(select-margins false false false false))
props (mf/spread-obj props {:on-blur on-blur})
props (mf/spread props :on-blur on-blur)
on-type-change'
(mf/use-fn
@ -220,14 +219,17 @@
i/margin-refactor]]))
(mf/defc element-behaviour-horizontal
{::mf/props :obj}
[{:keys [^boolean is-auto ^boolean has-fill sizing on-change]}]
[:div {:class (stl/css-case :horizontal-behaviour true
:one-element (and (not has-fill) (not is-auto))
:two-element (or has-fill is-auto)
:three-element (and has-fill is-auto))}
{::mf/props :obj
::mf/private true}
[{:keys [^boolean is-auto ^boolean has-fill value on-change]}]
[:div {:class (stl/css-case
:horizontal-behaviour true
:one-element (and (not has-fill) (not is-auto))
:two-element (or has-fill is-auto)
:three-element (and has-fill is-auto))}
[:& radio-buttons
{:selected (d/name sizing)
{:selected (d/name value)
:decode-fn keyword
:on-change on-change
:wide true
:name "flex-behaviour-h"}
@ -252,14 +254,17 @@
:id "behaviour-h-auto"}])]])
(mf/defc element-behaviour-vertical
{::mf/props :obj}
[{:keys [^boolean is-auto ^boolean has-fill sizing on-change]}]
[:div {:class (stl/css-case :vertical-behaviour true
:one-element (and (not has-fill) (not is-auto))
:two-element (or has-fill is-auto)
:three-element (and has-fill is-auto))}
{::mf/props :obj
::mf/private true}
[{:keys [^boolean is-auto ^boolean has-fill value on-change]}]
[:div {:class (stl/css-case
:vertical-behaviour true
:one-element (and (not has-fill) (not is-auto))
:two-element (or has-fill is-auto)
:three-element (and has-fill is-auto))}
[:& radio-buttons
{:selected (d/name sizing)
{:selected (d/name value)
:decode-fn keyword
:on-change on-change
:wide true
:name "flex-behaviour-v"}
@ -286,34 +291,11 @@
:title "Fit content"
:id "behaviour-v-auto"}])]])
(mf/defc element-behaviour
{::mf/props :obj
::mf/private true}
[{:keys [^boolean is-auto
^boolean has-fill
h-sizing
v-sizing
on-h-change
on-v-change]}]
[:div {:class (stl/css-case
:behaviour-menu true
:wrap (and has-fill is-auto))}
[:& element-behaviour-horizontal
{:is-auto is-auto
:has-fill has-fill
:sizing h-sizing
:on-change on-h-change}]
[:& element-behaviour-vertical
{:is-auto is-auto
:has-fill has-fill
:sizing v-sizing
:on-change on-v-change}]])
(mf/defc align-self-row
{::mf/props :obj}
[{:keys [^boolean is-col align-self on-change]}]
[:& radio-buttons {:selected (d/name align-self)
[{:keys [^boolean is-col value on-change]}]
[:& radio-buttons {:selected (d/name value)
:decode-fn keyword
:on-change on-change
:name "flex-align-self"
:allow-empty true}
@ -392,16 +374,16 @@
:else
"Layout element")
set-align-self
on-align-self-change
(mf/use-fn
(mf/deps ids align-self)
(fn [value]
(if (= align-self value)
(st/emit! (dwsl/update-layout-child ids {:layout-item-align-self nil}))
(st/emit! (dwsl/update-layout-child ids {:layout-item-align-self (keyword value)})))))
(st/emit! (dwsl/update-layout-child ids {:layout-item-align-self value})))))
;; Margin
on-change-margin-type
on-margin-type-change
(mf/use-fn
(mf/deps ids)
(fn [type]
@ -422,19 +404,17 @@
(st/emit! (dwsl/update-layout-child ids {:layout-item-margin {prop val}})))))
;; Behaviour
on-change-behaviour-h
on-behaviour-h-change
(mf/use-fn
(mf/deps ids)
(fn [value]
(let [value (keyword value)]
(st/emit! (dwsl/update-layout-child ids {:layout-item-h-sizing value})))))
(st/emit! (dwsl/update-layout-child ids {:layout-item-h-sizing value}))))
on-change-behaviour-v
on-behaviour-v-change
(mf/use-fn
(mf/deps ids)
(fn [value]
(let [value (keyword value)]
(st/emit! (dwsl/update-layout-child ids {:layout-item-v-sizing value})))))
(st/emit! (dwsl/update-layout-child ids {:layout-item-v-sizing value}))))
;; Size and position
on-size-change
@ -450,10 +430,9 @@
(mf/use-fn
(mf/deps ids)
(fn [value]
(let [value (keyword value)]
(when (= value :static)
(st/emit! (dwsl/update-layout-child ids {:layout-item-z-index nil})))
(st/emit! (dwsl/update-layout-child ids {:layout-item-absolute (= value :absolute)})))))
(when (= value :static)
(st/emit! (dwsl/update-layout-child ids {:layout-item-z-index nil})))
(st/emit! (dwsl/update-layout-child ids {:layout-item-absolute (= value :absolute)}))))
;; Z Index
on-change-z-index
@ -476,6 +455,7 @@
[:div {:class (stl/css :row)}
[:div {:class (stl/css :position-options)}
[:& radio-buttons {:selected (if is-absolute? "absolute" "static")
:decode-fn keyword
:on-change on-change-position
:name "layout-style"
:wide true}
@ -497,24 +477,32 @@
:value (:layout-item-z-index values)}]]])
[:div {:class (stl/css :row)}
[:& element-behaviour {:has-fill is-layout-child?
:is-auto is-layout-container?
:v-sizing (:layout-item-v-sizing values)
:h-sizing (:layout-item-h-sizing values)
:on-h-change on-change-behaviour-h
:on-v-change on-change-behaviour-v}]]
[:div {:class (stl/css-case
:behaviour-menu true
:wrap (and ^boolean is-layout-child?
^boolean is-layout-container?))}
[:& element-behaviour-horizontal
{:is-auto is-layout-container?
:has-fill is-layout-child?
:value (:layout-item-h-sizing values)
:on-change on-behaviour-h-change}]
[:& element-behaviour-vertical
{:is-auto is-layout-container?
:has-fill is-layout-child?
:value (:layout-item-v-sizing values)
:on-change on-behaviour-v-change}]]]
(when (and is-layout-child? is-flex-parent?)
[:div {:class (stl/css :row)}
[:& align-self-row {:is-col is-col?
:align-self align-self
:on-change set-align-self}]])
:value align-self
:on-change on-align-self-change}]])
(when is-layout-child?
[:div {:class (stl/css :row)}
[:& margin-section {:margin (:layout-item-margin values)
[:& margin-section {:value (:layout-item-margin values)
:type (:layout-item-margin-type values)
:on-type-change on-change-margin-type
:on-type-change on-margin-type-change
:on-change on-margin-change}]])
(when (or (= h-sizing :fill)

View file

@ -632,6 +632,7 @@
(not= @hover-top-frame-id (:id frame)))
[:& grid-layout/editor
{:zoom zoom
:key (dm/str (:id frame))
:objects base-objects
:modifiers modifiers
:shape frame

View file

@ -113,7 +113,7 @@
(+ (:y start-p) (/ 9 zoom))])
handle-click
(mf/use-callback
(mf/use-fn
(mf/deps on-click)
#(when on-click (on-click)))]
@ -142,7 +142,7 @@
current-pos-ref (mf/use-ref nil)
handle-pointer-down
(mf/use-callback
(mf/use-fn
(mf/deps on-drag-start)
(fn [event]
(let [raw-pt (dom/get-client-position event)
@ -154,7 +154,7 @@
(when on-drag-start (on-drag-start event position)))))
handle-lost-pointer-capture
(mf/use-callback
(mf/use-fn
(mf/deps on-drag-end)
(fn [event]
(let [raw-pt (mf/ref-val current-pos-ref)
@ -165,7 +165,7 @@
(when on-drag-end (on-drag-end event position)))))
handle-pointer-move
(mf/use-callback
(mf/use-fn
(mf/deps on-drag-delta on-drag-position)
(fn [event]
(when (mf/ref-val dragging-ref)
@ -198,7 +198,7 @@
layout-data (unchecked-get props "layout-data")
handle-drag-position
(mf/use-callback
(mf/use-fn
(mf/deps shape row column row-span column-span)
(fn [_ position]
(let [[drag-row drag-column] (gsg/get-position-grid-coord layout-data position)
@ -235,7 +235,7 @@
(st/emit! (dwm/set-modifiers (dwm/create-modif-tree [(:id shape)] modifiers))))))
handle-drag-end
(mf/use-callback
(mf/use-fn
(fn []
(st/emit! (dwm/apply-modifiers))))
@ -291,17 +291,10 @@
text]]))
(mf/defc grid-cell
{::mf/wrap [#(mf/memo' % (mf/check-props ["shape" "cell" "layout-data" "zoom" "hover?" "selected?"]))]
::mf/wrap-props false}
[props]
(let [shape (unchecked-get props "shape")
cell (unchecked-get props "cell")
layout-data (unchecked-get props "layout-data")
zoom (unchecked-get props "zoom")
hover? (unchecked-get props "hover?")
selected? (unchecked-get props "selected?")
cell-bounds (gsg/cell-bounds layout-data cell)
{::mf/memo #{:shape :cell :layout-data :zoom :hover? :selected?}
::mf/props :obj}
[{:keys [shape cell layout-data zoom hover? selected?]}]
(let [cell-bounds (gsg/cell-bounds layout-data cell)
cell-origin (gpo/origin cell-bounds)
cell-width (gpo/width-points cell-bounds)
cell-height (gpo/height-points cell-bounds)
@ -309,19 +302,19 @@
cell-origin (gpt/transform cell-origin (gmt/transform-in cell-center (:transform-inverse shape)))
handle-pointer-enter
(mf/use-callback
(mf/use-fn
(mf/deps (:id shape) (:id cell))
(fn []
(st/emit! (dwge/hover-grid-cell (:id shape) (:id cell) true))))
handle-pointer-leave
(mf/use-callback
(mf/use-fn
(mf/deps (:id shape) (:id cell))
(fn []
(st/emit! (dwge/hover-grid-cell (:id shape) (:id cell) false))))
handle-pointer-down
(mf/use-callback
(mf/use-fn
(mf/deps (:id shape) (:id cell) selected?)
(fn [event]
(when (dom/left-mouse? event)
@ -339,7 +332,7 @@
(st/emit! (dwge/set-selection (:id shape) (:id cell)))))))
handle-context-menu
(mf/use-callback
(mf/use-fn
(mf/deps (:id shape) (:id cell) selected?)
(fn [event]
(dom/prevent-default event)
@ -424,7 +417,7 @@
start-size-after (mf/use-var nil)
handle-drag-start
(mf/use-callback
(mf/use-fn
(mf/deps shape track-before track-after)
(fn []
(reset! start-size-before (:size track-before))
@ -444,7 +437,7 @@
(st/emit! (dwm/set-modifiers (dwm/create-modif-tree [(:id shape)] modifiers))))))
handle-drag-position
(mf/use-callback
(mf/use-fn
(mf/deps shape track-before track-after)
(fn [_ position]
(let [[tracks-prop axis]
@ -469,7 +462,7 @@
(st/emit! (dwm/set-modifiers (dwm/create-modif-tree [(:id shape)] modifiers))))))
handle-drag-end
(mf/use-callback
(mf/use-fn
(mf/deps track-before track-after)
(fn []
(reset! start-size-before nil)
@ -732,7 +725,7 @@
text-p (if (= type :column) hpt vpt)
handle-blur-track-input
(mf/use-callback
(mf/use-fn
(mf/deps (:id shape))
(fn [event]
(let [target (-> event dom/get-target)
@ -760,7 +753,7 @@
(obj/set! target "value" (dom/get-attribute target "data-default-value"))))))
handle-keydown-track-input
(mf/use-callback
(mf/use-fn
(fn [event]
(let [enter? (kbd/enter? event)
esc? (kbd/esc? event)]
@ -770,13 +763,13 @@
(dom/blur! (dom/get-target event))))))
handle-pointer-enter
(mf/use-callback
(mf/use-fn
(mf/deps (:id shape) type index)
(fn []
(st/emit! (dwsl/hover-layout-track [(:id shape)] type index true))))
handle-pointer-leave
(mf/use-callback
(mf/use-fn
(mf/deps (:id shape) type index)
(fn []
(st/emit! (dwsl/hover-layout-track [(:id shape)] type index false))))
@ -788,25 +781,25 @@
[(- (:x text-p) (max 0 (:size track-data))) (- (:y text-p) (/ 36 zoom)) (max 0 (:size track-data)) (/ 36 zoom)])
handle-drag-start
(mf/use-callback
(mf/use-fn
(mf/deps on-start-reorder-track type index)
(fn []
(on-start-reorder-track type index)))
handle-drag-end
(mf/use-callback
(mf/use-fn
(mf/deps on-end-reorder-track type index)
(fn [event position]
(on-end-reorder-track type index position (not (kbd/mod? event)))))
handle-drag-position
(mf/use-callback
(mf/use-fn
(mf/deps on-move-reorder-track type index)
(fn [_ position]
(on-move-reorder-track type index position)))
handle-show-track-menu
(mf/use-callback
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(dom/prevent-default event)
@ -895,10 +888,9 @@
:zoom zoom}]]))
(mf/defc editor
{::mf/wrap [mf/memo]
::mf/wrap-props false}
{::mf/memo true
::mf/props :obj}
[props]
(let [base-shape (unchecked-get props "shape")
objects (unchecked-get props "objects")
modifiers (unchecked-get props "modifiers")
@ -962,31 +954,30 @@
height (max (gpo/height-points bounds) (+ row-total-size row-total-gap (ctl/v-padding shape)))
handle-pointer-down
(mf/use-callback
(mf/use-fn
(fn [event]
(let [left-click? (= 1 (.-which (.-nativeEvent event)))]
(when left-click?
(dom/stop-propagation event)))))
handle-add-column
(mf/use-callback
(mf/use-fn
(mf/deps (:id shape))
(fn []
(st/emit! (st/emit! (dwsl/add-layout-track [(:id shape)] :column ctl/default-track-value)))))
handle-add-row
(mf/use-callback
(mf/use-fn
(mf/deps (:id shape))
(fn []
(st/emit! (st/emit! (dwsl/add-layout-track [(:id shape)] :row ctl/default-track-value)))))
target-tracks* (mf/use-ref nil)
drop-track-type* (mf/use-state nil)
drop-track-target* (mf/use-state nil)
handle-start-reorder-track
(mf/use-callback
(mf/use-fn
(mf/deps layout-data)
(fn [type _from-idx]
;; Initialize target-tracks
@ -1014,7 +1005,7 @@
(reset! drop-track-type* type))))
handle-move-reorder-track
(mf/use-callback
(mf/use-fn
(fn [_type _from-idx position]
(let [index
(->> (mf/ref-val target-tracks*)
@ -1025,7 +1016,7 @@
(reset! drop-track-target* index)))))
handle-end-reorder-track
(mf/use-callback
(mf/use-fn
(mf/deps base-shape @drop-track-target*)
(fn [type from-index _position move-content?]
(when-let [to-index @drop-track-target*]
@ -1041,9 +1032,8 @@
(reset! drop-track-type* nil)
(reset! drop-track-target* nil)))]
(mf/use-effect
(fn []
#(st/emit! (dwge/stop-grid-layout-editing (:id shape)))))
(mf/with-effect []
#(st/emit! (dwge/stop-grid-layout-editing (:id shape))))
(when (and (not (:hidden shape)) (not (:blocked shape)))
[:g.grid-editor {:pointer-events (when view-only "none")
@ -1057,7 +1047,8 @@
:zoom zoom
:hover? (contains? hover-cells (:id cell))
:selected? (contains? selected-cells (:id cell))}])]
(when-not view-only
(when-not ^boolean view-only
[:*
[:& grid-editor-frame {:zoom zoom
:bounds bounds

View file

@ -291,7 +291,7 @@
(not (ctk/main-instance? obj))))
;; Set with the elements to remove from the hover list
remove-id-xf
remove-hover-xf
(cond
mod?
(filter grouped?)
@ -306,8 +306,25 @@
(and (contains? #{:group :bool} (dm/get-in objects [% :type]))
(not (contains? child-parent? %)))))))
remove-id?
(into selected-with-parents remove-id-xf ids)
remove-measure-xf
(cond
mod?
(filter grouped?)
(not mod?)
(let [child-parent?
(into #{}
(comp (remove #(cfh/group-like-shape? objects %))
(mapcat #(cfh/get-parent-ids objects %)))
ids)]
(filter #(and (contains? #{:group :bool} (dm/get-in objects [% :type]))
(not (contains? child-parent? %))))))
remove-hover?
(into selected-with-parents remove-hover-xf ids)
remove-measure?
(into selected-with-parents remove-measure-xf ids)
no-fill-nested-frames?
(fn [id]
@ -318,7 +335,7 @@
hover-shape
(->> ids
(remove remove-id?)
(remove remove-hover?)
(remove (partial cfh/hidden-parent? objects))
(remove #(and mod? (no-fill-nested-frames? %)))
(filter #(or (empty? focus) (cpf/is-in-focus? objects focus %)))
@ -329,14 +346,12 @@
measure-hover-shape
(when show-measures?
(->> ids
(remove #(group-empty-space? % objects ids))
(remove remove-measure?)
(remove (partial cfh/hidden-parent? objects))
(remove #(and mod? (no-fill-nested-frames? %)))
(filter #(or (empty? focus) (cpf/is-in-focus? objects focus %)))
(first)
(get objects)))]
(reset! hover hover-shape)
(reset! measure-hover measure-hover-shape)
(reset! hover-ids ids)))

View file

@ -6,22 +6,7 @@
(ns app.util.time
(:require
["date-fns/format" :default dateFnsFormat]
["date-fns/formatDistanceToNowStrict" :default dateFnsFormatDistanceToNowStrict]
["date-fns/locale/ar-SA" :default dateFnsLocalesAr]
["date-fns/locale/ca" :default dateFnsLocalesCa]
["date-fns/locale/de" :default dateFnsLocalesDe]
["date-fns/locale/el" :default dateFnsLocalesEl]
["date-fns/locale/en-US" :default dateFnsLocalesEnUs]
["date-fns/locale/es" :default dateFnsLocalesEs]
["date-fns/locale/fa-IR" :default dateFnsLocalesFa]
["date-fns/locale/fr" :default dateFnsLocalesFr]
["date-fns/locale/he" :default dateFnsLocalesHe]
["date-fns/locale/pt-BR" :default dateFnsLocalesPtBr]
["date-fns/locale/ro" :default dateFnsLocalesRo]
["date-fns/locale/ru" :default dateFnsLocalesRu]
["date-fns/locale/tr" :default dateFnsLocalesTr]
["date-fns/locale/zh-CN" :default dateFnsLocalesZhCn]
["./time_impl.js" :as impl]
[app.common.data.macros :as dm]
[app.common.time :as common-time]
[app.util.object :as obj]
@ -207,22 +192,6 @@
:json (.toJSON it)
(.toFormat ^js it fmt))))
(def ^:private locales
#js {:en dateFnsLocalesEnUs
:ar dateFnsLocalesAr
:he dateFnsLocalesHe
:fr dateFnsLocalesFr
:tr dateFnsLocalesTr
:es dateFnsLocalesEs
:ca dateFnsLocalesCa
:el dateFnsLocalesEl
:ru dateFnsLocalesRu
:ro dateFnsLocalesRo
:de dateFnsLocalesDe
:fa dateFnsLocalesFa
:pt_br dateFnsLocalesPtBr
:zh_cn dateFnsLocalesZhCn})
(defn timeago
([v] (timeago v nil))
([v {:keys [locale] :or {locale "en"}}]
@ -230,19 +199,18 @@
(let [v (if (datetime? v) (format v :date) v)]
(->> #js {:includeSeconds true
:addSuffix true
:locale (obj/get locales locale)}
(dateFnsFormatDistanceToNowStrict v))))))
:locale (obj/get impl/locales locale)}
(impl/format-distance-to-now v))))))
(defn format-date-locale
([v] (format-date-locale v nil))
([v {:keys [locale] :or {locale "en"}}]
(when v
(let [v (if (datetime? v) (format v :date) v)
locale (obj/get locales locale)
locale (obj/get impl/locales locale)
f (.date (.-formatLong ^js locale) v)]
(->> #js {:locale locale}
(dateFnsFormat v f))))))
(impl/format v f))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Measurement Helpers

View file

@ -0,0 +1,71 @@
import fmt1 from "date-fns/format";
import fmt2 from "date-fns/formatDistanceToNowStrict";
import {arSA} from "date-fns/locale/ar-SA";
import {ca} from "date-fns/locale/ca";
import {de} from "date-fns/locale/de";
import {el} from "date-fns/locale/el";
import {enUS} from "date-fns/locale/en-US";
import {es} from "date-fns/locale/es";
import {faIR} from "date-fns/locale/fa-IR";
import {fr} from "date-fns/locale/fr";
import {he} from "date-fns/locale/he";
import {pt} from "date-fns/locale/pt";
import {ptBR} from "date-fns/locale/pt-BR";
import {ro} from "date-fns/locale/ro";
import {ru} from "date-fns/locale/ru";
import {tr} from "date-fns/locale/tr";
import {zhCN} from "date-fns/locale/zh-CN";
import {nl} from "date-fns/locale/nl";
import {eu} from "date-fns/locale/eu";
import {gl} from "date-fns/locale/gl";
import {hr} from "date-fns/locale/hr";
import {it} from "date-fns/locale/it";
import {nb} from "date-fns/locale/nb";
import {pl} from "date-fns/locale/pl";
import {id} from "date-fns/locale/id";
import {uk} from "date-fns/locale/uk";
import {cs} from "date-fns/locale/cs";
import {lv} from "date-fns/locale/lv";
import {ko} from "date-fns/locale/ko";
import {ja} from "date-fns/locale/ja";
export const locales = {
"ar": arSA,
"ca": ca,
"de": de,
"el": el,
"en": enUS,
"en_us": enUS,
"es": es,
"es_es": es,
"fa": faIR,
"fa_ir": faIR,
"fr": fr,
"he": he,
"pt": pt,
"pt_pt": pt,
"pt_br": ptBR,
"ro": ro,
"ru": ru,
"tr": tr,
"zh_cn": zhCN,
"nl": nl,
"eu": eu,
"gl": gl,
"hr": hr,
"it": it,
"nb": nb,
"nb_no": nb,
"pl": pl,
"id": id,
"uk": uk,
"cs": cs,
"lv": lv,
"ko": ko,
"ja": ja,
"ja_jp": ja,
};
export const format = fmt1.format;
export const format_distance_to_now = fmt2.formatDistanceToNowStrict;

View file

@ -43,7 +43,7 @@
(if (and (exists? js/window)
(.-requestIdleCallback js/window))
(do
(def ^:private request-idle-callback #(js/requestIdleCallback %))
(def ^:private request-idle-callback #(js/requestIdleCallback % #js {:timeout 30000})) ;; 30s timeout
(def ^:private cancel-idle-callback #(js/cancelIdleCallback %)))
(do
(def ^:private request-idle-callback #(js/setTimeout % 250))

View file

@ -28,7 +28,8 @@
[{:keys [page-id frame-id axis ranges bounds] :as message}]
(let [match-bounds?
(fn [[_ data]]
(some #(grc/contains-point? bounds %) (map :pt data)))]
(some #(or (= :guide (:type %))
(grc/contains-point? bounds (:pt %))) data))]
(->> (into []
(comp (mapcat #(sd/query @state page-id frame-id axis %))
(distinct))

View file

@ -4872,20 +4872,6 @@ msgstr[2] ""
"Aktiva, která již byla v těchto souborech použita, tam zůstanou (nebude "
"porušen žádný návrh)."
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.no-files-message"
msgid_plural "modals.unpublish-shared-confirm.activated.no-files-message"
msgstr[0] "Není aktivován v žádném souboru."
msgstr[1] "Nejsou aktivovány v žádném souboru."
msgstr[2] "Nejsou aktivovány v žádném souboru."
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.scd-message"
msgid_plural "modals.unpublish-shared-confirm.activated.scd-message"
msgstr[0] "Tato knihovna je aktivována zde:"
msgstr[1] "Tyto knihovny jsou aktivovány zde:"
msgstr[2] "Tyto knihovny jsou aktivovány zde:"
#: src/app/main/ui/auth/register.cljs, src/app/main/ui/dashboard/team_form.cljs, src/app/main/ui/onboarding/team_choice.cljs, src/app/main/ui/settings/access_tokens.cljs, src/app/main/ui/settings/feedback.cljs, src/app/main/ui/settings/profile.cljs, src/app/main/ui/workspace/sidebar/assets.cljs
msgid "auth.name.too-long"
msgstr "Název musí obsahovat maximálně 250 znaků."

View file

@ -2122,18 +2122,6 @@ msgid_plural "modals.unpublish-shared-confirm.accept"
msgstr[0] "Veröffentlichung aufheben"
msgstr[1] "Veröffentlichung aufheben"
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.no-files-message"
msgid_plural "modals.unpublish-shared-confirm.activated.no-files-message"
msgstr[0] "Es ist in keiner Datei aktiviert."
msgstr[1] "Sie sind in keiner Datei aktiviert."
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.scd-message"
msgid_plural "modals.unpublish-shared-confirm.activated.scd-message"
msgstr[0] "Diese Bibliothek ist hier aktiviert:"
msgstr[1] "Diese Bibliotheken sind hier aktiviert:"
#: src/app/main/ui/workspace/header.cljs,
#: src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.message"

View file

@ -2103,17 +2103,10 @@ msgid_plural "modals.unpublish-shared-confirm.accept"
msgstr[0] "Unpublish"
msgstr[1] "Unpublish"
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.no-files-message"
msgid_plural "modals.unpublish-shared-confirm.activated.no-files-message"
msgstr[0] "It isn't activated in any file."
msgstr[1] "They aren't activated in any file."
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.scd-message"
msgid_plural "modals.unpublish-shared-confirm.activated.scd-message"
msgstr[0] "This library is activated here:"
msgstr[1] "This libraries are activated here:"
msgid "modals.move-shared-confirm.accept"
msgid_plural "modals.move-shared-confirm.accept"
msgstr[0] "Move"
msgstr[1] "Move"
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.message"
@ -2121,12 +2114,22 @@ msgid_plural "modals.unpublish-shared-confirm.message"
msgstr[0] "Are you sure you want to unpublish this library?"
msgstr[1] "Are you sure you want to unpublish these libraries?"
msgid "modals.move-shared-confirm.message"
msgid_plural "modals.move-shared-confirm.message"
msgstr[0] "Are you sure you want to move this library?"
msgstr[1] "Are you sure you want to move these libraries?"
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.title"
msgid_plural "modals.unpublish-shared-confirm.title"
msgstr[0] "Unpublish library"
msgstr[1] "Unpublish libraries"
msgid "modals.move-shared-confirm.title"
msgid_plural "modals.move-shared-confirm.title"
msgstr[0] "Move library"
msgstr[1] "Move libraries"
#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs
msgid "modals.update-remote-component-in-bulk.hint"
msgstr ""

View file

@ -2138,16 +2138,10 @@ msgid_plural "modals.unpublish-shared-confirm.accept"
msgstr[0] "Despublicar"
msgstr[1] "Despublicar"
msgid "modals.unpublish-shared-confirm.activated.no-files-message"
msgid_plural "modals.unpublish-shared-confirm.activated.no-files-message"
msgstr[0] "No está activa en ningún fichero."
msgstr[1] "No están activas en ningún fichero."
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.scd-message"
msgid_plural "modals.unpublish-shared-confirm.activated.scd-message"
msgstr[0] "Está activa aquí:"
msgstr[1] "Están activas aquí:"
msgid "modals.move-shared-confirm.accept"
msgid_plural "modals.move-shared-confirm.accept"
msgstr[0] "Mover"
msgstr[1] "Mover"
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.message"
@ -2155,12 +2149,22 @@ msgid_plural "modals.unpublish-shared-confirm.message"
msgstr[0] "¿Seguro que quieres despublicar esta biblioteca?"
msgstr[1] "¿Seguro que quieres despublicar estas bibliotecas?"
msgid "modals.move-shared-confirm.message"
msgid_plural "modals.move-shared-confirm.message"
msgstr[0] "¿Seguro que quieres mover esta biblioteca?"
msgstr[1] "¿Seguro que quieres mover estas bibliotecas?"
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.title"
msgid_plural "modals.unpublish-shared-confirm.title"
msgstr[0] "Despublicar biblioteca"
msgstr[1] "Despublicar bibliotecas"
msgid "modals.move-shared-confirm.title"
msgid_plural "modals.move-shared-confirm.title"
msgstr[0] "Mover biblioteca"
msgstr[1] "Mover bibliotecas"
#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs,
#: src/app/main/ui/workspace/context_menu.cljs
msgid "modals.update-remote-component-in-bulk.hint"

View file

@ -2127,18 +2127,6 @@ msgid_plural "modals.unpublish-shared-confirm.accept"
msgstr[0] "Dépublier"
msgstr[1] "Dépublier"
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.no-files-message"
msgid_plural "modals.unpublish-shared-confirm.activated.no-files-message"
msgstr[0] "Activée dans aucun fichier."
msgstr[1] "Activées dans aucun fichier."
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.scd-message"
msgid_plural "modals.unpublish-shared-confirm.activated.scd-message"
msgstr[0] "Est activée ici:"
msgstr[1] "Sont activées ici:"
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.message"
msgid_plural "modals.unpublish-shared-confirm.message"

View file

@ -2157,12 +2157,6 @@ msgstr ""
"wurin da za ka san yadda za ka hada-hannu da fassara, neman fasali, manyan "
"gudunmawa, magance matsala…"
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.scd-message"
msgid_plural "modals.unpublish-shared-confirm.activated.scd-message"
msgstr[0] "an buxe wannan taskar a nan:"
msgstr[1] "an buxe taskokin nan a nan:"
#: src/app/main/ui/workspace/sidebar/align.cljs
msgid "workspace.align.hright"
msgstr "Daidaita dama (%s)"
@ -3309,12 +3303,6 @@ msgstr "Yi da kanka"
msgid "labels.save"
msgstr "ajiye"
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.no-files-message"
msgid_plural "modals.unpublish-shared-confirm.activated.no-files-message"
msgstr[0] "ba zai yi aiki a kowane kundi ba."
msgstr[1] "ba zai yi aiki a kowane kundi ba."
msgid "dashboard.import.progress.process-media"
msgstr "kammala aiki"

View file

@ -4992,14 +4992,6 @@ msgstr "… תרשימי מתאר, סיפורי ותהליכי משתמשים,
msgid "workspace.options.stroke-cap.diamond-marker-short"
msgstr "יהלום"
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.scd-message"
msgid_plural "modals.unpublish-shared-confirm.activated.scd-message"
msgstr[0] "הספרייה הזאת מופעלת כאן:"
msgstr[1] "הספריות האלו מופעלות כאן:"
msgstr[2] "הספריות האלו מופעלות כאן:"
msgstr[3] "הספריות האלו מופעלות כאן:"
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs,
msgid "settings.detach"
msgstr "ניתוק"
@ -5008,14 +5000,6 @@ msgstr "ניתוק"
msgid "workspace.options.stroke-cap.triangle-arrow-short"
msgstr "משולש"
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.no-files-message"
msgid_plural "modals.unpublish-shared-confirm.activated.no-files-message"
msgstr[0] "אינו מופעל באף קובץ."
msgstr[1] "אינם מופעלים באף קובץ."
msgstr[2] "אינם מופעלים באף קובץ."
msgstr[3] "אינם מופעלים באף קובץ."
msgid "workspace.shape.menu.create-annotation"
msgstr "יצירת הסבר"

View file

@ -2098,16 +2098,6 @@ msgid "modals.unpublish-shared-confirm.accept"
msgid_plural "modals.unpublish-shared-confirm.accept"
msgstr[0] "Batalkan penerbitan"
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.no-files-message"
msgid_plural "modals.unpublish-shared-confirm.activated.no-files-message"
msgstr[0] "Tidak diaktifkan dalam berkas mana pun."
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.scd-message"
msgid_plural "modals.unpublish-shared-confirm.activated.scd-message"
msgstr[0] "Pustaka ini diaktifkan di sini:"
#: src/app/main/ui/workspace/header.cljs,
#: src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.message"

View file

@ -2112,20 +2112,6 @@ msgstr[0] "Nav atlases"
msgstr[1] "Atcelt publicēšanu"
msgstr[2] "Atcelt publicēšanu"
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.no-files-message"
msgid_plural "modals.unpublish-shared-confirm.activated.no-files-message"
msgstr[0] "Nav aktivēti nevienā datnē."
msgstr[1] "Tas nav aktivēts nevienā datnē."
msgstr[2] "Tie nav aktivēti nevienā datnē."
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.scd-message"
msgid_plural "modals.unpublish-shared-confirm.activated.scd-message"
msgstr[0] "Bibliotēkas ir aktivētas šeit:"
msgstr[1] "Šī bibliotēka ir aktivēta šeit:"
msgstr[2] "Šīs bibliotēkas ir aktivētas šeit:"
#: src/app/main/ui/workspace/header.cljs,
#: src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.message"

View file

@ -2143,20 +2143,6 @@ msgid_plural "modals.unpublish-shared-confirm.accept"
msgstr[0] "Publicatie ongedaan maken"
msgstr[1] "Publicaties ongedaan maken"
#: src/app/main/ui/workspace/header.cljs,
#: src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.no-files-message"
msgid_plural "modals.unpublish-shared-confirm.activated.no-files-message"
msgstr[0] "Het is in geen enkel bestand geactiveerd."
msgstr[1] "Ze zijn in geen enkel bestand geactiveerd."
#: src/app/main/ui/workspace/header.cljs,
#: src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.scd-message"
msgid_plural "modals.unpublish-shared-confirm.activated.scd-message"
msgstr[0] "Deze bibliotheek wordt hier geactiveerd:"
msgstr[1] "Deze bibliotheken worden hier geactiveerd:"
#: src/app/main/ui/workspace/header.cljs,
#: src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.message"

View file

@ -2090,18 +2090,6 @@ msgstr "Remover \"%s\" como Biblioteca Partilhada"
msgid "modals.small-nudge"
msgstr "Pequeno deslocamento"
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.no-files-message"
msgid_plural "modals.unpublish-shared-confirm.activated.no-files-message"
msgstr[0] "Não está ativa em nenhum ficheiro."
msgstr[1] "Não estão ativas em nenhum ficheiro."
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.scd-message"
msgid_plural "modals.unpublish-shared-confirm.activated.scd-message"
msgstr[0] "Esta biblioteca está ativa aqui:"
msgstr[1] "Estas bibliotecas estão ativas aqui:"
#: src/app/main/ui/workspace/header.cljs,
#: src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.message"

View file

@ -2127,20 +2127,6 @@ msgstr[0] "Anulați publicarea"
msgstr[1] "Anulați publicarea"
msgstr[2] "Anulați publicarea"
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.no-files-message"
msgid_plural "modals.unpublish-shared-confirm.activated.no-files-message"
msgstr[0] "Nu este activat în niciun fișier."
msgstr[1] "Nu sunt activate în niciun fișier."
msgstr[2] "Nu sunt activate în niciun fișier."
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.scd-message"
msgid_plural "modals.unpublish-shared-confirm.activated.scd-message"
msgstr[0] "Aceasta librărie este activată aici:"
msgstr[1] "Aceste librării sunt activate aici:"
msgstr[2] "Aceste librării sunt activate aici:"
#: src/app/main/ui/workspace/header.cljs,
#: src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.message"

View file

@ -4764,12 +4764,6 @@ msgstr "Ortala"
msgid "workspace.options.stroke-cap.diamond-marker-short"
msgstr "Elmas"
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.scd-message"
msgid_plural "modals.unpublish-shared-confirm.activated.scd-message"
msgstr[0] "Bu kütüphane burada etkinleştirildi:"
msgstr[1] "Bu kütüphaneler burada etkinleştirildi:"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.questions-how-are-you-planning-to-use-penpot"
msgstr "Penpot'u nasıl kullanmayı planlıyorsunuz?"
@ -4859,12 +4853,6 @@ msgstr "Orantılı ölçeklendirmeyi etkinleştir"
msgid "workspace.updates.more-info"
msgstr "Daha fazla bilgi"
#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "modals.unpublish-shared-confirm.activated.no-files-message"
msgid_plural "modals.unpublish-shared-confirm.activated.no-files-message"
msgstr[0] "Hiçbir dosyada etkinleştirilmemiş."
msgstr[1] "Hiçbir dosyada etkinleştirilmemişler."
#: src/app/main/ui/settings/team-form.cljs, src/app/main/ui/auth/register.cljs, src/app/main/ui/dashboard/team_form.cljs, src/app/main/ui/onboarding/team_choice.cljs, src/app/main/ui/settings/access_tokens.cljs, src/app/main/ui/settings/feedback.cljs, src/app/main/ui/settings/profile.cljs, src/app/main/ui/workspace/sidebar/assets.cljs
msgid "auth.name.not-all-space"
msgstr "İsim boşluk dışında bir karakter içermelidir."

File diff suppressed because it is too large Load diff