0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-27 15:11:26 -05:00

Implement button* and icon-button* for the design system

This commit is contained in:
Belén Albeza 2024-07-23 15:42:02 +02:00
parent 3eaa997145
commit 60cba6c9f3
10 changed files with 425 additions and 1 deletions

View file

@ -6,6 +6,8 @@
(ns app.main.ui.ds
(:require
[app.main.ui.ds.buttons.button :refer [button*]]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.foundations.assets.icon :refer [icon* icon-list]]
[app.main.ui.ds.foundations.assets.raw-svg :refer [raw-svg* raw-svg-list]]
[app.main.ui.ds.foundations.typography :refer [typography-list]]
@ -16,8 +18,10 @@
(def default
"A export used for storybook"
#js {:Heading heading*
#js {:Button button*
:Heading heading*
:Icon icon*
:IconButton icon-button*
:Loader loader*
:RawSvg raw-svg*
:Text text*

View file

@ -0,0 +1,10 @@
// 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 "./utils.scss" as *;
// TODO: create actual tokens once we have them from design
$br-8: px2rem(8);

View file

@ -0,0 +1,10 @@
// 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 "./utils.scss" as *;
// TODO: create actual tokens once we have them from design
$sz-32: px2rem(32);

View file

@ -0,0 +1,132 @@
// 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 "../_sizes.scss" as *;
@use "../utils.scss" as *;
%base-button {
--button-bg-color: initial;
--button-fg-color: initial;
--button-hover-bg-color: initial;
--button-hover-fg-color: initial;
--button-active-bg-color: initial;
--button-disabled-bg-color: initial;
--button-disabled-fg-color: initial;
--button-border-color: var(--button-bg-color);
--button-focus-inner-ring-color: initial;
--button-focus-outer-ring-color: initial;
appearance: none;
height: $sz-32;
border: none;
border-radius: $br-8;
background: var(--button-bg-color);
color: var(--button-fg-color);
border: 1px solid var(--button-border-color);
&:hover {
--button-bg-color: var(--button-hover-bg-color);
--button-fg-color: var(--button-hover-fg-color);
}
&:active {
--button-bg-color: var(--button-active-bg-color);
}
&:focus-visible {
outline: var(--button-focus-inner-ring-color) solid #{px2rem(2)};
outline-offset: -#{px2rem(3)};
--button-border-color: var(--button-focus-outer-ring-color);
--button-fg-color: var(--button-focus-fg-color);
}
&:disabled {
--button-bg-color: var(--button-disabled-bg-color);
--button-fg-color: var(--button-disabled-fg-color);
}
}
%base-button-primary {
--button-bg-color: var(--color-accent-primary);
--button-fg-color: var(--color-background-secondary);
--button-hover-bg-color: var(--color-accent-tertiary);
--button-hover-fg-color: var(--color-background-secondary);
--button-active-bg-color: var(--color-accent-tertiary);
--button-disabled-bg-color: var(--color-accent-primary-muted);
--button-disabled-fg-color: var(--color-background-secondary);
--button-focus-bg-color: var(--color-accent-primary);
--button-focus-fg-color: var(--color-background-secondary);
--button-focus-inner-ring-color: var(--color-background-secondary);
--button-focus-outer-ring-color: var(--color-accent-primary);
&:active {
box-shadow: inset 0 0 #{px2rem(10)} #{px2rem(2)} rgba(0, 0, 0, 0.2);
}
}
%base-button-secondary {
--button-bg-color: var(--color-background-tertiary);
--button-fg-color: var(--color-foreground-secondary);
--button-hover-bg-color: var(--color-background-tertiary);
--button-hover-fg-color: var(--color-accent-primary);
--button-active-bg-color: var(--color-background-quaternary);
--button-disabled-bg-color: transparent;
--button-disabled-fg-color: var(--color-foreground-secondary);
--button-focus-bg-color: var(--color-background-tertiary);
--button-focus-fg-color: var(--color-foreground-primary);
--button-focus-inner-ring-color: var(--color-background-secondary);
--button-focus-outer-ring-color: var(--color-accent-primary);
}
%base-button-ghost {
--button-bg-color: transparent;
--button-fg-color: var(--color-foreground-secondary);
--button-hover-bg-color: var(--color-background-tertiary);
--button-hover-fg-color: var(--color-accent-primary);
--button-active-bg-color: var(--color-background-quaternary);
--button-disabled-bg-color: transparent;
--button-disabled-fg-color: var(--color-accent-primary-muted);
--button-focus-bg-color: transparent;
--button-focus-fg-color: var(--color-foreground-secondary);
--button-focus-inner-ring-color: transparent;
--button-focus-outer-ring-color: var(--color-accent-primary);
}
%base-button-destructive {
--button-bg-color: var(--color-accent-error);
--button-fg-color: var(--color-foreground-primary);
--button-hover-bg-color: var(--color-background-error);
--button-hover-fg-color: var(--color-foreground-primary);
--button-active-bg-color: var(--color-accent-error);
--button-disabled-bg-color: var(--color-background-error);
--button-disabled-fg-color: var(--color-accent-error);
--button-focus-bg-color: var(--color-accent-error);
--button-focus-fg-color: var(--color-foreground-primary);
--button-focus-inner-ring-color: var(--color-background-primary);
--button-focus-outer-ring-color: var(--color-accent-primary);
&:active {
box-shadow: inset 0 0 #{px2rem(10)} #{px2rem(2)} rgba(0, 0, 0, 0.2);
}
}

View file

@ -0,0 +1,30 @@
;; 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.buttons.button
(:require-macros
[app.common.data.macros :as dm]
[app.main.style :as stl])
(:require
[app.main.ui.ds.foundations.assets.icon :refer [icon*]]
[rumext.v2 :as mf]))
(def button-variants (set '("primary" "secondary" "ghost" "destructive")))
(mf/defc button*
{::mf/props :obj}
[{:keys [variant icon children class] :rest props}]
(assert (or (nil? variant) (contains? button-variants variant) "expected valid variant"))
(let [variant (or variant "primary")
class (dm/str class " " (stl/css-case :button true
:button-primary (= variant "primary")
:button-secondary (= variant "secondary")
:button-ghost (= variant "ghost")
:button-destructive (= variant "destructive")))
props (mf/spread-props props {:class class})]
[:> "button" props
(when icon [:> icon* {:id icon :size "m"}])
[:span {:class (stl/css :label-wrapper)} children]]))

View file

@ -0,0 +1,35 @@
// 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 "../typography.scss" as *;
@use "./buttons" as *;
.button {
@extend %base-button;
@include use-typography("headline-small");
padding: 0 var(--sp-m);
display: inline-flex;
align-items: center;
column-gap: var(--sp-xs);
}
.button-primary {
@extend %base-button-primary;
}
.button-secondary {
@extend %base-button-secondary;
}
.button-ghost {
@extend %base-button-ghost;
}
.button-destructive {
@extend %base-button-destructive;
}

View file

@ -0,0 +1,74 @@
// 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 * as React from "react";
import Components from "@target/components";
const { Button } = Components;
const { icons } = Components.meta;
const iconList = [
...Object.entries(icons)
.map(([_, value]) => value)
.sort(),
];
export default {
title: "Buttons/Button",
component: Components.Button,
argTypes: {
icon: {
options: iconList,
control: { type: "select" },
},
disabled: { control: "boolean" },
variant: {
options: ["primary", "secondary", "ghost", "destructive"],
control: { type: "select" },
},
},
args: {
children: "Lorem ipsum",
disabled: false,
variant: undefined,
},
parameters: {
controls: { exclude: ["children"] },
},
render: ({ ...args }) => <Button {...args} />,
};
export const Default = {};
export const WithIcon = {
args: {
icon: "effects",
},
};
export const Primary = {
args: {
variant: "primary",
},
};
export const Secondary = {
args: {
variant: "secondary",
},
};
export const Ghost = {
args: {
variant: "ghost",
},
};
export const Destructive = {
args: {
variant: "destructive",
},
};

View file

@ -0,0 +1,30 @@
;; 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.buttons.icon-button
(:require-macros
[app.common.data.macros :as dm]
[app.main.style :as stl])
(:require
[app.main.ui.ds.foundations.assets.icon :refer [icon*]]
[rumext.v2 :as mf]))
(def button-variants (set '("primary" "secondary" "ghost" "destructive")))
(mf/defc icon-button*
{::mf/props :obj}
[{:keys [class icon variant aria-label] :rest props}]
(assert (or (not variant) (contains? button-variants variant)) "invalid variant")
(assert (some? aria-label) "aria-label must be provided")
(assert (some? icon) "an icon id must be provided")
(let [variant (or variant "primary")
class (dm/str class " " (stl/css-case :icon-button true
:icon-button-primary (= variant "primary")
:icon-button-secondary (= variant "secondary")
:icon-button-ghost (= variant "ghost")
:icon-button-destructive (= variant "destructive")))
props (mf/spread-props props {:class class :title aria-label})]
[:> "button" props [:> icon* {:id icon :aria-label aria-label}]]))

View file

@ -0,0 +1,33 @@
// 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 "../typography.scss" as *;
@use "../_sizes.scss" as *;
@use "./buttons" as *;
.icon-button {
@extend %base-button;
width: #{$sz-32};
display: flex;
justify-content: center;
align-items: center;
}
.icon-button-primary {
@extend %base-button-primary;
}
.icon-button-secondary {
@extend %base-button-secondary;
}
.icon-button-ghost {
@extend %base-button-ghost;
}
.icon-button-destructive {
@extend %base-button-destructive;
}

View file

@ -0,0 +1,66 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
import * as React from "react";
import Components from "@target/components";
const { IconButton } = Components;
const { icons } = Components.meta;
const iconList = [
...Object.entries(icons)
.map(([_, value]) => value)
.sort(),
];
export default {
title: "Buttons/IconButton",
component: Components.IconButton,
argTypes: {
icon: {
options: iconList,
control: { type: "select" },
},
disabled: { control: "boolean" },
variant: {
options: ["primary", "secondary", "ghost", "destructive"],
control: { type: "select" },
},
},
args: {
disabled: false,
variant: undefined,
"aria-label": "Lorem ipsum",
icon: "effects",
},
render: ({ ...args }) => <IconButton {...args} />,
};
export const Default = {};
export const Primary = {
args: {
variant: "primary",
},
};
export const Secondary = {
args: {
variant: "secondary",
},
};
export const Ghost = {
args: {
variant: "ghost",
},
};
export const Destructive = {
args: {
variant: "destructive",
},
};