mirror of
https://github.com/penpot/penpot.git
synced 2025-08-07 14:38:33 +02:00
✨ Add new components to storybook (#5632)
* ✨ Add new components to storybook * ✨ Changes after review * ✨ More changes after review * ✨ Add file history components to the application * ✨ Unnest selector
This commit is contained in:
parent
0cf4d4636a
commit
c215214120
34 changed files with 1033 additions and 210 deletions
|
@ -1063,3 +1063,8 @@
|
||||||
(>= start 0) (< start size)
|
(>= start 0) (< start size)
|
||||||
(>= end 0) (<= start end) (<= end size))
|
(>= end 0) (<= start end) (<= end size))
|
||||||
(subvec v start end)))))
|
(subvec v start end)))))
|
||||||
|
|
||||||
|
(defn append-class
|
||||||
|
[class current-class]
|
||||||
|
(str (if (some? class) (str class " ") "")
|
||||||
|
current-class))
|
||||||
|
|
|
@ -23,7 +23,12 @@ const preview = {
|
||||||
date: /Date$/i,
|
date: /Date$/i,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
backgrounds: { disable: true },
|
backgrounds: {
|
||||||
|
values: [
|
||||||
|
{ name: 'theme', value: 'var(--color-background-secondary)' },
|
||||||
|
],
|
||||||
|
default: 'theme',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,8 @@
|
||||||
:controls :inline-actions
|
:controls :inline-actions
|
||||||
:type :inline
|
:type :inline
|
||||||
:level level
|
:level level
|
||||||
:actions [{:label "Refresh" :callback force-reload!}]
|
:accept {:label (tr "Refresh")
|
||||||
|
:callback force-reload!}
|
||||||
:tag :notification))
|
:tag :notification))
|
||||||
|
|
||||||
:maintenance
|
:maintenance
|
||||||
|
@ -92,8 +93,8 @@
|
||||||
:content (tr "notifications.by-code.maintenance")
|
:content (tr "notifications.by-code.maintenance")
|
||||||
:controls :inline-actions
|
:controls :inline-actions
|
||||||
:type level
|
:type level
|
||||||
:actions [{:label (tr "labels.accept")
|
:accept {:label (tr "labels.accept")
|
||||||
:callback hide-notifications!}]
|
:callback hide-notifications!}
|
||||||
:tag :notification))
|
:tag :notification))
|
||||||
|
|
||||||
(rx/of (ntf/dialog
|
(rx/of (ntf/dialog
|
||||||
|
|
|
@ -32,6 +32,14 @@
|
||||||
[:or :string :keyword]]
|
[:or :string :keyword]]
|
||||||
[:timeout {:optional true}
|
[:timeout {:optional true}
|
||||||
[:maybe :int]]
|
[:maybe :int]]
|
||||||
|
[:accept {:optional true}
|
||||||
|
[:map
|
||||||
|
[:label :string]
|
||||||
|
[:callback ::sm/fn]]]
|
||||||
|
[:cancel {:optional true}
|
||||||
|
[:map
|
||||||
|
[:label :string]
|
||||||
|
[:callback ::sm/fn]]]
|
||||||
[:actions {:optional true}
|
[:actions {:optional true}
|
||||||
[:vector
|
[:vector
|
||||||
[:map
|
[:map
|
||||||
|
@ -120,7 +128,7 @@
|
||||||
:timeout timeout})))
|
:timeout timeout})))
|
||||||
|
|
||||||
(defn dialog
|
(defn dialog
|
||||||
[& {:keys [content controls actions position tag level links]
|
[& {:keys [content controls actions accept cancel position tag level links]
|
||||||
:or {controls :none position :floating level :info}}]
|
:or {controls :none position :floating level :info}}]
|
||||||
(show (d/without-nils
|
(show (d/without-nils
|
||||||
{:content content
|
{:content content
|
||||||
|
@ -129,4 +137,6 @@
|
||||||
:position position
|
:position position
|
||||||
:controls controls
|
:controls controls
|
||||||
:actions actions
|
:actions actions
|
||||||
|
:accept accept
|
||||||
|
:cancel cancel
|
||||||
:tag tag})))
|
:tag tag})))
|
||||||
|
|
|
@ -1220,12 +1220,10 @@
|
||||||
:controls :inline-actions
|
:controls :inline-actions
|
||||||
:links [{:label (tr "workspace.updates.more-info")
|
:links [{:label (tr "workspace.updates.more-info")
|
||||||
:callback do-more-info}]
|
:callback do-more-info}]
|
||||||
:actions [{:label (tr "workspace.updates.dismiss")
|
:cancel {:label (tr "workspace.updates.dismiss")
|
||||||
:type :secondary
|
:callback do-dismiss}
|
||||||
:callback do-dismiss}
|
:accept {:label (tr "workspace.updates.update")
|
||||||
{:label (tr "workspace.updates.update")
|
:callback do-update}
|
||||||
:type :primary
|
|
||||||
:callback do-update}]
|
|
||||||
:tag :sync-dialog)))))))
|
:tag :sync-dialog)))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
(ptk/reify ::fetch-versions
|
(ptk/reify ::fetch-versions
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [file-id (:current-file-id state)]
|
(when-let [file-id (:current-file-id state)]
|
||||||
(->> (rp/cmd! :get-file-snapshots {:file-id file-id})
|
(->> (rp/cmd! :get-file-snapshots {:file-id file-id})
|
||||||
(rx/map #(update-version-state {:status :loaded :data %})))))))
|
(rx/map #(update-version-state {:status :loaded :data %})))))))
|
||||||
|
|
||||||
|
|
|
@ -19,10 +19,16 @@
|
||||||
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
||||||
[app.main.ui.ds.foundations.utilities.token.token-status :refer [token-status-icon* token-status-list]]
|
[app.main.ui.ds.foundations.utilities.token.token-status :refer [token-status-icon* token-status-list]]
|
||||||
[app.main.ui.ds.layout.tab-switcher :refer [tab-switcher*]]
|
[app.main.ui.ds.layout.tab-switcher :refer [tab-switcher*]]
|
||||||
|
[app.main.ui.ds.notifications.actionable :refer [actionable*]]
|
||||||
[app.main.ui.ds.notifications.toast :refer [toast*]]
|
[app.main.ui.ds.notifications.toast :refer [toast*]]
|
||||||
|
[app.main.ui.ds.product.autosaved-milestone :refer [autosaved-milestone*]]
|
||||||
|
[app.main.ui.ds.product.avatar :refer [avatar*]]
|
||||||
|
[app.main.ui.ds.product.cta :refer [cta*]]
|
||||||
[app.main.ui.ds.product.empty-placeholder :refer [empty-placeholder*]]
|
[app.main.ui.ds.product.empty-placeholder :refer [empty-placeholder*]]
|
||||||
[app.main.ui.ds.product.loader :refer [loader*]]
|
[app.main.ui.ds.product.loader :refer [loader*]]
|
||||||
|
[app.main.ui.ds.product.user-milestone :refer [user-milestone*]]
|
||||||
[app.main.ui.ds.storybook :as sb]
|
[app.main.ui.ds.storybook :as sb]
|
||||||
|
[app.main.ui.ds.utilities.date :refer [date*]]
|
||||||
[app.main.ui.ds.utilities.swatch :refer [swatch*]]
|
[app.main.ui.ds.utilities.swatch :refer [swatch*]]
|
||||||
[app.util.i18n :as i18n]
|
[app.util.i18n :as i18n]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
@ -46,8 +52,14 @@
|
||||||
:Text text*
|
:Text text*
|
||||||
:TabSwitcher tab-switcher*
|
:TabSwitcher tab-switcher*
|
||||||
:Toast toast*
|
:Toast toast*
|
||||||
|
:Actionable actionable*
|
||||||
:TokenStatusIcon token-status-icon*
|
:TokenStatusIcon token-status-icon*
|
||||||
:Swatch swatch*
|
:Swatch swatch*
|
||||||
|
:Cta cta*
|
||||||
|
:Avatar avatar*
|
||||||
|
:AutosavedMilestone autosaved-milestone*
|
||||||
|
:UserMilestone user-milestone*
|
||||||
|
:Date date*
|
||||||
;; meta / misc
|
;; meta / misc
|
||||||
:meta
|
:meta
|
||||||
{:icons (clj->js (sort icon-list))
|
{:icons (clj->js (sort icon-list))
|
||||||
|
|
|
@ -11,6 +11,7 @@ $sz-16: px2rem(16);
|
||||||
$sz-24: px2rem(24);
|
$sz-24: px2rem(24);
|
||||||
$sz-32: px2rem(32);
|
$sz-32: px2rem(32);
|
||||||
$sz-36: px2rem(36);
|
$sz-36: px2rem(36);
|
||||||
|
$sz-40: px2rem(40);
|
||||||
$sz-48: px2rem(48);
|
$sz-48: px2rem(48);
|
||||||
$sz-160: px2rem(160);
|
$sz-160: px2rem(160);
|
||||||
$sz-200: px2rem(200);
|
$sz-200: px2rem(200);
|
||||||
|
|
|
@ -74,8 +74,8 @@ $grayish-red: #bfbfbf;
|
||||||
--color-accent-secondary: #{$cobalt-700};
|
--color-accent-secondary: #{$cobalt-700};
|
||||||
--color-accent-tertiary: #{$purple-600};
|
--color-accent-tertiary: #{$purple-600};
|
||||||
--color-accent-quaternary: #{$pink-400};
|
--color-accent-quaternary: #{$pink-400};
|
||||||
--color-accent-overlay: #{$purple-600-10};
|
--color-accent-overlay: #{$purple-700-60};
|
||||||
--color-accent-select: #{$purple-700-60};
|
--color-accent-select: #{$purple-600-10};
|
||||||
|
|
||||||
--color-accent-success: #{$green-500};
|
--color-accent-success: #{$green-500};
|
||||||
--color-background-success: #{$green-200};
|
--color-background-success: #{$green-200};
|
||||||
|
@ -112,8 +112,8 @@ $grayish-red: #bfbfbf;
|
||||||
--color-accent-secondary: #{$purple-400};
|
--color-accent-secondary: #{$purple-400};
|
||||||
--color-accent-tertiary: #{$mint-250};
|
--color-accent-tertiary: #{$mint-250};
|
||||||
--color-accent-quaternary: #{$pink-400};
|
--color-accent-quaternary: #{$pink-400};
|
||||||
--color-accent-overlay: #{$mint-250-10};
|
--color-accent-overlay: #{$mint-150-60};
|
||||||
--color-accent-select: #{$mint-150-60};
|
--color-accent-select: #{$mint-250-10};
|
||||||
|
|
||||||
--color-accent-success: #{$green-500};
|
--color-accent-success: #{$green-500};
|
||||||
--color-background-success: #{$green-950};
|
--color-background-success: #{$green-950};
|
||||||
|
|
|
@ -19,13 +19,14 @@
|
||||||
[:class {:optional true} :string]
|
[:class {:optional true} :string]
|
||||||
[:icon {:optional true}
|
[:icon {:optional true}
|
||||||
[:and :string [:fn #(contains? icon-list %)]]]
|
[:and :string [:fn #(contains? icon-list %)]]]
|
||||||
[:type {:optional true} :string]])
|
[:type {:optional true} :string]
|
||||||
|
[:variant {:optional true} :string]])
|
||||||
|
|
||||||
(mf/defc input*
|
(mf/defc input*
|
||||||
{::mf/props :obj
|
{::mf/props :obj
|
||||||
::mf/forward-ref true
|
::mf/forward-ref true
|
||||||
::mf/schema schema:input}
|
::mf/schema schema:input}
|
||||||
[{:keys [icon class type] :rest props} ref]
|
[{:keys [icon class type variant] :rest props} ref]
|
||||||
(let [ref (or ref (mf/use-ref))
|
(let [ref (or ref (mf/use-ref))
|
||||||
type (d/nilv type "text")
|
type (d/nilv type "text")
|
||||||
props (mf/spread-props props
|
props (mf/spread-props props
|
||||||
|
@ -43,7 +44,8 @@
|
||||||
(dom/select-node input-node)
|
(dom/select-node input-node)
|
||||||
(dom/focus! input-node))))]
|
(dom/focus! input-node))))]
|
||||||
|
|
||||||
[:> :span {:class (dm/str class " " (stl/css :container))}
|
[:> :span {:class (dm/str class " " (stl/css-case :container true
|
||||||
|
:input-seamless (= variant "seamless")))}
|
||||||
(when (some? icon)
|
(when (some? icon)
|
||||||
[:> icon* {:icon-id icon :class (stl/css :icon) :on-click on-icon-click}])
|
[:> icon* {:icon-id icon :class (stl/css :icon) :on-click on-icon-click}])
|
||||||
[:> :input props]]))
|
[:> :input props]]))
|
||||||
|
|
|
@ -41,12 +41,32 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input-seamless {
|
||||||
|
--input-bg-color: none;
|
||||||
|
--input-outline-color: none;
|
||||||
|
--input-height: auto;
|
||||||
|
--input-margin: 0;
|
||||||
|
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
--input-bg-color: none;
|
||||||
|
--input-outline-color: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:has(*:focus-visible) {
|
||||||
|
--input-bg-color: none;
|
||||||
|
--input-outline-color: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
margin: unset; // remove settings from global css
|
margin: var(--input-margin, unset); // remove settings from global css
|
||||||
padding: 0;
|
padding: 0;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
margin-inline-start: var(--sp-xxs);
|
margin-inline-start: var(--sp-xxs);
|
||||||
height: $sz-32;
|
height: var(--input-height, #{$sz-32});
|
||||||
border: none;
|
border: none;
|
||||||
background: none;
|
background: none;
|
||||||
inline-size: 100%;
|
inline-size: 100%;
|
||||||
|
|
50
frontend/src/app/main/ui/ds/notifications/actionable.cljs
Normal file
50
frontend/src/app/main/ui/ds/notifications/actionable.cljs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
;; 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.ds.notifications.actionable
|
||||||
|
(:require-macros
|
||||||
|
[app.main.style :as stl])
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(def ^:private schema:actionable
|
||||||
|
[:map
|
||||||
|
[:class {:optional true} :string]
|
||||||
|
[:variant {:optional true}
|
||||||
|
[:maybe [:enum "default" "error"]]]
|
||||||
|
[:acceptLabel {:optional true} :string]
|
||||||
|
[:cancelLabel {:optional true} :string]
|
||||||
|
[:onAccept {:optional true} [:fn fn?]]
|
||||||
|
[:onCancel {:optional true} [:fn fn?]]])
|
||||||
|
|
||||||
|
(mf/defc actionable*
|
||||||
|
{::mf/props :obj
|
||||||
|
::mf/schema schema:actionable}
|
||||||
|
[{:keys [class variant acceptLabel cancelLabel children onAccept onCancel] :rest props}]
|
||||||
|
|
||||||
|
(let [variant (or variant "default")
|
||||||
|
class (d/append-class class (stl/css :notification))
|
||||||
|
props (mf/spread-props props {:class class :data-testid "actionable"})
|
||||||
|
|
||||||
|
handle-accept
|
||||||
|
(mf/use-fn
|
||||||
|
(fn [e]
|
||||||
|
(when onAccept (onAccept e))))
|
||||||
|
|
||||||
|
handle-cancel
|
||||||
|
(mf/use-fn
|
||||||
|
(fn [e]
|
||||||
|
(when onCancel (onCancel e))))]
|
||||||
|
|
||||||
|
[:> "aside" props
|
||||||
|
[:div {:class (stl/css :notification-message)}
|
||||||
|
children]
|
||||||
|
[:> button* {:variant "secondary"
|
||||||
|
:on-click handle-cancel} cancelLabel]
|
||||||
|
[:> button* {:variant (if (= variant "default") "primary" "destructive")
|
||||||
|
:on-click handle-accept} acceptLabel]]))
|
25
frontend/src/app/main/ui/ds/notifications/actionable.scss
Normal file
25
frontend/src/app/main/ui/ds/notifications/actionable.scss
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
@use "../_borders.scss" as *;
|
||||||
|
@use "../typography.scss" as t;
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
align-items: center;
|
||||||
|
background: var(--color-background-primary);
|
||||||
|
border-radius: $br-8;
|
||||||
|
border: $b-1 solid var(--color-background-quaternary);
|
||||||
|
display: grid;
|
||||||
|
gap: var(--sp-s);
|
||||||
|
grid-template-columns: 1fr auto auto;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: var(--sp-s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-message {
|
||||||
|
@include t.use-typography("body-small");
|
||||||
|
color: var(--color-foreground-primary);
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import Components from "@target/components";
|
||||||
|
|
||||||
|
const { Actionable } = Components;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Notifications/Actionable",
|
||||||
|
component: Actionable,
|
||||||
|
argTypes: {
|
||||||
|
variant: {
|
||||||
|
options: ["default", "error"],
|
||||||
|
control: { type: "select" },
|
||||||
|
},
|
||||||
|
acceptLabel: {
|
||||||
|
control: { type: "text" },
|
||||||
|
},
|
||||||
|
cancelLabel: {
|
||||||
|
control: { type: "text" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
variant: "default",
|
||||||
|
acceptLabel: "Update",
|
||||||
|
cancelLabel: "Dismiss",
|
||||||
|
},
|
||||||
|
render: ({ ...args }) => (
|
||||||
|
<Actionable {...args}>
|
||||||
|
Message for the notification <a href="#">more info</a>
|
||||||
|
</Actionable>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {};
|
||||||
|
|
||||||
|
export const Error = {
|
||||||
|
args: {
|
||||||
|
variant: "error",
|
||||||
|
},
|
||||||
|
};
|
70
frontend/src/app/main/ui/ds/product/autosaved_milestone.cljs
Normal file
70
frontend/src/app/main/ui/ds/product/autosaved_milestone.cljs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
(ns app.main.ui.ds.product.autosaved-milestone
|
||||||
|
(:require-macros
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.main.style :as stl])
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||||
|
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||||
|
[app.main.ui.ds.foundations.typography :as t]
|
||||||
|
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
||||||
|
[app.main.ui.ds.utilities.date :refer [date* valid-date?]]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(def ^:private schema:milestone
|
||||||
|
[:map
|
||||||
|
[:class {:optional true} :string]
|
||||||
|
[:active {:optional true} :boolean]
|
||||||
|
[:versionToggled {:optional true} :boolean]
|
||||||
|
[:label :string]
|
||||||
|
[:autosavedMessage :string]
|
||||||
|
[:snapshots [:vector [:fn valid-date?]]]])
|
||||||
|
|
||||||
|
(mf/defc autosaved-milestone*
|
||||||
|
{::mf/props :obj
|
||||||
|
::mf/schema schema:milestone}
|
||||||
|
[{:keys [class active versionToggled label autosavedMessage snapshots
|
||||||
|
onClickSnapshotMenu onToggleExpandSnapshots] :rest props}]
|
||||||
|
(let [class (d/append-class class (stl/css-case :milestone true :is-selected active))
|
||||||
|
props (mf/spread-props props {:class class :data-testid "milestone"})
|
||||||
|
|
||||||
|
handle-click-menu
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps onClickSnapshotMenu)
|
||||||
|
(fn [event]
|
||||||
|
(let [index (-> (dom/get-current-target event)
|
||||||
|
(dom/get-data "index")
|
||||||
|
(d/parse-integer))]
|
||||||
|
(when onClickSnapshotMenu
|
||||||
|
(onClickSnapshotMenu event index)))))]
|
||||||
|
[:> "div" props
|
||||||
|
[:> text* {:as "span" :typography t/body-small :class (stl/css :name)} label]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :snapshots)}
|
||||||
|
[:button {:class (stl/css :toggle-snapshots)
|
||||||
|
:aria-label (tr "workspace.versions.expand-snapshot")
|
||||||
|
:on-click onToggleExpandSnapshots}
|
||||||
|
[:> i/icon* {:icon-id i/clock :class (stl/css :icon-clock)}]
|
||||||
|
[:> text* {:as "span" :typography t/body-medium :class (stl/css :toggle-message)} autosavedMessage]
|
||||||
|
[:> i/icon* {:icon-id i/arrow :class (stl/css-case :icon-arrow true :icon-arrow-toggled versionToggled)}]]
|
||||||
|
|
||||||
|
(when versionToggled
|
||||||
|
(for [[idx d] (d/enumerate snapshots)]
|
||||||
|
[:div {:key (dm/str "entry-" idx)
|
||||||
|
:class (stl/css :version-entry)}
|
||||||
|
[:> date* {:date d :class (stl/css :date) :typography t/body-small}]
|
||||||
|
[:> icon-button* {:class (stl/css :entry-button)
|
||||||
|
:variant "ghost"
|
||||||
|
:icon "menu"
|
||||||
|
:aria-label (tr "workspace.versions.version-menu")
|
||||||
|
:data-index idx
|
||||||
|
:on-click handle-click-menu}]]))]]))
|
||||||
|
|
117
frontend/src/app/main/ui/ds/product/autosaved_milestone.scss
Normal file
117
frontend/src/app/main/ui/ds/product/autosaved_milestone.scss
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
@use "../_sizes.scss" as *;
|
||||||
|
@use "../_borders.scss" as *;
|
||||||
|
@use "../typography.scss" as t;
|
||||||
|
|
||||||
|
.milestone {
|
||||||
|
border: $b-1 solid var(--border-color, transparent);
|
||||||
|
border-radius: $br-8;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
background: var(--color-background-primary);
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas:
|
||||||
|
"avatar name button"
|
||||||
|
"avatar content button";
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
grid-template-columns: calc(var(--sp-xxl) + var(--sp-l)) 1fr auto;
|
||||||
|
|
||||||
|
padding: var(--sp-s) 0;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
column-gap: var(--sp-s);
|
||||||
|
|
||||||
|
&.is-selected,
|
||||||
|
&:hover {
|
||||||
|
--border-color: var(--color-accent-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
grid-area: avatar;
|
||||||
|
justify-self: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
@include t.use-typography("body-small");
|
||||||
|
grid-area: name;
|
||||||
|
color: var(--color-foreground-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-message {
|
||||||
|
@include t.use-typography("body-small");
|
||||||
|
grid-area: name;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date {
|
||||||
|
grid-area: content;
|
||||||
|
color: var(--date-color, var(--color-foreground-secondary));
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
--date-color: var(--color-accent-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.snapshots {
|
||||||
|
grid-area: content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.milestone-buttons {
|
||||||
|
grid-area: button;
|
||||||
|
display: flex;
|
||||||
|
padding-right: var(--sp-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-entry {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
grid-template-areas: "content button";
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
--date-color: var(--color-accent-primary);
|
||||||
|
--display-button: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-snapshots {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--color-foreground-secondary);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: var(--sp-xs);
|
||||||
|
align-items: flex-end;
|
||||||
|
margin: 0;
|
||||||
|
margin-top: var(--sp-xxs);
|
||||||
|
margin-bottom: var(--sp-s);
|
||||||
|
padding: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-accent-primary);
|
||||||
|
--icon-stroke-color: var(--color-accent-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-clock {
|
||||||
|
--icon-stroke-color: var(--color-accent-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-arrow {
|
||||||
|
--icon-stroke-color: var(--icon-stroke-color, var(--color-foreground-secondary));
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-arrow-toggled {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry-button {
|
||||||
|
visibility: var(--display-button, hidden);
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import Components from "@target/components";
|
||||||
|
|
||||||
|
const { AutosavedMilestone } = Components;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Product/Milestones/Autosaved",
|
||||||
|
component: AutosavedMilestone,
|
||||||
|
|
||||||
|
argTypes: {
|
||||||
|
label: {
|
||||||
|
control: { type: "text" },
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
control: { type: "boolean" },
|
||||||
|
},
|
||||||
|
autosaved: {
|
||||||
|
control: { type: "boolean" },
|
||||||
|
},
|
||||||
|
versionToggled: {
|
||||||
|
control: { type: "boolean" },
|
||||||
|
},
|
||||||
|
snapshots: {
|
||||||
|
control: { type: "object" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
label: "Milestone 1",
|
||||||
|
active: false,
|
||||||
|
versionToggled: false,
|
||||||
|
snapshots: [1737452413841, 1737452422063, 1737452431603],
|
||||||
|
autosavedMessage: "3 autosave versions",
|
||||||
|
},
|
||||||
|
render: ({ ...args }) => {
|
||||||
|
const user = {
|
||||||
|
name: args.userName,
|
||||||
|
avatar: args.userAvatar,
|
||||||
|
color: args.userColor,
|
||||||
|
};
|
||||||
|
return <AutosavedMilestone user={user} {...args} />;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {};
|
46
frontend/src/app/main/ui/ds/product/avatar.cljs
Normal file
46
frontend/src/app/main/ui/ds/product/avatar.cljs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
(ns app.main.ui.ds.product.avatar
|
||||||
|
(:require-macros
|
||||||
|
[app.main.style :as stl])
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.util.avatars :as avatars]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(def ^:private schema:avatar
|
||||||
|
[:map
|
||||||
|
[:class {:optional true} :string]
|
||||||
|
[:tag {:optional true} :string]
|
||||||
|
[:name {:optional true} [:maybe :string]]
|
||||||
|
[:url {:optional true} [:maybe :string]]
|
||||||
|
[:color {:optional true} [:maybe :string]]
|
||||||
|
[:selected {:optional true} :boolean]
|
||||||
|
[:variant {:optional true}
|
||||||
|
[:maybe [:enum "S" "M" "L"]]]])
|
||||||
|
|
||||||
|
(mf/defc avatar*
|
||||||
|
{::mf/props :obj
|
||||||
|
::mf/schema schema:avatar}
|
||||||
|
|
||||||
|
[{:keys [tag class name color url selected variant] :rest props}]
|
||||||
|
(let [variant (or variant "S")
|
||||||
|
url (if (and (some? url) (d/not-empty? url))
|
||||||
|
url
|
||||||
|
(avatars/generate {:name name :color color}))]
|
||||||
|
[:> (or tag "div")
|
||||||
|
{:class (d/append-class
|
||||||
|
class
|
||||||
|
(stl/css-case :avatar true
|
||||||
|
:avatar-small (= variant "S")
|
||||||
|
:avatar-medium (= variant "M")
|
||||||
|
:avatar-large (= variant "L")
|
||||||
|
:is-selected selected))
|
||||||
|
:style {"--avatar-color" color}
|
||||||
|
:title name}
|
||||||
|
[:div {:class (stl/css :avatar-image)}
|
||||||
|
[:img {:alt name :src url}]]]))
|
47
frontend/src/app/main/ui/ds/product/avatar.scss
Normal file
47
frontend/src/app/main/ui/ds/product/avatar.scss
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
@use "../_sizes.scss" as *;
|
||||||
|
@use "../_borders.scss" as *;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
border-radius: $br-circle;
|
||||||
|
overflow: hidden;
|
||||||
|
border: $b-1 solid var(--border-color, none);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-small {
|
||||||
|
width: $sz-24;
|
||||||
|
height: $sz-24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-medium {
|
||||||
|
width: $sz-32;
|
||||||
|
height: $sz-32;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-large {
|
||||||
|
width: $sz-40;
|
||||||
|
height: $sz-40;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-image {
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: $br-circle;
|
||||||
|
background-color: var(--avatar-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-selected {
|
||||||
|
--border-color: var(--color-accent-primary);
|
||||||
|
padding: var(--sp-xxs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
--border-color: var(--color-accent-primary);
|
||||||
|
}
|
||||||
|
}
|
67
frontend/src/app/main/ui/ds/product/avatar.stories.jsx
Normal file
67
frontend/src/app/main/ui/ds/product/avatar.stories.jsx
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import Components from "@target/components";
|
||||||
|
|
||||||
|
const { Avatar } = Components;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Product/Avatar",
|
||||||
|
component: Avatar,
|
||||||
|
argTypes: {
|
||||||
|
name: {
|
||||||
|
control: { type: "text" },
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
control: { type: "text" },
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
control: { type: "color" },
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
options: ["S", "M", "L"],
|
||||||
|
control: { type: "select" },
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
control: { type: "boolean" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
name: "Ada Lovelace",
|
||||||
|
url: "/images/avatar-blue.jpg",
|
||||||
|
color: "#79d4ff",
|
||||||
|
variant: "S",
|
||||||
|
selected: false,
|
||||||
|
},
|
||||||
|
render: ({ ...args }) => <Avatar profile={{ fullname: "TEST" }} {...args} />,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {};
|
||||||
|
|
||||||
|
export const NoURL = {
|
||||||
|
args: {
|
||||||
|
url: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Small = {
|
||||||
|
args: {
|
||||||
|
variant: "S",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Medium = {
|
||||||
|
args: {
|
||||||
|
variant: "M",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Large = {
|
||||||
|
args: {
|
||||||
|
variant: "L",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Selected = {
|
||||||
|
args: {
|
||||||
|
selected: true,
|
||||||
|
},
|
||||||
|
};
|
32
frontend/src/app/main/ui/ds/product/cta.cljs
Normal file
32
frontend/src/app/main/ui/ds/product/cta.cljs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
;; 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.ds.product.cta
|
||||||
|
(:require-macros
|
||||||
|
[app.main.style :as stl])
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.main.ui.ds.foundations.typography :as t]
|
||||||
|
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(def ^:private schema:cta
|
||||||
|
[:map
|
||||||
|
[:class {:optional true} :string]
|
||||||
|
[:title :string]])
|
||||||
|
|
||||||
|
(mf/defc cta*
|
||||||
|
{::mf/props :obj
|
||||||
|
::mf/schema schema:cta}
|
||||||
|
[{:keys [class title children] :rest props}]
|
||||||
|
|
||||||
|
(let [class (d/append-class class (stl/css :cta))
|
||||||
|
props (mf/spread-props props {:class class :data-testid "cta"})]
|
||||||
|
[:> "div" props
|
||||||
|
[:div {:class (stl/css :cta-title)}
|
||||||
|
[:> text* {:as "span" :typography t/headline-small :class (stl/css :placeholder-title)} title]]
|
||||||
|
[:div {:class (stl/css :cta-message)}
|
||||||
|
children]]))
|
21
frontend/src/app/main/ui/ds/product/cta.scss
Normal file
21
frontend/src/app/main/ui/ds/product/cta.scss
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
//
|
||||||
|
// Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
@use "../colors.scss" as *;
|
||||||
|
@use "../_sizes.scss" as *;
|
||||||
|
@use "../_borders.scss" as *;
|
||||||
|
@use "../typography.scss" as t;
|
||||||
|
|
||||||
|
.cta {
|
||||||
|
border-radius: $br-8;
|
||||||
|
border: $b-1 solid var(--color-accent-primary-muted);
|
||||||
|
background: var(--color-accent-select);
|
||||||
|
padding: var(--sp-m);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-title {
|
||||||
|
color: var(--color-foreground-primary);
|
||||||
|
}
|
34
frontend/src/app/main/ui/ds/product/cta.stories.jsx
Normal file
34
frontend/src/app/main/ui/ds/product/cta.stories.jsx
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import Components from "@target/components";
|
||||||
|
|
||||||
|
const { Cta } = Components;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Product/CTA",
|
||||||
|
component: Cta,
|
||||||
|
argTypes: {
|
||||||
|
title: {
|
||||||
|
control: { type: "text" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
title: "Autosaved versions will be kept for 7 days.",
|
||||||
|
},
|
||||||
|
render: ({ ...args }) => (
|
||||||
|
<Cta {...args}>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: "0.75rem",
|
||||||
|
color: "var(--color-foreground-secondary)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
If you’d like to increase this limit, write to us at{" "}
|
||||||
|
<a style={{ color: "var(--color-accent-primary)" }} href="#">
|
||||||
|
support@penpot.app
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</Cta>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {};
|
77
frontend/src/app/main/ui/ds/product/user_milestone.cljs
Normal file
77
frontend/src/app/main/ui/ds/product/user_milestone.cljs
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
;; 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.ds.product.user-milestone
|
||||||
|
(:require-macros
|
||||||
|
[app.main.style :as stl])
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||||
|
[app.main.ui.ds.controls.input :refer [input*]]
|
||||||
|
[app.main.ui.ds.foundations.typography :as t]
|
||||||
|
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
||||||
|
[app.main.ui.ds.product.avatar :refer [avatar*]]
|
||||||
|
[app.main.ui.ds.utilities.date :refer [valid-date?]]
|
||||||
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[app.util.time :as dt]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(def ^:private schema:milestone
|
||||||
|
[:map
|
||||||
|
[:class {:optional true} :string]
|
||||||
|
[:active {:optional true} :boolean]
|
||||||
|
[:editing {:optional true} :boolean]
|
||||||
|
[:user
|
||||||
|
[:map
|
||||||
|
[:name {:optional true} [:maybe :string]]
|
||||||
|
[:avatar {:optional true} [:maybe :string]]
|
||||||
|
[:color {:optional true} [:maybe :string]]]]
|
||||||
|
[:label :string]
|
||||||
|
[:date [:fn valid-date?]]
|
||||||
|
[:onOpenMenu {:optional true} [:maybe [:fn fn?]]]
|
||||||
|
[:onFocusInput {:optional true} [:maybe [:fn fn?]]]
|
||||||
|
[:onBlurInput {:optional true} [:maybe [:fn fn?]]]
|
||||||
|
[:onKeyDownInput {:optional true} [:maybe [:fn fn?]]]])
|
||||||
|
|
||||||
|
(mf/defc user-milestone*
|
||||||
|
{::mf/props :obj
|
||||||
|
::mf/schema schema:milestone}
|
||||||
|
[{:keys [class active editing user label date
|
||||||
|
onOpenMenu onFocusInput onBlurInput onKeyDownInput] :rest props}]
|
||||||
|
(let [class (d/append-class class (stl/css-case :milestone true :is-selected active))
|
||||||
|
props (mf/spread-props props {:class class :data-testid "milestone"})
|
||||||
|
date (cond-> date (not (dt/datetime? date)) dt/datetime)
|
||||||
|
time (dt/timeago date)]
|
||||||
|
[:> "div" props
|
||||||
|
[:> avatar* {:name (obj/get user "name")
|
||||||
|
:url (obj/get user "avatar")
|
||||||
|
:color (obj/get user "color")
|
||||||
|
:variant "S" :class (stl/css :avatar)}]
|
||||||
|
|
||||||
|
(if editing
|
||||||
|
[:> input*
|
||||||
|
{:class (stl/css :name-input)
|
||||||
|
:variant "seamless"
|
||||||
|
:default-value label
|
||||||
|
:auto-focus true
|
||||||
|
:on-focus onFocusInput
|
||||||
|
:on-blur onBlurInput
|
||||||
|
:on-key-down onKeyDownInput}]
|
||||||
|
[:> text* {:as "span" :typography t/body-small :class (stl/css :name)} label])
|
||||||
|
|
||||||
|
[:*
|
||||||
|
[:time {:dateTime (dt/format date :iso)
|
||||||
|
:class (stl/css :date)} time]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :milestone-buttons)}
|
||||||
|
[:> icon-button* {:class (stl/css :menu-button)
|
||||||
|
:variant "ghost"
|
||||||
|
:icon "menu"
|
||||||
|
:aria-label (tr "workspace.versions.version-menu")
|
||||||
|
:on-click onOpenMenu}]]]]))
|
||||||
|
|
||||||
|
|
64
frontend/src/app/main/ui/ds/product/user_milestone.scss
Normal file
64
frontend/src/app/main/ui/ds/product/user_milestone.scss
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
@use "../_sizes.scss" as *;
|
||||||
|
@use "../_borders.scss" as *;
|
||||||
|
@use "../typography.scss" as t;
|
||||||
|
|
||||||
|
.milestone {
|
||||||
|
border: $b-1 solid var(--border-color, transparent);
|
||||||
|
border-radius: $br-8;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
background: var(--color-background-primary);
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas:
|
||||||
|
"avatar name button"
|
||||||
|
"avatar content button";
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
grid-template-columns: calc(var(--sp-xxl) + var(--sp-l)) 1fr auto;
|
||||||
|
|
||||||
|
padding: var(--sp-s) 0;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
column-gap: var(--sp-s);
|
||||||
|
|
||||||
|
&.is-selected,
|
||||||
|
&:hover {
|
||||||
|
--border-color: var(--color-accent-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.name-input {
|
||||||
|
grid-area: name;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
grid-area: avatar;
|
||||||
|
justify-self: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
grid-area: name;
|
||||||
|
color: var(--color-foreground-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.date {
|
||||||
|
@include t.use-typography("body-small");
|
||||||
|
grid-area: content;
|
||||||
|
color: var(--color-foreground-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.snapshots {
|
||||||
|
grid-area: content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.milestone-buttons {
|
||||||
|
grid-area: button;
|
||||||
|
display: flex;
|
||||||
|
padding-right: var(--sp-xs);
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import Components from "@target/components";
|
||||||
|
|
||||||
|
const { UserMilestone } = Components;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Product/Milestones/User",
|
||||||
|
component: UserMilestone,
|
||||||
|
|
||||||
|
argTypes: {
|
||||||
|
userName: {
|
||||||
|
control: { type: "text" },
|
||||||
|
},
|
||||||
|
userAvatar: {
|
||||||
|
control: { type: "text" },
|
||||||
|
},
|
||||||
|
userColor: {
|
||||||
|
control: { type: "color" },
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
control: { type: "text" },
|
||||||
|
},
|
||||||
|
date: {
|
||||||
|
control: { type: "date" },
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
control: { type: "boolean" },
|
||||||
|
},
|
||||||
|
editing: {
|
||||||
|
control: { type: "boolean" },
|
||||||
|
},
|
||||||
|
autosaved: {
|
||||||
|
control: { type: "boolean" },
|
||||||
|
},
|
||||||
|
versionToggled: {
|
||||||
|
control: { type: "boolean" },
|
||||||
|
},
|
||||||
|
snapshots: {
|
||||||
|
control: { type: "object" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
label: "Milestone 1",
|
||||||
|
userName: "Ada Lovelace",
|
||||||
|
userAvatar: "/images/avatar-blue.jpg",
|
||||||
|
userColor: "#79d4ff",
|
||||||
|
date: 1735686000000,
|
||||||
|
active: false,
|
||||||
|
editing: false,
|
||||||
|
},
|
||||||
|
render: ({ ...args }) => {
|
||||||
|
const user = {
|
||||||
|
name: args.userName,
|
||||||
|
avatar: args.userAvatar,
|
||||||
|
color: args.userColor,
|
||||||
|
};
|
||||||
|
return <UserMilestone user={user} {...args} />;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {};
|
42
frontend/src/app/main/ui/ds/utilities/date.cljs
Normal file
42
frontend/src/app/main/ui/ds/utilities/date.cljs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
(ns app.main.ui.ds.utilities.date
|
||||||
|
(:require-macros
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.main.style :as stl])
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.main.ui.ds.foundations.typography :as t]
|
||||||
|
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
||||||
|
[app.util.time :as dt]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(defn valid-date?
|
||||||
|
[date]
|
||||||
|
(or (dt/datetime? date) (number? date)))
|
||||||
|
|
||||||
|
(def ^:private schema:date
|
||||||
|
[:map
|
||||||
|
[:class {:optional true} :string]
|
||||||
|
[:as {:optional true} :string]
|
||||||
|
[:date [:fn valid-date?]]
|
||||||
|
[:selected {:optional true} :boolean]
|
||||||
|
[:typography {:optional true} :string]])
|
||||||
|
|
||||||
|
(mf/defc date*
|
||||||
|
{::mf/props :obj
|
||||||
|
::mf/schema schema:date}
|
||||||
|
[{:keys [class date selected typography] :rest props}]
|
||||||
|
(let [class (d/append-class class (stl/css-case :date true :is-selected selected))
|
||||||
|
date (cond-> date (not (dt/datetime? date)) dt/datetime)
|
||||||
|
typography (or typography t/body-medium)]
|
||||||
|
[:> text* {:as "time" :typography typography :class class :dateTime (dt/format date :iso)}
|
||||||
|
(dm/str
|
||||||
|
(dt/format date :date-full)
|
||||||
|
" . "
|
||||||
|
(dt/format date :time-24-simple)
|
||||||
|
"h")]))
|
13
frontend/src/app/main/ui/ds/utilities/date.scss
Normal file
13
frontend/src/app/main/ui/ds/utilities/date.scss
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
.date {
|
||||||
|
color: var(--date-color, var(--color-foreground-secondary));
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-selected {
|
||||||
|
color: var(--date-color, var(--color-accent-primary));
|
||||||
|
}
|
25
frontend/src/app/main/ui/ds/utilities/date.stories.jsx
Normal file
25
frontend/src/app/main/ui/ds/utilities/date.stories.jsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import Components from "@target/components";
|
||||||
|
|
||||||
|
const { Date } = Components;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Foundations/Utilities/Date",
|
||||||
|
component: Date,
|
||||||
|
argTypes: {
|
||||||
|
date: {
|
||||||
|
control: { type: "date" },
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
control: { type: "boolean" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
title: "Date",
|
||||||
|
date: 1735686000000,
|
||||||
|
selected: false,
|
||||||
|
},
|
||||||
|
render: ({ ...args }) => <Date {...args} />,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {};
|
|
@ -39,7 +39,8 @@
|
||||||
|
|
||||||
inline?
|
inline?
|
||||||
[:& inline-notification
|
[:& inline-notification
|
||||||
{:actions (:actions notification)
|
{:accept (:accept notification)
|
||||||
|
:cancel (:cancel notification)
|
||||||
:links (:links notification)
|
:links (:links notification)
|
||||||
:content (:content notification)}]
|
:content (:content notification)}]
|
||||||
|
|
||||||
|
|
|
@ -9,37 +9,28 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.uuid :as uuid]
|
|
||||||
[app.main.ui.components.link-button :as lb]
|
[app.main.ui.components.link-button :as lb]
|
||||||
|
[app.main.ui.ds.notifications.actionable :refer [actionable*]]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(mf/defc inline-notification
|
(mf/defc inline-notification
|
||||||
"They are persistent messages and report a special situation
|
"They are persistent messages and report a special situation
|
||||||
of the application and require user interaction to disappear."
|
of the application and require user interaction to disappear."
|
||||||
|
|
||||||
{::mf/props :obj}
|
{::mf/props :obj}
|
||||||
[{:keys [content actions links] :as props}]
|
[{:keys [content accept cancel links] :as props}]
|
||||||
[:aside {:class (stl/css :inline-notification)}
|
|
||||||
[:div {:class (stl/css :inline-text)}
|
|
||||||
|
|
||||||
content
|
[:> actionable* {:class (stl/css :new-inline)
|
||||||
|
:cancelLabel (:label cancel)
|
||||||
|
:onCancel (:callback cancel)
|
||||||
|
:acceptLabel (:label accept)
|
||||||
|
:onAccept (:callback accept)}
|
||||||
|
content
|
||||||
|
|
||||||
(when (some? links)
|
(when (some? links)
|
||||||
[:nav {:class (stl/css :link-nav)}
|
[:nav {:class (stl/css :link-nav)}
|
||||||
(for [[index link] (d/enumerate links)]
|
(for [[index link] (d/enumerate links)]
|
||||||
[:& lb/link-button {:key (dm/str "link-" index)
|
[:& lb/link-button {:key (dm/str "link-" index)
|
||||||
:class (stl/css :link)
|
:class (stl/css :link)
|
||||||
:on-click (:callback link)
|
:on-click (:callback link)
|
||||||
:value (:label 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)])]])
|
|
||||||
|
|
|
@ -6,73 +6,15 @@
|
||||||
|
|
||||||
@import "refactor/common-refactor.scss";
|
@import "refactor/common-refactor.scss";
|
||||||
|
|
||||||
.inline-notification {
|
.new-inline {
|
||||||
--inline-notification-bg-color: var(--alert-background-color-default);
|
|
||||||
--inline-notification-fg-color: var(--alert-text-foreground-color-default);
|
|
||||||
--inline-notification-border-color: var(--alert-border-color-default);
|
|
||||||
@include alertShadow;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: $s-72;
|
top: $s-72;
|
||||||
|
margin: auto;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr auto;
|
|
||||||
gap: $s-24;
|
|
||||||
min-height: $s-48;
|
min-height: $s-48;
|
||||||
min-width: $s-640;
|
min-width: $s-640;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
max-width: $s-960;
|
max-width: $s-960;
|
||||||
padding: $s-8;
|
|
||||||
margin-inline: auto;
|
|
||||||
border: $s-1 solid var(--inline-notification-border-color);
|
|
||||||
border-radius: $br-8;
|
|
||||||
z-index: $z-index-modal;
|
z-index: $z-index-modal;
|
||||||
background-color: var(--inline-notification-bg-color);
|
|
||||||
color: var(--inline-notification-fg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.inline-text {
|
|
||||||
@include bodySmallTypography;
|
|
||||||
align-self: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link-nav {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link {
|
|
||||||
@include bodySmallTypography;
|
|
||||||
margin: 0;
|
|
||||||
height: 100%;
|
|
||||||
color: var(--modal-link-foreground-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: none;
|
|
||||||
grid-auto-flow: column;
|
|
||||||
align-self: center;
|
|
||||||
gap: $s-8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn {
|
|
||||||
@extend .button-secondary;
|
|
||||||
@include uppercaseTitleTipography;
|
|
||||||
min-height: $s-32;
|
|
||||||
min-width: $s-32;
|
|
||||||
width: fit-content;
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
(ns app.main.ui.workspace.sidebar.versions
|
(ns app.main.ui.workspace.sidebar.versions
|
||||||
(:require-macros [app.main.style :as stl])
|
(:require-macros [app.main.style :as stl])
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.time :as ct]
|
[app.common.time :as ct]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
|
@ -20,6 +19,9 @@
|
||||||
[app.main.ui.components.select :refer [select]]
|
[app.main.ui.components.select :refer [select]]
|
||||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||||
|
[app.main.ui.ds.product.autosaved-milestone :refer [autosaved-milestone*]]
|
||||||
|
[app.main.ui.ds.product.cta :refer [cta*]]
|
||||||
|
[app.main.ui.ds.product.user-milestone :refer [user-milestone*]]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[app.util.keyboard :as kbd]
|
[app.util.keyboard :as kbd]
|
||||||
|
@ -51,9 +53,7 @@
|
||||||
|
|
||||||
(mf/defc version-entry
|
(mf/defc version-entry
|
||||||
[{:keys [entry profile on-restore-version on-delete-version on-rename-version editing?]}]
|
[{:keys [entry profile on-restore-version on-delete-version on-rename-version editing?]}]
|
||||||
(let [input-ref (mf/use-ref nil)
|
(let [show-menu? (mf/use-state false)
|
||||||
|
|
||||||
show-menu? (mf/use-state false)
|
|
||||||
|
|
||||||
handle-open-menu
|
handle-open-menu
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
|
@ -112,35 +112,16 @@
|
||||||
(st/emit! (dwv/update-version-state {:editing nil})))))]
|
(st/emit! (dwv/update-version-state {:editing nil})))))]
|
||||||
|
|
||||||
[:li {:class (stl/css :version-entry-wrap)}
|
[:li {:class (stl/css :version-entry-wrap)}
|
||||||
[:div {:class (stl/css :version-entry :is-snapshot)}
|
[:> user-milestone* {:label (:label entry)
|
||||||
[:img {:class (stl/css :version-entry-avatar)
|
:user #js {:name (:fullname profile)
|
||||||
:alt (:fullname profile)
|
:avatar (cfg/resolve-profile-photo-url profile)
|
||||||
:src (cfg/resolve-profile-photo-url profile)}]
|
:color (:color profile)}
|
||||||
|
:editing editing?
|
||||||
[:div {:class (stl/css :version-entry-data)}
|
:date (:created-at entry)
|
||||||
(if editing?
|
:onOpenMenu handle-open-menu
|
||||||
[:input {:class (stl/css :version-entry-name-edit)
|
:onFocusInput handle-name-input-focus
|
||||||
:type "text"
|
:onBlurInput handle-name-input-blur
|
||||||
:ref input-ref
|
:onKeyDownInput handle-name-input-key-down}]
|
||||||
:on-focus handle-name-input-focus
|
|
||||||
:on-blur handle-name-input-blur
|
|
||||||
:on-key-down handle-name-input-key-down
|
|
||||||
:auto-focus true
|
|
||||||
:default-value (:label entry)}]
|
|
||||||
|
|
||||||
[:p {:class (stl/css :version-entry-name)}
|
|
||||||
(:label entry)])
|
|
||||||
|
|
||||||
[:p {:class (stl/css :version-entry-time)}
|
|
||||||
(let [locale (mf/deref i18n/locale)
|
|
||||||
time (dt/timeago (:created-at entry) {:locale locale})]
|
|
||||||
[:span {:class (stl/css :date)} time])]]
|
|
||||||
|
|
||||||
[:> icon-button* {:class (stl/css :version-entry-options)
|
|
||||||
:variant "ghost"
|
|
||||||
:aria-label (tr "workspace.versions.version-menu")
|
|
||||||
:on-click handle-open-menu
|
|
||||||
:icon "menu"}]]
|
|
||||||
|
|
||||||
[:& dropdown {:show @show-menu? :on-close handle-close-menu}
|
[:& dropdown {:show @show-menu? :on-close handle-close-menu}
|
||||||
[:ul {:class (stl/css :version-options-dropdown)}
|
[:ul {:class (stl/css :version-options-dropdown)}
|
||||||
|
@ -158,6 +139,7 @@
|
||||||
[{:keys [index is-expanded entry on-toggle-expand on-pin-snapshot on-restore-snapshot]}]
|
[{:keys [index is-expanded entry on-toggle-expand on-pin-snapshot on-restore-snapshot]}]
|
||||||
|
|
||||||
(let [open-menu (mf/use-state nil)
|
(let [open-menu (mf/use-state nil)
|
||||||
|
entry-ref (mf/use-ref nil)
|
||||||
|
|
||||||
handle-toggle-expand
|
handle-toggle-expand
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
|
@ -180,51 +162,45 @@
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(let [node (dom/get-current-target event)
|
(let [node (dom/get-current-target event)
|
||||||
id (-> (dom/get-data node "id") uuid/uuid)]
|
id (-> (dom/get-data node "id") uuid/uuid)]
|
||||||
(when on-restore-snapshot (on-restore-snapshot id)))))]
|
(when on-restore-snapshot (on-restore-snapshot id)))))
|
||||||
|
|
||||||
[:li {:class (stl/css :version-entry-wrap)}
|
|
||||||
[:div {:class (stl/css-case :version-entry true
|
|
||||||
:is-autosave true
|
|
||||||
:is-expanded is-expanded)}
|
|
||||||
[:p {:class (stl/css :version-entry-name)}
|
|
||||||
(tr "workspace.versions.autosaved.version" (dt/format (:created-at entry) :date-full))]
|
|
||||||
|
|
||||||
[:button {:class (stl/css :version-entry-snapshots)
|
handle-open-snapshot-menu
|
||||||
:aria-label (tr "workspace.versions.expand-snapshot")
|
(mf/use-fn
|
||||||
:on-click handle-toggle-expand}
|
(mf/deps entry)
|
||||||
[:> i/icon* {:icon-id i/clock :class (stl/css :icon-clock)}]
|
(fn [event index]
|
||||||
(tr "workspace.versions.autosaved.entry" (count (:snapshots entry)))
|
(let [snapshot (nth (:snapshots entry) index)
|
||||||
[:> i/icon* {:icon-id i/arrow :class (stl/css :icon-arrow)}]]
|
current-bb (-> entry-ref mf/ref-val dom/get-bounding-rect :top)
|
||||||
|
target-bb (-> event dom/get-target dom/get-bounding-rect :top)
|
||||||
|
offset (+ (- target-bb current-bb) 32)]
|
||||||
|
(swap! open-menu assoc
|
||||||
|
:snapshot (:id snapshot)
|
||||||
|
:offset offset))))]
|
||||||
|
|
||||||
[:ul {:class (stl/css :version-snapshot-list)}
|
[:li {:ref entry-ref :class (stl/css :version-entry-wrap)}
|
||||||
(for [[idx snapshot] (d/enumerate (:snapshots entry))]
|
[:> autosaved-milestone*
|
||||||
[:li {:class (stl/css :version-snapshot-entry-wrapper)
|
{:label (tr "workspace.versions.autosaved.version"
|
||||||
:key (dm/str "snp-" idx)}
|
(dt/format (:created-at entry) :date-full))
|
||||||
[:div {:class (stl/css :version-snapshot-entry)}
|
:autosavedMessage (tr "workspace.versions.autosaved.entry" (count (:snapshots entry)))
|
||||||
(str
|
:snapshots (mapv :created-at (:snapshots entry))
|
||||||
(dt/format (:created-at snapshot) :date-full)
|
:versionToggled is-expanded
|
||||||
" . "
|
:onClickSnapshotMenu handle-open-snapshot-menu
|
||||||
(dt/format (:created-at snapshot) :time-24-simple))]
|
:onToggleExpandSnapshots handle-toggle-expand}]
|
||||||
|
|
||||||
[:> icon-button* {:variant "ghost"
|
[:& dropdown {:show (some? @open-menu)
|
||||||
:aria-label (tr "workspace.versions.snapshot-menu")
|
:on-close #(reset! open-menu nil)}
|
||||||
:on-click #(reset! open-menu snapshot)
|
[:ul {:class (stl/css :version-options-dropdown)
|
||||||
:icon "menu"
|
:style {"--offset" (dm/str (:offset @open-menu) "px")}}
|
||||||
:class (stl/css :version-snapshot-menu-btn)}]
|
[:li {:class (stl/css :menu-option)
|
||||||
|
:role "button"
|
||||||
[:& dropdown {:show (= @open-menu snapshot)
|
:data-id (dm/str (:snapshot @open-menu))
|
||||||
:on-close #(reset! open-menu nil)}
|
:on-click handle-restore-snapshot}
|
||||||
[:ul {:class (stl/css :version-options-dropdown)}
|
(tr "workspace.versions.button.restore")]
|
||||||
[:li {:class (stl/css :menu-option)
|
[:li {:class (stl/css :menu-option)
|
||||||
:role "button"
|
:role "button"
|
||||||
:data-id (dm/str (:id snapshot))
|
:data-id (dm/str (:snapshot @open-menu))
|
||||||
:on-click handle-restore-snapshot}
|
:on-click handle-pin-snapshot}
|
||||||
(tr "workspace.versions.button.restore")]
|
(tr "workspace.versions.button.pin")]]]]))
|
||||||
[:li {:class (stl/css :menu-option)
|
|
||||||
:role "button"
|
|
||||||
:data-id (dm/str (:id snapshot))
|
|
||||||
:on-click handle-pin-snapshot}
|
|
||||||
(tr "workspace.versions.button.pin")]]]])]]]))
|
|
||||||
|
|
||||||
(mf/defc versions-toolbox*
|
(mf/defc versions-toolbox*
|
||||||
[]
|
[]
|
||||||
|
@ -282,12 +258,10 @@
|
||||||
(ntf/dialog
|
(ntf/dialog
|
||||||
:content (tr "workspace.versions.restore-warning")
|
:content (tr "workspace.versions.restore-warning")
|
||||||
:controls :inline-actions
|
:controls :inline-actions
|
||||||
:actions [{:label (tr "workspace.updates.dismiss")
|
:cancel {:label (tr "workspace.updates.dismiss")
|
||||||
:type :secondary
|
:callback #(st/emit! (ntf/hide))}
|
||||||
:callback #(st/emit! (ntf/hide))}
|
:accept {:label (tr "labels.restore")
|
||||||
{:label (tr "labels.restore")
|
:callback #(st/emit! (dwv/restore-version id origin))}
|
||||||
:type :primary
|
|
||||||
:callback #(st/emit! (dwv/restore-version id origin))}]
|
|
||||||
:tag :restore-dialog))))
|
:tag :restore-dialog))))
|
||||||
|
|
||||||
handle-restore-version-pinned
|
handle-restore-version-pinned
|
||||||
|
@ -384,12 +358,9 @@
|
||||||
|
|
||||||
nil))])
|
nil))])
|
||||||
|
|
||||||
[:div {:class (stl/css :autosave-warning)}
|
[:> cta* {:title (tr "workspace.versions.warning.text" versions-stored-days)}
|
||||||
[:div {:class (stl/css :autosave-warning-text)}
|
[:> i18n/tr-html*
|
||||||
(tr "workspace.versions.warning.text" versions-stored-days)]
|
{:tag-name "div"
|
||||||
|
:class (stl/css :cta)
|
||||||
[:div {:class (stl/css :autosave-warning-subtext)}
|
:content (tr "workspace.versions.warning.subtext"
|
||||||
[:> i18n/tr-html*
|
"mailto:support@penpot.app")}]]])]))
|
||||||
{:tag-name "div"
|
|
||||||
:content (tr "workspace.versions.warning.subtext"
|
|
||||||
"mailto:support@penpot.app")}]]]])]))
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) KALEIDOS INC
|
// Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
@use "../../ds/typography.scss" as t;
|
||||||
@import "refactor/common-refactor.scss";
|
@import "refactor/common-refactor.scss";
|
||||||
|
|
||||||
.version-toolbox {
|
.version-toolbox {
|
||||||
|
@ -145,6 +146,7 @@
|
||||||
max-width: $s-200;
|
max-width: $s-200;
|
||||||
right: 0;
|
right: 0;
|
||||||
left: unset;
|
left: unset;
|
||||||
|
top: var(--offset);
|
||||||
.menu-option {
|
.menu-option {
|
||||||
@extend .dropdown-element-base;
|
@extend .dropdown-element-base;
|
||||||
}
|
}
|
||||||
|
@ -231,22 +233,10 @@
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.autosave-warning {
|
.cta {
|
||||||
display: flex;
|
@include t.use-typography("body-small");
|
||||||
flex-direction: column;
|
|
||||||
gap: $s-8;
|
|
||||||
padding: $s-16;
|
|
||||||
}
|
|
||||||
|
|
||||||
.autosave-warning-text {
|
|
||||||
color: var(--color-foreground-primary);
|
|
||||||
font-size: $fs-12;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.autosave-warning-subtext {
|
|
||||||
color: var(--color-foreground-secondary);
|
color: var(--color-foreground-secondary);
|
||||||
font-size: $fs-12;
|
|
||||||
a {
|
a {
|
||||||
color: var(--color-accent-primary);
|
color: var(--color-accent-primary);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue