0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-16 00:41:25 -05:00

♻️ Create shared notification utility component

This commit is contained in:
Xaviju 2025-02-03 13:11:59 +01:00 committed by Xaviju
parent 9660307f00
commit 5c32ec8cfa
9 changed files with 157 additions and 258 deletions

View file

@ -21,6 +21,7 @@
[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.notifications.actionable :refer [actionable*]]
[app.main.ui.ds.notifications.shared.notification-pill :refer [notification-pill*]]
[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*]]
@ -54,6 +55,7 @@
:Text text*
:TabSwitcher tab-switcher*
:Toast toast*
:NotificationPill notification-pill*
:Actionable actionable*
:TokenStatusIcon token-status-icon*
:Swatch swatch*

View 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.notifications.shared.notification-pill
(:require-macros
[app.main.style :as stl])
(:require
[app.main.ui.ds.foundations.assets.icon :as i]
[rumext.v2 :as mf]))
(defn icons-by-level
[level]
(case level
:info i/info
:warning i/msg-neutral
:error i/delete-text
:success i/status-tick
i/info))
(def ^:private schema:notification-pill
[:map
[:level [:enum :info :warning :error :success]]
[:type [:enum :toast :context]]])
(mf/defc notification-pill*
{::mf/props :obj
::mf/schema schema:notification-pill}
[{:keys [level type children]}]
(let [class (stl/css-case :notification-pill true
:type-toast (= type :toast)
:type-context (= type :context)
:level-warning (= level :warning)
:level-error (= level :error)
:level-success (= level :success)
:level-info (= level :info))
icon-id (icons-by-level level)]
[:div {:class class}
[:> i/icon* {:icon-id icon-id :class (stl/css :icon)}]
children]))

View file

@ -0,0 +1,68 @@
// 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 *;
.notification-pill {
@include use-typography("body-medium");
--notification-bg-color: var(--color-background-primary);
--notification-fg-color: var(--color-foreground-primary);
--notification-border-color: var(--color-background-quaternary);
--notification-padding: var(--sp-l);
--notification-icon-color: var(--color-foreground-secondary);
--notification-icon-margin: var(--sp-xxs);
background-color: var(--notification-bg-color);
border: $b-1 solid var(--notification-border-color);
border-radius: $br-8;
padding: var(--notification-padding);
display: flex;
gap: var(--sp-s);
color: var(--notification-fg-color);
}
.type-toast {
padding-inline-end: var(--sp-xxxl);
}
.level-info {
--notification-bg-color: var(--color-background-info);
--notification-fg-color: var(--color-foreground-primary);
--notification-border-color: var(--color-accent-info);
--notification-icon-color: var(--color-accent-info);
}
.level-error {
--notification-bg-color: var(--color-background-error);
--notification-fg-color: var(--color-foreground-primary);
--notification-border-color: var(--color-accent-error);
--notification-icon-color: var(--color-accent-error);
}
.level-warning {
--notification-bg-color: var(--color-background-warning);
--notification-fg-color: var(--color-foreground-warning);
--notification-border-color: var(--color-accent-warning);
--notification-icon-color: var(--color-accent-warning);
}
.level-success {
--notification-bg-color: var(--color-background-success);
--notification-fg-color: var(--color-foreground-success);
--notification-border-color: var(--color-accent-success);
--notification-icon-color: var(--color-accent-success);
}
.icon {
flex-shrink: 0;
color: var(--notification-icon-color);
margin-block-start: var(--notification-icon-margin);
}

View file

@ -9,37 +9,35 @@
[app.common.data.macros :as dm]
[app.main.style :as stl])
(:require
[app.common.data :as d]
[app.main.ui.ds.foundations.assets.icon :as i]
[app.main.ui.ds.notifications.shared.notification-pill :refer [notification-pill*]]
[rumext.v2 :as mf]))
(def ^:private icons-by-level
{"info" i/info
"warning" i/msg-neutral
"error" i/delete-text
"success" i/status-tick})
(def ^:private schema:toast
[:map
[:class {:optional true} :string]
[:level {:optional true}
[:maybe [:enum "info" "warning" "error" "success"]]]
[:type {:optional true} [:maybe [:enum :toast :context]]]
[:level {:optional true} [:maybe [:enum :info :warning :error :success]]]
[:on-close {:optional true} fn?]])
(mf/defc toast*
{::mf/props :obj
::mf/schema schema:toast}
[{:keys [class level children on-close] :rest props}]
(let [class (dm/str (stl/css-case :toast true
:toast-info (= level "info")
:toast-warning (= level "warning")
:toast-error (= level "error")
:toast-success (= level "success")) " " class)
icon-id (or (get icons-by-level level) i/msg-neutral)
props (mf/spread-props props {:class class})]
[{:keys [class level type children on-close] :rest props}]
(let [class (dm/str class " " (stl/css-case :toast true))
level (if (string? level)
(keyword level)
(d/nilv level :info))
type (or type :context)
props (mf/spread-props props {:class class
:role "alert"
:aria-live "polite"})]
[:> "aside" props
[:*
[:> i/icon* {:icon-id icon-id :class (stl/css :icon)}]
children
[:> notification-pill* {:level level :type type} children]
;; TODO: this should be a buttom from the DS, but this variant is not designed yet.
;; https://tree.taiga.io/project/penpot/task/8492
[:> "button" {:on-click on-close :aria-label "Close" :class (stl/css :close-button)} [:> i/icon* {:icon-id i/close}]]]]))
[:> "button" {:on-click on-close
:aria-label "Close"
:class (stl/css :close-button)}
[:> i/icon* {:icon-id i/close}]]]))

View file

@ -7,72 +7,33 @@
@use "../_sizes.scss" as *;
@use "../_borders.scss" as *;
@use "../typography.scss" as *;
@use "../spacing.scss" as *;
@use "../z-index.scss" as *;
.toast {
@include use-typography("body-medium");
--toast-bg-color: var(--color-background-primary);
--toast-fg-color: var(--color-foreground-primary);
--toast-border-color: var(--color-background-quaternary);
--toast-padding: var(--sp-l);
--toast-icon-color: var(--color-foreground-secondary);
--toast-icon-margin: var(--sp-xxs);
--toast-vertical-index: var(--z-index-notifications);
--toast-inset-block-start-position: var(--sp-l);
--toast-inset-inline-end-position: var(--sp-l);
min-inline-size: $sz-224;
max-inline-size: $sz-480;
background-color: var(--toast-bg-color);
border: $b-1 solid var(--toast-border-color);
border-radius: $br-8;
padding: var(--toast-padding);
display: inline-grid;
grid-template-columns: auto 1fr auto;
column-gap: var(--sp-s);
align-items: flex-start;
color: var(--toast-fg-color);
}
.toast-info {
--toast-bg-color: var(--color-background-info);
--toast-fg-color: var(--color-foreground-primary);
--toast-border-color: var(--color-accent-info);
--toast-icon-color: var(--color-accent-info);
}
.toast-error {
--toast-bg-color: var(--color-background-error);
--toast-fg-color: var(--color-foreground-primary);
--toast-border-color: var(--color-accent-error);
--toast-icon-color: var(--color-accent-error);
}
.toast-warning {
--toast-bg-color: var(--color-background-warning);
--toast-fg-color: var(--color-foreground-primary);
--toast-border-color: var(--color-accent-warning);
--toast-icon-color: var(--color-accent-warning);
}
.toast-success {
--toast-bg-color: var(--color-background-success);
--toast-fg-color: var(--color-foreground-primary);
--toast-border-color: var(--color-accent-success);
--toast-icon-color: var(--color-accent-success);
}
.icon {
color: var(--toast-icon-color);
margin-block-start: var(--toast-icon-margin);
display: block;
position: fixed;
inset-block-start: var(--toast-inset-block-start-position);
inset-inline-end: var(--toast-inset-inline-end-position);
z-index: var(--toast-vertical-index);
}
.close-button {
appearance: none;
width: $sz-16;
height: $sz-16;
display: inline-grid;
place-content: center;
position: absolute;
top: var(--sp-l);
right: var(--sp-l);
background: none;
border: none;
background: var(--toast-bg-color);
color: var(--toast-icon-color);
}

View file

@ -6,6 +6,7 @@
import * as React from "react";
import Components from "@target/components";
import { action } from "@storybook/addon-actions";
const { Toast } = Components;
@ -19,13 +20,12 @@ export default {
},
args: {
children: "Lorem ipsum",
onClose: () => {
alert("Close callback");
},
type: "toast",
onClose: action("on-close"),
},
parameters: {
controls: {
exclude: ["onClose"],
exclude: ["onClose", "type"],
},
},
render: ({ ...args }) => <Toast {...args} />,

View file

@ -8,9 +8,9 @@
(:require
[app.main.data.notifications :as ntf]
[app.main.store :as st]
[app.main.ui.ds.notifications.toast :refer [toast*]]
[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]]
[okulary.core :as l]
[rumext.v2 :as mf]))
@ -26,16 +26,16 @@
inline? (or (= :inline (:type notification))
(= :floating (:position notification)))
toast? (or (= :toast (:type notification))
(some? (:timeout notification)))]
(some? (:timeout notification)))
content (or (:content notification) "")]
(when notification
(cond
toast?
[:& toast-notification
[:> toast*
{:level (or (:level notification) :info)
:links (:links notification)
:on-close on-close
:content (:content notification)}]
:type (:type notification)
:on-close on-close} content]
inline?
[:& inline-notification
@ -51,8 +51,7 @@
:content (:content notification)}]
:else
[:& toast-notification
[:> toast*
{:level (or (:level notification) :info)
:links (:links notification)
:on-close on-close
:content (:content notification)}]))))
:type (:type notification)
:on-close on-close} content]))))

View file

@ -1,71 +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
(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 (stl/css :icon)))
(def ^:private error-icon
(i/icon-xref :delete-text (stl/css :icon)))
(def ^:private success-icon
(i/icon-xref :status-tick (stl/css :icon)))
(def ^:private info-icon
(i/icon-xref :help (stl/css :icon)))
(def ^:private close-icon
(i/icon-xref :close (stl/css :close-icon)))
(defn get-icon-by-level
[level]
(case level
: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 [level content on-close links] :as props}]
[:aside {:class (stl/css-case :toast-notification true
:warning (= level :warning)
:error (= level :error)
:success (= level :success)
:info (= level :info))}
(get-icon-by-level level)
[: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

@ -1,100 +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";
.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-228;
max-width: $s-400;
padding: $s-8;
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;
}
.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 {
--toast-notification-bg-color: var(--alert-background-color-info);
--toast-notification-fg-color: var(--alert-text-foreground-color-info);
--toast-notification-icon-color: var(--alert-icon-foreground-color-info);
--toast-notification-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 {
display: inline;
}
.link {
@include bodySmallTypography;
color: var(--modal-link-foreground-color);
margin: 0;
}
.icon {
@extend .button-icon;
align-self: flex-start;
stroke: var(--toast-notification-icon-color);
}
.text {
@include bodySmallTypography;
align-self: center;
}
.btn-close {
@include buttonStyle;
align-self: flex-start;
width: $s-16;
margin: 0;
padding: 0;
background-color: transparent;
}
.close-icon {
@extend .button-icon;
stroke: var(--toast-notification-icon-color);
}