diff --git a/package-lock.json b/package-lock.json index 6425a40..1687430 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4804,6 +4804,10 @@ "resolved": "packages/snackbar", "link": true }, + "node_modules/@pepabo-inhouse/sticker": { + "resolved": "packages/sticker", + "link": true + }, "node_modules/@pepabo-inhouse/stories-web": { "resolved": "packages/stories-web", "link": true @@ -31253,6 +31257,7 @@ "@pepabo-inhouse/side-navigation": "^4.0.0", "@pepabo-inhouse/skeleton": "^4.0.0", "@pepabo-inhouse/snackbar": "^4.0.0", + "@pepabo-inhouse/sticker": "^4.0.0", "@pepabo-inhouse/tab": "^4.0.0", "@pepabo-inhouse/table": "^4.0.0", "@pepabo-inhouse/textfield": "^4.0.0", @@ -31311,6 +31316,7 @@ } }, "packages/disclosure": { + "name": "@pepabo-inhouse/disclosure", "version": "4.0.0", "dependencies": { "@pepabo-inhouse/adapter": "^4.0.0", @@ -31510,6 +31516,18 @@ "@pepabo-inhouse/tokens": "^2.4.0" } }, + "packages/sticker": { + "name": "@pepabo-inhouse/sticker", + "version": "4.0.0", + "dependencies": { + "@pepabo-inhouse/adapter": "^4.0.0", + "@pepabo-inhouse/skeleton": "^4.0.0" + }, + "devDependencies": { + "@pepabo-inhouse/flavor": "^4.0.0", + "@pepabo-inhouse/tokens": "^2.4.0" + } + }, "packages/stories-web": { "name": "@pepabo-inhouse/stories-web", "version": "4.0.0", diff --git a/packages/components-web/_index.scss b/packages/components-web/_index.scss index 130236c..856e6bf 100644 --- a/packages/components-web/_index.scss +++ b/packages/components-web/_index.scss @@ -28,4 +28,5 @@ @forward "@pepabo-inhouse/table" as table-*; @forward "@pepabo-inhouse/textfield" as textfield-*; @forward "@pepabo-inhouse/snackbar" as snackbar-*; +@forward "@pepabo-inhouse/sticker" as sticker-*; @forward "@pepabo-inhouse/validation-message" as validation-message-*; diff --git a/packages/components-web/inhouse-components-web.scss b/packages/components-web/inhouse-components-web.scss index 450f616..583e81e 100644 --- a/packages/components-web/inhouse-components-web.scss +++ b/packages/components-web/inhouse-components-web.scss @@ -28,6 +28,7 @@ @use "@pepabo-inhouse/table"; @use "@pepabo-inhouse/textfield"; @use "@pepabo-inhouse/snackbar"; +@use "@pepabo-inhouse/sticker"; @use "@pepabo-inhouse/validation-message"; // flavor内で@import が存在しているので最初に flavor を読む @@ -62,4 +63,5 @@ @include table.export; @include textfield.export; @include snackbar.export; +@include sticker.export; @include validation-message.export; diff --git a/packages/components-web/package.json b/packages/components-web/package.json index 1e82d0d..4aef1db 100644 --- a/packages/components-web/package.json +++ b/packages/components-web/package.json @@ -41,6 +41,7 @@ "@pepabo-inhouse/side-navigation": "^4.0.0", "@pepabo-inhouse/skeleton": "^4.0.0", "@pepabo-inhouse/snackbar": "^4.0.0", + "@pepabo-inhouse/sticker": "^4.0.0", "@pepabo-inhouse/tab": "^4.0.0", "@pepabo-inhouse/table": "^4.0.0", "@pepabo-inhouse/textfield": "^4.0.0", diff --git a/packages/sticker/README.md b/packages/sticker/README.md new file mode 100644 index 0000000..3d1cf7c --- /dev/null +++ b/packages/sticker/README.md @@ -0,0 +1,13 @@ +# Inhouse Sticker + +## Usage + +### Installation + +```bash +$ npm install @pepabo-inhouse/sticker + +# or + +$ yarn add @pepabo-inhouse/sticker +``` diff --git a/packages/sticker/_index.scss b/packages/sticker/_index.scss new file mode 100644 index 0000000..9e8069c --- /dev/null +++ b/packages/sticker/_index.scss @@ -0,0 +1 @@ +@forward "./mixins" show style, export; diff --git a/packages/sticker/_mixins.scss b/packages/sticker/_mixins.scss new file mode 100644 index 0000000..25a76ab --- /dev/null +++ b/packages/sticker/_mixins.scss @@ -0,0 +1,137 @@ +@use "sass:map"; +@use "@pepabo-inhouse/adapter/functions" as adapter; +@use "@pepabo-inhouse/skeleton/mixins" as skeleton; +@use "./variables"; + +@mixin export { + .in-sticker { + @include style; + } + + .in-skeleton-sticker { + @include skeleton-style; + } +} + +@mixin style($options: variables.$default-option) { + $options: map.merge(variables.$default-option, $options); + + @include -proto; + @include -color-style( + $color: map.get($options, color), + $appearance: map.get($options, appearance) + ); + @include -size-style( + $size: map.get($options, size) + ); + + & > * { + @include -child-element-style; + } + + @each $color in adapter.get-semantic-intentions() { + &.-color-#{$color} { + @include -color-style( + $color: $color, + $appearance: map.get($options, appearance) + ); + + @each $appearance in variables.$available-appearances { + &.-appearance-#{$appearance} { + @include -color-style( + $color: $color, + $appearance: $appearance + ); + } + } + } + } + + @each $appearance in variables.$available-appearances { + &.-appearance-#{$appearance} { + @include -color-style( + $color: map.get($options, color), + $appearance: $appearance + ); + } + } + + @each $size in adapter.get-interactive-component-height-keys() { + &.-size-#{$size} { + @include -size-style($size: $size); + } + } +} + +@mixin skeleton-style($options: variables.$default-option) { + $options: map.merge(variables.$default-option, $options); + + @include skeleton.style; + + display: inline-block; + width: auto; + height: + calc( + adapter.get-line-height( + $level: map.get($options, size), + $density: normal + ) + adapter.get-spacing-size($level: xs) + ); + border-radius: 4px; + + @each $size in adapter.get-interactive-component-height-keys() { + &.-size-#{$size} { + height: + calc( + adapter.get-line-height( + $level: $size, + $density: normal + ) + adapter.get-spacing-size($level: xs) + ); + } + } +} + +@mixin -proto { + display: inline-flex; + gap: adapter.get-spacing-size($level: xxs); + align-items: center; + font-family: inherit; + white-space: nowrap; +} + +@mixin -child-element-style { + display: inline-flex; + align-items: center; + justify-content: center; +} + +@mixin -color-style($color, $appearance) { + @if $appearance == fill-only { + color: #fff; + background-color: adapter.get-semantic-color($color, 700); + } @else if $appearance == fill-with-outlined { + color: adapter.get-semantic-color($color, 800); + background-color: adapter.get-semantic-color($color, 100); + border: 1px solid adapter.get-semantic-color($color, 300); + } @else if $appearance == outlined { + color: adapter.get-semantic-color($color, 800); + background-color: #fff; + border: 1px solid adapter.get-semantic-color($color, 300); + } @else if $appearance == text-only { + color: adapter.get-semantic-color($color, 800); + background-color: transparent; + } +} + +@mixin -size-style($size) { + font-size: adapter.get-font-size($level: $size); + line-height: + adapter.get-line-height( + $level: $size, + $density: normal + ); + padding-block: calc(adapter.get-spacing-size($level: xs) / 2); + padding-inline: adapter.get-spacing-size($level: s); + border-radius: 4px; +} diff --git a/packages/sticker/_variables.scss b/packages/sticker/_variables.scss new file mode 100644 index 0000000..a4e0d4e --- /dev/null +++ b/packages/sticker/_variables.scss @@ -0,0 +1,12 @@ +$default-option: ( + color: informative, + size: m, + appearance: fill-only, +); + +$available-appearances: ( + fill-only, + fill-with-outlined, + outlined, + text-only, +); diff --git a/packages/sticker/inhouse-sticker.scss b/packages/sticker/inhouse-sticker.scss new file mode 100644 index 0000000..b2be6e9 --- /dev/null +++ b/packages/sticker/inhouse-sticker.scss @@ -0,0 +1,2 @@ +@use "./mixins"; +@include mixins.export; diff --git a/packages/sticker/package.json b/packages/sticker/package.json new file mode 100644 index 0000000..b3c6463 --- /dev/null +++ b/packages/sticker/package.json @@ -0,0 +1,21 @@ +{ + "name": "@pepabo-inhouse/sticker", + "description": "Inhouse Components for the web sticker component", + "version": "4.0.0", + "repository": { + "type": "git", + "url": "https://github.com/pepabo/inhouse-components-web.git", + "directory": "packages/sticker" + }, + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@pepabo-inhouse/adapter": "^4.0.0", + "@pepabo-inhouse/skeleton": "^4.0.0" + }, + "devDependencies": { + "@pepabo-inhouse/flavor": "^4.0.0", + "@pepabo-inhouse/tokens": "^2.4.0" + } +} diff --git a/packages/stories-web/src/components/demo/StickerDemo.tsx b/packages/stories-web/src/components/demo/StickerDemo.tsx new file mode 100644 index 0000000..4436c40 --- /dev/null +++ b/packages/stories-web/src/components/demo/StickerDemo.tsx @@ -0,0 +1,40 @@ +import React, { FC } from 'react' +import Sticker, { Props as StickerProps } from '../sticker/Sticker' +import Icon from '../Icon' + +export type Props = Pick + +const StickerDemo: FC = (props: Props) => ( + <> + + + + + } + /> + } + /> + } + /> + } + /> + +) + +export default StickerDemo diff --git a/packages/stories-web/src/components/sticker/SkeletonSticker.tsx b/packages/stories-web/src/components/sticker/SkeletonSticker.tsx new file mode 100644 index 0000000..159f95a --- /dev/null +++ b/packages/stories-web/src/components/sticker/SkeletonSticker.tsx @@ -0,0 +1,43 @@ +import React, { CSSProperties, FC } from 'react' +import { Size } from '../types' + +type TextUnit = 'rem' +type TextLength = `${number}${TextUnit}` + +export interface Props { + size?: Extract + width?: TextLength +} + +const SkeletonSticker: FC = (props: Props) => { + const { + size, + width, + ...rest + } = props + + const classes = ['in-skeleton-sticker'] + let style: CSSProperties = {} + + if (typeof size !== 'undefined') { + classes.push(`-size-${size}`) + } + + if (typeof width !== 'undefined') { + style = { + ...style, + width: width, + } + } + + return ( +
+
+ ) +} + +export default SkeletonSticker diff --git a/packages/stories-web/src/components/sticker/Sticker.tsx b/packages/stories-web/src/components/sticker/Sticker.tsx new file mode 100644 index 0000000..71a4325 --- /dev/null +++ b/packages/stories-web/src/components/sticker/Sticker.tsx @@ -0,0 +1,57 @@ +import React, { FC, HTMLAttributes, ReactNode } from 'react' +import { SemanticColor, Size } from '../types' + +type HTMLProps = HTMLAttributes + +export interface Props extends HTMLProps { + appearance?: 'fill-only' | 'fill-with-outlined' | 'outlined' | 'text-only' + body?: ReactNode + color?: SemanticColor + leading?: ReactNode + size?: Extract +} + +const Sticker: FC = (props: Props) => { + const { + appearance, + body, + color, + leading, + size, + ...rest + } = props + + const classes = ['in-sticker'] + + if (typeof color !== 'undefined') { + classes.push(`-color-${color}`) + } + + if (typeof size !== 'undefined') { + classes.push(`-size-${size}`) + } + + if (typeof appearance !== 'undefined') { + classes.push(`-appearance-${appearance}`) + } + + return ( + + {leading && ( + + {leading} + + )} + {body && ( + + {body} + + )} + + ) +} + +export default Sticker diff --git a/packages/stories-web/src/sticker.stories.tsx b/packages/stories-web/src/sticker.stories.tsx new file mode 100644 index 0000000..c00d5b7 --- /dev/null +++ b/packages/stories-web/src/sticker.stories.tsx @@ -0,0 +1,21 @@ +import type { StoryFn, Meta } from '@storybook/react' +import React from 'react' +import StickerDemo, { Props } from './components/demo/StickerDemo' +import SkeletonSticker, { Props as SkeletonStickerProps } from './components/sticker/SkeletonSticker' + +export default { + title: 'Components/Sticker', + component: StickerDemo, +} as Meta + +const Template: StoryFn = (args) => +const SkeletonTemplate: StoryFn = (args) => + +export const Index = Template.bind({}) +Index.args = {} + +export const Skeleton = SkeletonTemplate.bind({}) +Skeleton.args = { + size: 'm', + width: '5rem', +}