Merge pull request #27843 from backstage/canon
Introducing Canon, a new UI library for Backstage
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
...require('@backstage/cli/config/eslint-factory')(__dirname),
|
||||
extends: ['plugin:storybook/recommended'],
|
||||
rules: {
|
||||
'react/forbid-elements': 'off',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { StorybookConfig } from '@storybook/react-webpack5';
|
||||
import { VanillaExtractPlugin } from '@vanilla-extract/webpack-plugin';
|
||||
import { merge } from 'webpack-merge';
|
||||
|
||||
import { join, dirname } from 'path';
|
||||
|
||||
/**
|
||||
* This function is used to resolve the absolute path of a package.
|
||||
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
|
||||
*/
|
||||
function getAbsolutePath(value: string): any {
|
||||
return dirname(require.resolve(join(value, 'package.json')));
|
||||
}
|
||||
const config: StorybookConfig = {
|
||||
stories: [
|
||||
'../docs/**/*.mdx',
|
||||
'../src/components/**/*.mdx',
|
||||
'../src/components/**/*.stories.@(js|jsx|mjs|ts|tsx)',
|
||||
],
|
||||
staticDirs: ['../static'],
|
||||
addons: [
|
||||
getAbsolutePath('@storybook/addon-webpack5-compiler-swc'),
|
||||
getAbsolutePath('@storybook/addon-essentials'),
|
||||
getAbsolutePath('@chromatic-com/storybook'),
|
||||
getAbsolutePath('@storybook/addon-interactions'),
|
||||
getAbsolutePath('@storybook/addon-themes'),
|
||||
],
|
||||
framework: {
|
||||
name: getAbsolutePath('@storybook/react-webpack5'),
|
||||
options: {
|
||||
plugins: [new VanillaExtractPlugin()],
|
||||
},
|
||||
},
|
||||
webpackFinal: config => {
|
||||
return merge(config, {
|
||||
plugins: [new VanillaExtractPlugin()],
|
||||
});
|
||||
},
|
||||
};
|
||||
export default config;
|
||||
@@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import type { Preview, ReactRenderer } from '@storybook/react';
|
||||
import { withThemeByDataAttribute } from '@storybook/addon-themes';
|
||||
|
||||
import '../src/theme/styles.css';
|
||||
|
||||
const preview: Preview = {
|
||||
parameters: {
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/i,
|
||||
},
|
||||
},
|
||||
backgrounds: {
|
||||
disable: true,
|
||||
},
|
||||
},
|
||||
decorators: [
|
||||
withThemeByDataAttribute<ReactRenderer>({
|
||||
themes: {
|
||||
light: 'light',
|
||||
dark: 'dark',
|
||||
},
|
||||
defaultTheme: 'light',
|
||||
}),
|
||||
(Story, context) => {
|
||||
document.body.style.backgroundColor = 'var(--canon-background)';
|
||||
|
||||
const docsStoryElements = document.getElementsByClassName('docs-story');
|
||||
Array.from(docsStoryElements).forEach(element => {
|
||||
(element as HTMLElement).style.backgroundColor =
|
||||
'var(--canon-background)';
|
||||
});
|
||||
|
||||
return <Story />;
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default preview;
|
||||
@@ -0,0 +1,12 @@
|
||||
# @backstage/canon
|
||||
|
||||
_This package was created through the Backstage CLI_.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the package via Yarn:
|
||||
|
||||
```sh
|
||||
cd <package-dir> # if within a monorepo
|
||||
yarn add @backstage/canon
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: backstage-canon
|
||||
title: '@backstage/canon'
|
||||
spec:
|
||||
lifecycle: experimental
|
||||
type: backstage-web-library
|
||||
owner: maintainers
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Unstyled } from '@storybook/blocks';
|
||||
import { Columns, Text, ComponentStatus, Banner } from './components';
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<img src="header.png" style={{ width: '100%', marginTop: '-16px' }} />
|
||||
|
||||
<Text style={{ marginTop: '64px' }}>
|
||||
Welcome to the Canon, the new design library for Backstage plugins. This
|
||||
project is still under active development but we will make sure to document
|
||||
the API as we go. We are aiming to improve the general UI of Backstage and
|
||||
plugins across Backstage. This new library will take time to build but we are
|
||||
building it incrementally with not conflict with the existing theming system.
|
||||
</Text>
|
||||
|
||||
<Banner variant="warning">
|
||||
This library is still under heavy construction. Please be aware that the API
|
||||
will change until we reach a stable release.
|
||||
</Banner>
|
||||
|
||||
<Columns style={{ marginTop: '64px' }}>
|
||||
<ComponentStatus
|
||||
name="Theming system"
|
||||
status="inProgress"
|
||||
link="/?path=/docs/theme--docs"
|
||||
/>
|
||||
<ComponentStatus name="Storybook setup" status="inProgress" />
|
||||
<ComponentStatus name="Tokens" status="inProgress" />
|
||||
<ComponentStatus
|
||||
name="Box"
|
||||
status="inProgress"
|
||||
link="/?path=/docs/components-box--docs"
|
||||
/>
|
||||
<ComponentStatus
|
||||
name="Button"
|
||||
status="inProgress"
|
||||
link="/?path=/docs/components-button--docs"
|
||||
/>
|
||||
<ComponentStatus name="Input" status="notStarted" />
|
||||
<ComponentStatus name="Select" status="notStarted" />
|
||||
<ComponentStatus name="Checkbox" status="notStarted" />
|
||||
<ComponentStatus name="Radio" status="notStarted" />
|
||||
<ComponentStatus name="Switch" status="notStarted" />
|
||||
<ComponentStatus name="Tooltip" status="notStarted" />
|
||||
<ComponentStatus name="Header" status="notStarted" />
|
||||
<ComponentStatus name="Tabs" status="notStarted" />
|
||||
</Columns>
|
||||
|
||||
</Unstyled>
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Unstyled, Source } from '@storybook/blocks';
|
||||
import { Title, Text, LayoutComponents } from './components';
|
||||
import * as Table from './components/Table';
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<Title type="h1">Layout</Title>
|
||||
<Text>
|
||||
Canon is made for extensibility. We built this library to make it easy for any
|
||||
Backstage plugin creator to be able to build their ideas at speed ensuring
|
||||
consistency across the rest of your ecosystem. Each component is designed to
|
||||
be editable to match your need but sometimes you want to have more control
|
||||
over the layout of your page. To help you with that, we created a set of
|
||||
layout components that you can use to build your own layouts. All of these
|
||||
components are built to extend on our theming system, making it easy for you
|
||||
to build your own layouts. Sometimes these components are not enough so we
|
||||
created a set of helpers to be used with any CSS-in-JS library.
|
||||
</Text>
|
||||
|
||||
<Title type="h2">Layout Components</Title>
|
||||
|
||||
<Text>
|
||||
We built a couple of layout components to help you build responsive elements
|
||||
that will be consistent with the rest of your Backstage instance. These
|
||||
components are opinionated and use TypeScript to ensure that the props you
|
||||
provide are the ones coming from the theme.
|
||||
</Text>
|
||||
|
||||
<Source
|
||||
code={`<Stack direction="column" gap="md">
|
||||
<Box>Hello World</Box>
|
||||
<Inline gap="sm">
|
||||
<Box>Project 1</Box>
|
||||
<Box>Project 2</Box>
|
||||
</Inline>
|
||||
</Stack>`}
|
||||
language="tsx"
|
||||
dark
|
||||
/>
|
||||
|
||||
<LayoutComponents />
|
||||
|
||||
<Title type="h2">Layout Helpers</Title>
|
||||
|
||||
<Text>TBD</Text>
|
||||
|
||||
</Unstyled>
|
||||
@@ -0,0 +1,229 @@
|
||||
import { Unstyled, Source } from '@storybook/blocks';
|
||||
import { Title, Text, Chip } from './components';
|
||||
import * as Table from './components/Table';
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<Title type="h1">Theming</Title>
|
||||
<Text>
|
||||
Backstage ships with a default theme with a light and dark mode variant. The
|
||||
themes are provided as a part of the `@backstage/canon` package, which also
|
||||
includes utilities for customizing the default theme, or creating completely
|
||||
new themes.
|
||||
</Text>
|
||||
|
||||
<Title type="h2">Light & Dark modes</Title>
|
||||
<Text>
|
||||
By default we are supporting both light and dark modes. Each user can opt to
|
||||
choose what theme they want to use or to use their system decide what theme to
|
||||
use. If you want to create your own theme, you will have to set both light and
|
||||
dark themes following the instructions below. If you only set one of them, the
|
||||
other mode will fallback to the default theme.
|
||||
</Text>
|
||||
|
||||
<Title type="h2">How to create your own theme</Title>
|
||||
<Text>
|
||||
To create your own theme, you will have to define the variables below. To do
|
||||
that, create a theme.css file and import it in your application. Here's an
|
||||
example below on how to set your light and dark mode.
|
||||
</Text>
|
||||
<Source
|
||||
code={`/** Light theme **/
|
||||
[data-theme='light'] {
|
||||
--canon-accent: #1ed760;
|
||||
--canon-bg: #fff;
|
||||
--canon-surface-1: #f5f5f5;
|
||||
--canon-surface-2: #000;
|
||||
--canon-outline: #666;
|
||||
--canon-outline-focus: #ccc;
|
||||
--canon-text-primary: #f0f0f0;
|
||||
--canon-text-secondary: #666;
|
||||
--canon-font-sans: 'Geist', serif;
|
||||
--canon-font-mono: 'Monospace', monospace;
|
||||
/* ... other values */
|
||||
}
|
||||
|
||||
/** Dark theme **/
|
||||
[data-theme='dark'] {
|
||||
--canon-accent: #1ed760;
|
||||
--canon-bg: #000;
|
||||
--canon-surface-1: #f5f5f5;
|
||||
--canon-surface-2: #000;
|
||||
--canon-outline: #666;
|
||||
--canon-outline-focus: #ccc;
|
||||
--canon-text-primary: #fff;
|
||||
--canon-text-secondary: #666;
|
||||
--canon-font-sans: 'Geist', serif;
|
||||
--canon-font-mono: 'Monospace', monospace;
|
||||
/* ... other values */
|
||||
}
|
||||
`}
|
||||
language="css"
|
||||
dark
|
||||
/>
|
||||
|
||||
<Title type="h2">Colors</Title>
|
||||
<Text>
|
||||
We provide a set of generic colours tokens that we use across Canon. By
|
||||
changing these colours you can easily change the look and feel of your
|
||||
application to match your brand.
|
||||
</Text>
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.HeaderRow>
|
||||
<Table.HeaderCell>Prop</Table.HeaderCell>
|
||||
<Table.HeaderCell>Description</Table.HeaderCell>
|
||||
</Table.HeaderRow>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-accent</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The accent color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-bg</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The background color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-surface-1</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The first surface color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-surface-2</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The second surface color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-outline</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The outline color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-outline-focus</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The outline focus color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-text-primary</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The primary text color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-text-secondary</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The secondary text color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
|
||||
<Title type="h2">Typography</Title>
|
||||
<Text>
|
||||
We have two fonts that we use across Canon. The first one is the sans-serif
|
||||
font that we use for the body of the application. The second one is the
|
||||
monospace font that we use for code blocks and tables.
|
||||
</Text>
|
||||
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.HeaderRow>
|
||||
<Table.HeaderCell>Prop</Table.HeaderCell>
|
||||
<Table.HeaderCell>Description</Table.HeaderCell>
|
||||
</Table.HeaderRow>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-font-sans</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The sans-serif font for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-font-mono</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The monospace font for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
|
||||
<Title type="h2">Spacing</Title>
|
||||
<Text>
|
||||
Our default spacing system is made to work in most scenarios. We have 7 scale
|
||||
values from `xxs` to `xxl`. We use the values on padding and margin in our
|
||||
layout components mostly. If you prefer to use a different spacing system, you
|
||||
can do that by changing the values below.
|
||||
</Text>
|
||||
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.HeaderRow>
|
||||
<Table.HeaderCell>Prop</Table.HeaderCell>
|
||||
<Table.HeaderCell>Description</Table.HeaderCell>
|
||||
</Table.HeaderRow>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-unit</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
The base unit for the spacing system. Default value is `1em`
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-xxs</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Default value is `0.25 x space unit`</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-xs</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Default value is `0.5 x space unit`</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-sm</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Default value is `0.75 x space unit`</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-md</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Default value is `1.25 x space unit`</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-lg</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Default value is `2 x space unit`</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-xl</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Default value is `3.25 x space unit`</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-space-xxl</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>Default value is `5.25 x space unit`</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
|
||||
</Unstyled>
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { recipe } from '@vanilla-extract/recipes';
|
||||
import { globalStyle, style } from '@vanilla-extract/css';
|
||||
|
||||
export const bannerStyles = recipe({
|
||||
base: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
fontFamily: '"Geist", sans-serif',
|
||||
fontSize: '16px',
|
||||
lineHeight: '28px',
|
||||
padding: '16px',
|
||||
borderRadius: '6px',
|
||||
marginBottom: '16px',
|
||||
border: '1px solid #e0e0e0',
|
||||
},
|
||||
variants: {
|
||||
variant: {
|
||||
info: {
|
||||
backgroundColor: '#F2F2F2',
|
||||
borderColor: '#CDCDCD',
|
||||
color: '#888888',
|
||||
},
|
||||
warning: {
|
||||
backgroundColor: '#FFF2B9',
|
||||
borderColor: '#FFD000',
|
||||
color: '#D79927',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
globalStyle(`${bannerStyles.classNames.base} p`, {
|
||||
margin: '0',
|
||||
});
|
||||
|
||||
export const bannerIconStyles = style({
|
||||
width: '32px',
|
||||
height: '32px',
|
||||
backgroundColor: 'rgba(215, 153, 39, 0.2)',
|
||||
borderRadius: '6px',
|
||||
marginRight: '16px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { bannerIconStyles, bannerStyles } from './banner.css';
|
||||
|
||||
export const Banner = ({
|
||||
children,
|
||||
variant = 'info',
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
variant?: 'info' | 'warning';
|
||||
}) => {
|
||||
return (
|
||||
<div className={bannerStyles({ variant })}>
|
||||
<div className={bannerIconStyles}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="18"
|
||||
height="18"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M4.00001 20V14C4.00001 9.58172 7.58173 6 12 6C16.4183 6 20 9.58172 20 14V20H21V22H3.00001V20H4.00001ZM6.00001 20H18V14C18 10.6863 15.3137 8 12 8C8.6863 8 6.00001 10.6863 6.00001 14V20ZM11 2H13V5H11V2ZM19.7782 4.80761L21.1924 6.22183L19.0711 8.34315L17.6569 6.92893L19.7782 4.80761ZM2.80762 6.22183L4.22183 4.80761L6.34315 6.92893L4.92894 8.34315L2.80762 6.22183ZM7.00001 14C7.00001 11.2386 9.23858 9 12 9V11C10.3432 11 9.00001 12.3431 9.00001 14H7.00001Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { recipe } from '@vanilla-extract/recipes';
|
||||
|
||||
export const chipStyles = recipe({
|
||||
base: {
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
fontFamily: 'Geist Mono, monospace',
|
||||
fontSize: '13px',
|
||||
borderRadius: '6px',
|
||||
padding: '0px 8px',
|
||||
height: '24px',
|
||||
marginRight: '4px',
|
||||
},
|
||||
variants: {
|
||||
head: {
|
||||
true: {
|
||||
backgroundColor: '#eaf2fd',
|
||||
color: '#2563eb',
|
||||
},
|
||||
false: {
|
||||
backgroundColor: '#f0f0f0',
|
||||
color: '#5d5d5d',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { chipStyles } from './chip.css';
|
||||
|
||||
export const Chip = ({
|
||||
children,
|
||||
head = false,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
head?: boolean;
|
||||
}) => {
|
||||
return <span className={chipStyles({ head })}>{children}</span>;
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const styles = style({
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(2, 1fr)',
|
||||
rowGap: '20px',
|
||||
columnGap: '80px',
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { styles } from './columns.css';
|
||||
|
||||
export const Columns = ({
|
||||
children,
|
||||
style,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
}) => {
|
||||
return (
|
||||
<div className={styles} style={style}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { style } from '@vanilla-extract/css';
|
||||
import { recipe } from '@vanilla-extract/recipes';
|
||||
|
||||
export const container = style({
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
});
|
||||
|
||||
export const title = style({
|
||||
color: '#3b59ff',
|
||||
fontFamily: '"Geist", sans-serif',
|
||||
fontSize: '16px',
|
||||
fontWeight: 400,
|
||||
textDecoration: 'none',
|
||||
});
|
||||
|
||||
export const pill = recipe({
|
||||
base: {
|
||||
fontFamily: '"Geist", sans-serif',
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
color: '#000',
|
||||
borderRadius: '40px',
|
||||
padding: '0px 8px',
|
||||
height: '24px',
|
||||
fontSize: '12px',
|
||||
fontWeight: 600,
|
||||
borderStyle: 'solid',
|
||||
borderWidth: '1px',
|
||||
marginLeft: '8px',
|
||||
},
|
||||
variants: {
|
||||
status: {
|
||||
notStarted: {
|
||||
backgroundColor: '#F2F2F2',
|
||||
borderColor: '#CDCDCD',
|
||||
color: '#888888',
|
||||
},
|
||||
inProgress: {
|
||||
backgroundColor: '#FFF2B9',
|
||||
borderColor: '#FFD000',
|
||||
color: '#D79927',
|
||||
},
|
||||
done: {
|
||||
backgroundColor: '#D7F9D7',
|
||||
borderColor: '#4ED14A',
|
||||
color: '#3A9837',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { container, pill, title } from './component-status.css';
|
||||
|
||||
export const ComponentStatus = ({
|
||||
name,
|
||||
status = 'notStarted',
|
||||
style,
|
||||
link,
|
||||
}: {
|
||||
name: string;
|
||||
status: 'notStarted' | 'inProgress' | 'done';
|
||||
style?: React.CSSProperties;
|
||||
link?: string;
|
||||
}) => {
|
||||
return (
|
||||
<div className={container} style={style}>
|
||||
{link ? (
|
||||
<a href={link} className={title}>
|
||||
{name}
|
||||
</a>
|
||||
) : (
|
||||
<span className={title}>{name}</span>
|
||||
)}
|
||||
<span className={pill({ status })}>
|
||||
{status === 'notStarted' && 'Not Started'}
|
||||
{status === 'inProgress' && 'In Progress'}
|
||||
{status === 'done' && 'Done'}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
container,
|
||||
box,
|
||||
content,
|
||||
title,
|
||||
description,
|
||||
whiteBox,
|
||||
whiteBoxStack,
|
||||
stack,
|
||||
verticalDivider,
|
||||
horizontalDivider,
|
||||
columns,
|
||||
inline,
|
||||
tiles,
|
||||
whiteBoxColumns,
|
||||
whiteBoxInline,
|
||||
whiteBoxTiles,
|
||||
} from './layout-components.css';
|
||||
|
||||
export const LayoutComponents = () => {
|
||||
return (
|
||||
<div className={container}>
|
||||
<div className={box}>
|
||||
<a className={content} href="/?path=/docs/components-box--docs">
|
||||
<div className={whiteBox} />
|
||||
</a>
|
||||
<div className={title}>Box</div>
|
||||
<div className={description}>The most basic layout component.</div>
|
||||
</div>
|
||||
<div className={box}>
|
||||
<a
|
||||
className={`${content} ${stack}`}
|
||||
href="/?path=/docs/components-stack--docs"
|
||||
>
|
||||
<div className={whiteBoxStack} />
|
||||
<div className={verticalDivider} />
|
||||
<div className={whiteBoxStack} />
|
||||
</a>
|
||||
<div className={title}>Stack</div>
|
||||
<div className={description}>Arrange your components vertically.</div>
|
||||
</div>
|
||||
<div className={box}>
|
||||
<a
|
||||
className={`${content} ${columns}`}
|
||||
href="/?path=/docs/components-columns--docs"
|
||||
>
|
||||
<div className={whiteBoxColumns} />
|
||||
<div className={horizontalDivider} />
|
||||
<div className={whiteBoxColumns} />
|
||||
</a>
|
||||
<div className={title}>Columns</div>
|
||||
<div className={description}>Arrange your components horizontally.</div>
|
||||
</div>
|
||||
<div className={box}>
|
||||
<a className={content} href="/?path=/docs/components-inline--docs">
|
||||
<div className={inline}>
|
||||
<div className={whiteBoxInline} />
|
||||
<div className={whiteBoxInline} />
|
||||
<div className={whiteBoxInline} />
|
||||
<div className={whiteBoxInline} />
|
||||
<div className={whiteBoxInline} />
|
||||
<div className={whiteBoxInline} />
|
||||
<div className={whiteBoxInline} />
|
||||
<div className={whiteBoxInline} />
|
||||
<div className={whiteBoxInline} />
|
||||
<div className={whiteBoxInline} />
|
||||
<div className={whiteBoxInline} />
|
||||
<div className={whiteBoxInline} />
|
||||
</div>
|
||||
</a>
|
||||
<div className={title}>Inline</div>
|
||||
<div className={description}>Arrange your components in a row.</div>
|
||||
</div>
|
||||
<div className={box}>
|
||||
<a className={content} href="/?path=/docs/components-tiles--docs">
|
||||
<div className={tiles}>
|
||||
<div className={whiteBoxTiles} />
|
||||
<div className={whiteBoxTiles} />
|
||||
<div className={whiteBoxTiles} />
|
||||
<div className={whiteBoxTiles} />
|
||||
<div className={whiteBoxTiles} />
|
||||
<div className={whiteBoxTiles} />
|
||||
<div className={whiteBoxTiles} />
|
||||
<div className={whiteBoxTiles} />
|
||||
</div>
|
||||
</a>
|
||||
<div className={title}>Tiles</div>
|
||||
<div className={description}>Arrange your components in a grid.</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const container = style({
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-start',
|
||||
gap: '1rem',
|
||||
flexWrap: 'wrap',
|
||||
});
|
||||
|
||||
export const box = style({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: 'calc(33.33% - 0.67rem)',
|
||||
marginBottom: '1rem',
|
||||
alignItems: 'flex-start',
|
||||
});
|
||||
|
||||
export const content = style({
|
||||
flex: 'none',
|
||||
background: '#f2f2f2',
|
||||
borderRadius: '4px',
|
||||
width: '100%',
|
||||
height: '180px',
|
||||
border: '1px solid #e0e0e0',
|
||||
transition: 'border-color 0.2s ease-in-out',
|
||||
marginBottom: '0.75rem',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
selectors: {
|
||||
[`${box}:hover &`]: {
|
||||
borderColor: '#1f47ff',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const title = style({
|
||||
fontSize: '16px',
|
||||
fontFamily: 'var(--canon-font-sans)',
|
||||
transition: 'color 0.2s ease-in-out',
|
||||
marginBottom: '0.25rem',
|
||||
selectors: {
|
||||
[`${box}:hover &`]: {
|
||||
color: '#1f47ff',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const description = style({
|
||||
fontSize: '16px',
|
||||
fontFamily: 'var(--canon-font-sans)',
|
||||
color: '#9e9e9e',
|
||||
});
|
||||
|
||||
export const whiteBox = style({
|
||||
background: '#fff',
|
||||
width: '40px',
|
||||
height: '40px',
|
||||
borderRadius: '4px',
|
||||
boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)',
|
||||
});
|
||||
|
||||
export const whiteBoxStack = style([
|
||||
whiteBox,
|
||||
{
|
||||
width: '40%',
|
||||
height: '28px',
|
||||
},
|
||||
]);
|
||||
|
||||
export const whiteBoxColumns = style([
|
||||
whiteBox,
|
||||
{
|
||||
width: '30%',
|
||||
height: '32px',
|
||||
},
|
||||
]);
|
||||
|
||||
export const whiteBoxInline = style([
|
||||
whiteBox,
|
||||
{
|
||||
width: '32px',
|
||||
height: '32px',
|
||||
},
|
||||
]);
|
||||
|
||||
export const whiteBoxTiles = style([
|
||||
whiteBox,
|
||||
{
|
||||
width: '100%',
|
||||
height: '32px',
|
||||
},
|
||||
]);
|
||||
|
||||
export const stack = style({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
});
|
||||
|
||||
export const columns = style({
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
});
|
||||
|
||||
export const inline = style({
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'flex-start',
|
||||
gap: '0.5rem',
|
||||
paddingLeft: '1rem',
|
||||
});
|
||||
|
||||
export const tiles = style({
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(4, 1fr)',
|
||||
gap: '0.5rem',
|
||||
paddingLeft: '1rem',
|
||||
paddingRight: '1rem',
|
||||
width: '100%',
|
||||
});
|
||||
|
||||
export const verticalDivider = style({
|
||||
display: 'flex',
|
||||
flexShrink: 0,
|
||||
flexDirection: 'column',
|
||||
position: 'relative',
|
||||
height: '20px',
|
||||
width: '2px',
|
||||
backgroundColor: '#1f47ff',
|
||||
selectors: {
|
||||
'&::before': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: '-9px',
|
||||
width: '20px',
|
||||
height: '2px',
|
||||
backgroundColor: '#1f47ff',
|
||||
},
|
||||
'&::after': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: '-9px',
|
||||
width: '20px',
|
||||
height: '2px',
|
||||
backgroundColor: '#1f47ff',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const horizontalDivider = style({
|
||||
flexShrink: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
position: 'relative',
|
||||
height: '2px',
|
||||
width: '20px',
|
||||
backgroundColor: '#1f47ff',
|
||||
selectors: {
|
||||
'&::before': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: '-9px',
|
||||
width: '2px',
|
||||
height: '20px',
|
||||
backgroundColor: '#1f47ff',
|
||||
},
|
||||
'&::after': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: '-9px',
|
||||
width: '2px',
|
||||
height: '20px',
|
||||
backgroundColor: '#1f47ff',
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
cellStyles,
|
||||
headerCellStyles,
|
||||
tableStyles,
|
||||
wrapperStyles,
|
||||
rowStyles,
|
||||
} from './table.css';
|
||||
|
||||
export const Root = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<div className={wrapperStyles}>
|
||||
<table className={tableStyles}>{children}</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Header = ({ children }: { children: React.ReactNode }) => {
|
||||
return <thead>{children}</thead>;
|
||||
};
|
||||
|
||||
export const Body = ({ children }: { children: React.ReactNode }) => {
|
||||
return <tbody>{children}</tbody>;
|
||||
};
|
||||
|
||||
export const HeaderRow = ({ children }: { children: React.ReactNode }) => {
|
||||
return <tr>{children}</tr>;
|
||||
};
|
||||
|
||||
export const HeaderCell = ({ children }: { children: React.ReactNode }) => {
|
||||
return <th className={`${cellStyles} ${headerCellStyles}`}>{children}</th>;
|
||||
};
|
||||
|
||||
export const Row = ({ children }: { children: React.ReactNode }) => {
|
||||
return <tr className={rowStyles}>{children}</tr>;
|
||||
};
|
||||
|
||||
export const Cell = ({ children }: { children: React.ReactNode }) => {
|
||||
return <td className={cellStyles}>{children}</td>;
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { style, globalStyle } from '@vanilla-extract/css';
|
||||
|
||||
export const wrapperStyles = style({
|
||||
border: '1px solid #e7e7e7',
|
||||
borderRadius: '4px',
|
||||
overflow: 'hidden',
|
||||
});
|
||||
|
||||
export const tableStyles = style({
|
||||
width: '100%',
|
||||
margin: '0 !important',
|
||||
padding: '0 !important',
|
||||
borderSpacing: '0px',
|
||||
borderCollapse: 'collapse',
|
||||
});
|
||||
|
||||
export const cellStyles = style({
|
||||
padding: '12px 16px !important',
|
||||
border: 'none !important',
|
||||
textAlign: 'left',
|
||||
backgroundColor: 'white !important',
|
||||
fontFamily: '"Geist", sans-serif',
|
||||
fontSize: '16px',
|
||||
fontWeight: 300,
|
||||
});
|
||||
|
||||
// Apply global styles to <p> elements inside cellStyles
|
||||
globalStyle(`${cellStyles} p`, {
|
||||
margin: '0',
|
||||
});
|
||||
|
||||
export const headerRowStyles = style({
|
||||
borderBottom: '1px solid #e7e7e7',
|
||||
});
|
||||
|
||||
export const headerCellStyles = style({
|
||||
backgroundColor: '#f5f5f5 !important',
|
||||
borderBottom: '1px solid #e7e7e7 !important',
|
||||
fontWeight: 500,
|
||||
fontSize: '14px',
|
||||
});
|
||||
|
||||
export const rowStyles = style({
|
||||
border: 'none',
|
||||
borderBottom: '1px solid #e7e7e7',
|
||||
selectors: {
|
||||
'&:last-child': {
|
||||
borderBottom: 'none',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const chipStyles = style({
|
||||
display: 'inline-block',
|
||||
fontSize: '14px !important',
|
||||
border: '1px solid #e7e7e7',
|
||||
borderRadius: '6px',
|
||||
padding: '0px 6px',
|
||||
height: '24px',
|
||||
});
|
||||
|
||||
export const typeStyles = style({
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
flexDirection: 'row',
|
||||
gap: '8px',
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { textStyles } from './text.css';
|
||||
|
||||
export const Text = ({
|
||||
children,
|
||||
style,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
}) => {
|
||||
return (
|
||||
<div className={textStyles} style={style}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { globalStyle, style } from '@vanilla-extract/css';
|
||||
|
||||
export const textStyles = style({
|
||||
fontFamily: '"Geist", sans-serif',
|
||||
fontSize: '16px',
|
||||
lineHeight: '28px',
|
||||
margin: '0',
|
||||
fontWeight: 300,
|
||||
paddingBottom: '16px',
|
||||
});
|
||||
|
||||
globalStyle(`${textStyles} p`, {
|
||||
fontFamily: '"Geist", sans-serif',
|
||||
fontSize: '16px',
|
||||
lineHeight: '28px',
|
||||
margin: '0',
|
||||
fontWeight: 300,
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { titleStyles } from './title.css';
|
||||
|
||||
export const Title = ({
|
||||
children,
|
||||
style,
|
||||
type = 'h1',
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
type?: 'h1' | 'h2' | 'h3';
|
||||
}) => {
|
||||
let Component = 'h1';
|
||||
if (type === 'h1') Component = 'h1';
|
||||
if (type === 'h2') Component = 'h2';
|
||||
if (type === 'h3') Component = 'h3';
|
||||
|
||||
return React.createElement(Component, {
|
||||
className: titleStyles({ type }),
|
||||
style,
|
||||
children,
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { recipe } from '@vanilla-extract/recipes';
|
||||
|
||||
export const titleStyles = recipe({
|
||||
base: {
|
||||
fontFamily: '"Geist", sans-serif',
|
||||
margin: '0',
|
||||
fontWeight: 500,
|
||||
border: 'none',
|
||||
},
|
||||
variants: {
|
||||
type: {
|
||||
h1: {
|
||||
fontSize: '36px',
|
||||
lineHeight: '44px',
|
||||
marginBottom: '32px',
|
||||
marginTop: '0px',
|
||||
},
|
||||
h2: {
|
||||
fontSize: '24px',
|
||||
lineHeight: '32px',
|
||||
marginBottom: '12px',
|
||||
marginTop: '52px',
|
||||
},
|
||||
h3: {
|
||||
fontSize: '20px',
|
||||
lineHeight: '28px',
|
||||
marginBottom: '12px',
|
||||
marginTop: '52px',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './Chip';
|
||||
export * from './Table';
|
||||
export * from './Title';
|
||||
export * from './Text';
|
||||
export * from './Columns';
|
||||
export * from './ComponentStatus';
|
||||
export * from './LayoutComponents';
|
||||
export * from './Banner';
|
||||
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"name": "@backstage/canon",
|
||||
"version": "0.0.0",
|
||||
"backstage": {
|
||||
"role": "web-library"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"private": true,
|
||||
"keywords": [
|
||||
"backstage"
|
||||
],
|
||||
"homepage": "https://backstage.io",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
"directory": "packages/canon"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"sideEffects": false,
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "backstage-cli package build",
|
||||
"build-storybook": "storybook build",
|
||||
"clean": "backstage-cli package clean",
|
||||
"lint": "backstage-cli package lint",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack",
|
||||
"start": "storybook dev -p 6006",
|
||||
"test": "backstage-cli package test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@base_ui/react": "^1.0.0-alpha.3",
|
||||
"@vanilla-extract/css": "^1.16.0",
|
||||
"@vanilla-extract/dynamic": "^2.1.2",
|
||||
"@vanilla-extract/recipes": "^0.5.5",
|
||||
"@vanilla-extract/sprinkles": "^1.6.3",
|
||||
"lucide-react": "^0.460.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@chromatic-com/storybook": "^3.2.2",
|
||||
"@storybook/addon-essentials": "^8.4.5",
|
||||
"@storybook/addon-interactions": "^8.4.5",
|
||||
"@storybook/addon-styling-webpack": "^1.0.1",
|
||||
"@storybook/addon-themes": "^8.4.5",
|
||||
"@storybook/addon-webpack5-compiler-swc": "^1.0.5",
|
||||
"@storybook/blocks": "^8.4.5",
|
||||
"@storybook/react": "^8.4.5",
|
||||
"@storybook/react-webpack5": "^8.4.5",
|
||||
"@storybook/test": "^8.4.5",
|
||||
"@testing-library/jest-dom": "^6.0.0",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@vanilla-extract/rollup-plugin": "^1.3.10",
|
||||
"@vanilla-extract/webpack-plugin": "^2.3.14",
|
||||
"eslint-plugin-storybook": "^0.11.1",
|
||||
"globals": "^15.11.0",
|
||||
"mini-css-extract-plugin": "^2.9.2",
|
||||
"react": "^18.0.2",
|
||||
"react-dom": "^18.0.2",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"storybook": "^8.4.5",
|
||||
"webpack-merge": "^6.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
||||
"react": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
||||
"react-router-dom": "6.0.0-beta.0 || ^6.3.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { Box } from './Box';
|
||||
import { listResponsiveValues } from '../../utils/list-values';
|
||||
import { responsiveProperties, colorProperties } from './sprinkles.css';
|
||||
|
||||
const argTypesResponsive = Object.keys(responsiveProperties.styles).reduce<
|
||||
Record<string, any>
|
||||
>((acc, n) => {
|
||||
if (
|
||||
[
|
||||
'margin',
|
||||
'marginBottom',
|
||||
'marginLeft',
|
||||
'marginRight',
|
||||
'marginTop',
|
||||
'marginX',
|
||||
'marginY',
|
||||
'padding',
|
||||
'paddingBottom',
|
||||
'paddingLeft',
|
||||
'paddingRight',
|
||||
'paddingTop',
|
||||
'paddingX',
|
||||
'paddingY',
|
||||
].includes(n)
|
||||
) {
|
||||
acc[n] = {
|
||||
control: 'select',
|
||||
options: ['xs', 'sm', 'md', 'lg', 'xl', '2xl'],
|
||||
};
|
||||
} else {
|
||||
acc[n] = {
|
||||
control: 'select',
|
||||
options: listResponsiveValues(
|
||||
n as keyof typeof responsiveProperties.styles,
|
||||
),
|
||||
};
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const argTypesColor = Object.keys(colorProperties.styles).reduce<
|
||||
Record<string, any>
|
||||
>((acc, n) => {
|
||||
acc[n as keyof typeof colorProperties.styles] = {
|
||||
control: 'select',
|
||||
options: Object.keys(
|
||||
colorProperties.styles[n as keyof typeof colorProperties.styles].values,
|
||||
),
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const argTypes = {
|
||||
...argTypesResponsive,
|
||||
...argTypesColor,
|
||||
};
|
||||
|
||||
// Sort the resulting object keys alphabetically
|
||||
const sortedArgTypes = Object.keys(argTypes)
|
||||
.sort()
|
||||
.reduce((acc, key) => {
|
||||
acc[key] = argTypes[key];
|
||||
return acc;
|
||||
}, {} as Record<string, any>);
|
||||
|
||||
// Add 'as' and 'children' to the sortedArgTypes
|
||||
sortedArgTypes['as'] = {
|
||||
control: 'select',
|
||||
options: ['div', 'span', 'article', 'section'],
|
||||
};
|
||||
|
||||
sortedArgTypes['children'] = {
|
||||
control: 'text',
|
||||
};
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Box',
|
||||
component: Box,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
},
|
||||
argTypes: sortedArgTypes,
|
||||
args: {
|
||||
as: 'div',
|
||||
background: 'elevation1',
|
||||
borderRadius: 'small',
|
||||
children: 'Basic Box',
|
||||
display: 'block',
|
||||
padding: 'sm',
|
||||
},
|
||||
} satisfies Meta<typeof Box>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Basic: Story = {
|
||||
args: {},
|
||||
};
|
||||
|
||||
export const Responsive: Story = {
|
||||
render: () => (
|
||||
<Box
|
||||
display={{ xs: 'block', sm: 'flex' }}
|
||||
padding={{ xs: 'xs', sm: 'md', lg: 'lg' }}
|
||||
background="background"
|
||||
>
|
||||
Responsive Box
|
||||
</Box>
|
||||
),
|
||||
};
|
||||
|
||||
export const FlexContainer: Story = {
|
||||
render: () => (
|
||||
<Box
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
padding="lg"
|
||||
background="background"
|
||||
>
|
||||
<Box padding="md" background="background" color="primary">
|
||||
Item 1
|
||||
</Box>
|
||||
<Box padding="md" background="background" color="primary">
|
||||
Item 2
|
||||
</Box>
|
||||
<Box padding="md" background="background" color="primary">
|
||||
Item 3
|
||||
</Box>
|
||||
</Box>
|
||||
),
|
||||
};
|
||||
|
||||
export const Nested: Story = {
|
||||
render: () => (
|
||||
<Box padding="lg" background="background">
|
||||
<Box padding="md" background="background">
|
||||
Header
|
||||
</Box>
|
||||
<Box
|
||||
display="flex"
|
||||
padding="md"
|
||||
background="background"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Box padding="xs" background="background">
|
||||
Sidebar
|
||||
</Box>
|
||||
<Box padding="xs" background="background">
|
||||
Main Content
|
||||
</Box>
|
||||
</Box>
|
||||
<Box padding="md" background="background">
|
||||
Footer
|
||||
</Box>
|
||||
</Box>
|
||||
),
|
||||
};
|
||||
|
||||
export const Alignment: Story = {
|
||||
render: () => (
|
||||
<Box
|
||||
display="flex"
|
||||
padding="lg"
|
||||
background="background"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
style={{ height: '200px' }}
|
||||
>
|
||||
<Box padding="md" background="background">
|
||||
Centered Content
|
||||
</Box>
|
||||
</Box>
|
||||
),
|
||||
};
|
||||
|
||||
// Example showing different spacing combinations
|
||||
export const Spacing: Story = {
|
||||
render: () => (
|
||||
<Box display="flex" flexDirection="column" gap="md">
|
||||
<Box padding="xs" background="background">
|
||||
Small Padding
|
||||
</Box>
|
||||
<Box padding="md" background="background">
|
||||
Medium Padding
|
||||
</Box>
|
||||
<Box padding="lg" background="background">
|
||||
Large Padding
|
||||
</Box>
|
||||
<Box paddingX="lg" paddingY="xs" background="background">
|
||||
Mixed Padding
|
||||
</Box>
|
||||
</Box>
|
||||
),
|
||||
};
|
||||
|
||||
// Example showing different display values
|
||||
export const DisplayVariants: Story = {
|
||||
render: () => (
|
||||
<Box display="flex" flexDirection="column" gap="md">
|
||||
<Box padding="md" background="background" display="block">
|
||||
Display Block
|
||||
</Box>
|
||||
<Box padding="md" background="background" display="flex">
|
||||
Display Flex
|
||||
</Box>
|
||||
<Box padding="md" background="background" display="inline">
|
||||
Display Inline
|
||||
</Box>
|
||||
</Box>
|
||||
),
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createElement } from 'react';
|
||||
import { boxSprinkles } from './sprinkles.css';
|
||||
import { base } from './box.css';
|
||||
|
||||
/**
|
||||
* Properties for {@link Box}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type BoxProps = Parameters<typeof boxSprinkles>[0] &
|
||||
Omit<
|
||||
React.AllHTMLAttributes<HTMLElement>,
|
||||
keyof Parameters<typeof boxSprinkles>[0]
|
||||
> & {
|
||||
as?: keyof JSX.IntrinsicElements;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export const Box = (props: BoxProps) => {
|
||||
const { as = 'div', className, style, ...rest } = props;
|
||||
const boxSprinklesProps: Record<string, unknown> = {};
|
||||
const nativeProps: Record<string, unknown> = {};
|
||||
|
||||
// Split props between sprinkles and native HTML props
|
||||
Object.entries(rest).forEach(([key, value]) => {
|
||||
if (value === undefined) return;
|
||||
|
||||
if (
|
||||
boxSprinkles.properties.has(
|
||||
key as keyof Parameters<typeof boxSprinkles>[0],
|
||||
)
|
||||
) {
|
||||
boxSprinklesProps[key] = value;
|
||||
} else {
|
||||
nativeProps[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
const sprinklesClassName = boxSprinkles(boxSprinklesProps);
|
||||
|
||||
return createElement(as, {
|
||||
className: [base, sprinklesClassName, className].filter(Boolean).join(' '),
|
||||
style,
|
||||
...nativeProps,
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const base = style({
|
||||
fontFamily: 'var(--canon-font-sans)',
|
||||
color: 'var(--canon-text-primary)',
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Canvas, Meta, Unstyled } from '@storybook/blocks';
|
||||
import * as BoxStories from './Box.stories';
|
||||
import { Chip } from '../../../docs/components/Chip';
|
||||
import * as Table from '../../../docs/components/Table';
|
||||
import { Box } from './Box';
|
||||
import { Stack } from '../Stack';
|
||||
import { Title, Text } from '../../../docs/components';
|
||||
import { Padding } from './docs/padding';
|
||||
import { listResponsiveValues, listColorValues } from '../../utils/list-values';
|
||||
import { responsiveProperties } from './sprinkles.css';
|
||||
import { PropsTable } from './docs/props-table';
|
||||
import { SpacingTable } from './docs/spacing-table';
|
||||
|
||||
<Meta of={BoxStories} />
|
||||
|
||||
<Unstyled>
|
||||
|
||||
<Title type="h1">Box</Title>
|
||||
<Text>
|
||||
Box is the lowest-level component in Canon. We use it internally to build all
|
||||
of our components. It is a wrapper around the HTML `<div>` element and
|
||||
provides a consistent API for styling and layout. You'll find below a list of all the available properties.
|
||||
</Text>
|
||||
|
||||
<PropsTable />
|
||||
|
||||
<Title type="h2">Padding & Margin</Title>
|
||||
<Text>
|
||||
Padding and margin are used to create space around your component using our
|
||||
predefined spacing tokens. We would recommend to use padding over margin to
|
||||
avoid collapsing margins but both are available.
|
||||
</Text>
|
||||
|
||||
<Padding />
|
||||
|
||||
<SpacingTable />
|
||||
|
||||
</Unstyled>
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Stack } from '../../Stack';
|
||||
import { Inline } from '../../Inline';
|
||||
import { Box } from '../Box';
|
||||
|
||||
const FakeBox = ({ children }: { children: string }) => (
|
||||
<Box
|
||||
paddingX="md"
|
||||
paddingY="sm"
|
||||
borderRadius="small"
|
||||
style={{ background: '#1f47ff', color: 'white' }}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const Padding = () => {
|
||||
return (
|
||||
<Stack
|
||||
align="center"
|
||||
borderRadius="small"
|
||||
marginBottom="md"
|
||||
gap="xl"
|
||||
paddingY="xl"
|
||||
style={{ border: '1px solid #e7e7e7' }}
|
||||
>
|
||||
<Inline align="center" gap="xl">
|
||||
<Stack
|
||||
align="center"
|
||||
borderRadius="small"
|
||||
padding="md"
|
||||
style={{ background: '#c4cafb', color: 'white' }}
|
||||
>
|
||||
<FakeBox>padding</FakeBox>
|
||||
</Stack>
|
||||
<Stack
|
||||
align="center"
|
||||
borderRadius="small"
|
||||
paddingX="md"
|
||||
style={{ background: '#c4cafb', color: 'white' }}
|
||||
>
|
||||
<FakeBox>paddingX</FakeBox>
|
||||
</Stack>
|
||||
<Stack
|
||||
align="center"
|
||||
borderRadius="small"
|
||||
paddingY="md"
|
||||
style={{ background: '#c4cafb', color: 'white' }}
|
||||
>
|
||||
<FakeBox>paddingY</FakeBox>
|
||||
</Stack>
|
||||
</Inline>
|
||||
<Inline align="center" gap="xl">
|
||||
<Stack
|
||||
align="center"
|
||||
borderRadius="small"
|
||||
paddingTop="md"
|
||||
style={{ background: '#c4cafb', color: 'white' }}
|
||||
>
|
||||
<FakeBox>paddingTop</FakeBox>
|
||||
</Stack>
|
||||
<Stack
|
||||
align="center"
|
||||
borderRadius="small"
|
||||
paddingBottom="md"
|
||||
style={{ background: '#c4cafb', color: 'white' }}
|
||||
>
|
||||
<FakeBox>paddingBottom</FakeBox>
|
||||
</Stack>
|
||||
<Stack
|
||||
align="center"
|
||||
borderRadius="small"
|
||||
paddingLeft="md"
|
||||
style={{ background: '#c4cafb', color: 'white' }}
|
||||
>
|
||||
<FakeBox>paddingLeft</FakeBox>
|
||||
</Stack>
|
||||
<Stack
|
||||
align="center"
|
||||
borderRadius="small"
|
||||
paddingRight="md"
|
||||
style={{ background: '#c4cafb', color: 'white' }}
|
||||
>
|
||||
<FakeBox>paddingRight</FakeBox>
|
||||
</Stack>
|
||||
</Inline>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import * as Table from '../../../../docs/components';
|
||||
import { Chip } from '../../../../docs/components';
|
||||
import { listResponsiveValues } from '../../../utils/list-values';
|
||||
import { responsiveProperties } from '../sprinkles.css';
|
||||
|
||||
export const PropsTable = () => {
|
||||
return (
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.HeaderRow>
|
||||
<Table.HeaderCell>Prop</Table.HeaderCell>
|
||||
<Table.HeaderCell>Type</Table.HeaderCell>
|
||||
</Table.HeaderRow>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{Object.keys(responsiveProperties.styles)
|
||||
.filter(
|
||||
n =>
|
||||
![
|
||||
'padding',
|
||||
'paddingX',
|
||||
'paddingY',
|
||||
'paddingLeft',
|
||||
'paddingRight',
|
||||
'paddingTop',
|
||||
'paddingBottom',
|
||||
'margin',
|
||||
'marginX',
|
||||
'marginY',
|
||||
'marginLeft',
|
||||
'marginRight',
|
||||
'marginTop',
|
||||
'marginBottom',
|
||||
].includes(n),
|
||||
)
|
||||
.map(n => (
|
||||
<Table.Row key={n}>
|
||||
<Table.Cell>
|
||||
<Chip head>{n}</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{listResponsiveValues(
|
||||
n as keyof typeof responsiveProperties.styles,
|
||||
).map(value => (
|
||||
<Chip key={value}>{value}</Chip>
|
||||
))}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import * as Table from '../../../../docs/components';
|
||||
import { Chip } from '../../../../docs/components';
|
||||
import { listResponsiveValues } from '../../../utils/list-values';
|
||||
import { responsiveProperties } from '../sprinkles.css';
|
||||
|
||||
console.log(responsiveProperties.styles);
|
||||
|
||||
export const SpacingTable = () => {
|
||||
return (
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.HeaderRow>
|
||||
<Table.HeaderCell>Prop</Table.HeaderCell>
|
||||
<Table.HeaderCell>Type</Table.HeaderCell>
|
||||
</Table.HeaderRow>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{Object.keys(responsiveProperties.styles)
|
||||
.filter(n =>
|
||||
[
|
||||
'padding',
|
||||
'paddingX',
|
||||
'paddingY',
|
||||
'paddingLeft',
|
||||
'paddingRight',
|
||||
'paddingTop',
|
||||
'paddingBottom',
|
||||
'margin',
|
||||
'marginX',
|
||||
'marginY',
|
||||
'marginLeft',
|
||||
'marginRight',
|
||||
'marginTop',
|
||||
'marginBottom',
|
||||
].includes(n),
|
||||
)
|
||||
.map(n => (
|
||||
<Table.Row key={n}>
|
||||
<Table.Cell>
|
||||
<Chip head>{n}</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{listResponsiveValues(
|
||||
'paddingTop' as keyof typeof responsiveProperties.styles,
|
||||
).map(value => (
|
||||
<Chip key={value}>{value}</Chip>
|
||||
))}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { Box } from './Box';
|
||||
export type { BoxProps } from './Box';
|
||||
export { boxSprinkles } from './sprinkles.css';
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const breakpoints = {
|
||||
xs: {},
|
||||
sm: { '@media': 'screen and (min-width: 640px)' },
|
||||
md: { '@media': 'screen and (min-width: 768px)' },
|
||||
lg: { '@media': 'screen and (min-width: 1024px)' },
|
||||
xl: { '@media': 'screen and (min-width: 1280px)' },
|
||||
'2xl': { '@media': 'screen and (min-width: 1536px)' },
|
||||
};
|
||||
|
||||
export const space = {
|
||||
none: 0,
|
||||
xxs: 'var(--canon-space-xxs)',
|
||||
xs: 'var(--canon-space-xs)',
|
||||
sm: 'var(--canon-space-sm)',
|
||||
md: 'var(--canon-space-md)',
|
||||
lg: 'var(--canon-space-lg)',
|
||||
xl: 'var(--canon-space-xl)',
|
||||
xxl: 'var(--canon-space-xxl)',
|
||||
};
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
defineProperties,
|
||||
createSprinkles,
|
||||
RequiredConditionalValue,
|
||||
ConditionalValue,
|
||||
createMapValueFn,
|
||||
} from '@vanilla-extract/sprinkles';
|
||||
import { breakpoints, space } from './properties';
|
||||
|
||||
export const responsiveProperties = defineProperties({
|
||||
conditions: breakpoints,
|
||||
defaultCondition: 'xs',
|
||||
responsiveArray: ['xs', 'sm', 'md', 'lg', 'xl', '2xl'],
|
||||
properties: {
|
||||
flexDirection: ['row', 'column'],
|
||||
justifyContent: [
|
||||
'stretch',
|
||||
'flex-start',
|
||||
'center',
|
||||
'flex-end',
|
||||
'space-around',
|
||||
'space-between',
|
||||
],
|
||||
alignItems: ['stretch', 'flex-start', 'center', 'flex-end'],
|
||||
borderRadius: {
|
||||
none: 0,
|
||||
small: '4px',
|
||||
medium: '8px',
|
||||
full: '9999px',
|
||||
},
|
||||
boxShadow: {
|
||||
small: 'var(--canon-box-shadow-small)',
|
||||
medium: 'var(--canon-box-shadow-medium)',
|
||||
large: 'var(--canon-box-shadow-large)',
|
||||
},
|
||||
border: {
|
||||
none: 'none',
|
||||
thin: '1px solid var(--canon-outline)',
|
||||
error: '1px solid var(--canon-error)',
|
||||
},
|
||||
display: ['none', 'flex', 'block', 'inline'],
|
||||
paddingTop: space,
|
||||
paddingBottom: space,
|
||||
paddingLeft: space,
|
||||
paddingRight: space,
|
||||
marginTop: space,
|
||||
marginBottom: space,
|
||||
marginLeft: space,
|
||||
marginRight: space,
|
||||
gap: space,
|
||||
flexWrap: ['wrap', 'nowrap'],
|
||||
},
|
||||
shorthands: {
|
||||
padding: ['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight'],
|
||||
paddingX: ['paddingLeft', 'paddingRight'],
|
||||
paddingY: ['paddingTop', 'paddingBottom'],
|
||||
margin: ['marginTop', 'marginBottom', 'marginLeft', 'marginRight'],
|
||||
marginX: ['marginLeft', 'marginRight'],
|
||||
marginY: ['marginTop', 'marginBottom'],
|
||||
},
|
||||
});
|
||||
|
||||
export const colorProperties = defineProperties({
|
||||
conditions: {
|
||||
light: { selector: '[data-theme="light"] &' },
|
||||
dark: { selector: '[data-theme="dark"] &' },
|
||||
},
|
||||
defaultCondition: ['light', 'dark'],
|
||||
properties: {
|
||||
color: {
|
||||
primary: 'var(--canon-text-primary)',
|
||||
secondary: 'var(--canon-text-secondary)',
|
||||
error: 'var(--canon-error)',
|
||||
},
|
||||
background: {
|
||||
background: 'var(--canon-background)',
|
||||
elevation1: 'var(--canon-surface-1)',
|
||||
elevation2: 'var(--canon-surface-2)',
|
||||
transparent: 'transparent',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/** @public */
|
||||
export const boxSprinkles = createSprinkles(
|
||||
responsiveProperties,
|
||||
colorProperties,
|
||||
);
|
||||
|
||||
export type OptionalResponsiveValue<Value extends string | number> =
|
||||
ConditionalValue<typeof responsiveProperties, Value>;
|
||||
|
||||
export type RequiredResponsiveValue<Value extends string | number> =
|
||||
RequiredConditionalValue<typeof responsiveProperties, Value>;
|
||||
|
||||
export const mapResponsiveValue = createMapValueFn(responsiveProperties);
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { Button } from './Button';
|
||||
import { Box } from '../Box/Box';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Button',
|
||||
component: Button,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
size: {
|
||||
control: 'select',
|
||||
options: ['small', 'medium'],
|
||||
},
|
||||
},
|
||||
args: {
|
||||
size: 'medium',
|
||||
},
|
||||
} satisfies Meta<typeof Button>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Primary: Story = {
|
||||
args: {
|
||||
children: 'Primary button',
|
||||
},
|
||||
};
|
||||
|
||||
export const Secondary: Story = {
|
||||
args: {
|
||||
children: 'Secondary button',
|
||||
variant: 'secondary',
|
||||
},
|
||||
};
|
||||
|
||||
export const Sizes: Story = {
|
||||
args: {
|
||||
children: 'Button',
|
||||
},
|
||||
render: args => (
|
||||
<div style={{ display: 'flex', gap: '10px' }}>
|
||||
<Button {...args} size="small" />
|
||||
<Button {...args} size="medium" />
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
export const WithIcons: Story = {
|
||||
args: {
|
||||
children: 'Button',
|
||||
},
|
||||
render: args => (
|
||||
<div style={{ display: 'flex', gap: '10px' }}>
|
||||
<Button {...args} iconStart="Cloud" />
|
||||
<Button {...args} iconEnd="ArrowRight" />
|
||||
<Button {...args} iconStart="Cloud" iconEnd="ArrowRight" />
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
export const FullWidth: Story = {
|
||||
args: {
|
||||
children: 'Button',
|
||||
},
|
||||
render: args => (
|
||||
<Box>
|
||||
<Button {...args} iconStart="Cloud" />
|
||||
<Button {...args} iconEnd="ArrowRight" />
|
||||
<Button {...args} iconStart="Cloud" iconEnd="ArrowRight" />
|
||||
</Box>
|
||||
),
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
children: 'Button',
|
||||
disabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const CustomTheme: Story = {
|
||||
args: {
|
||||
children: 'Custom Button',
|
||||
},
|
||||
decorators: [
|
||||
Story => (
|
||||
<div
|
||||
style={
|
||||
{
|
||||
'--button-primary-background-color': 'blue',
|
||||
'--button-primary-border-color': 'blue',
|
||||
'--button-primary-text-color': 'white',
|
||||
'--button-primary-border-radius': '8px',
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
<Story />
|
||||
</div>
|
||||
),
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { button } from './button.css';
|
||||
import { Box } from '../Box/Box';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
import { IconNames } from '../Icon/context';
|
||||
|
||||
/**
|
||||
* Properties for {@link Button}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface ButtonProps {
|
||||
size?: 'small' | 'medium';
|
||||
variant?: 'primary' | 'secondary';
|
||||
children: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
iconStart?: IconNames;
|
||||
iconEnd?: IconNames;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const Button = ({
|
||||
size = 'medium',
|
||||
variant = 'primary',
|
||||
children,
|
||||
disabled,
|
||||
iconStart,
|
||||
iconEnd,
|
||||
...props
|
||||
}: ButtonProps) => {
|
||||
return (
|
||||
<Box
|
||||
{...props}
|
||||
as="button"
|
||||
disabled={disabled}
|
||||
className={button({ size, variant, disabled })}
|
||||
>
|
||||
{iconStart && <Icon name={iconStart} />}
|
||||
{children}
|
||||
{iconEnd && <Icon name={iconEnd} />}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Button;
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { recipe } from '@vanilla-extract/recipes';
|
||||
|
||||
export const button = recipe({
|
||||
base: {
|
||||
all: 'unset',
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
userSelect: 'none',
|
||||
fontFamily: 'var(--canon-font-sans)',
|
||||
fontWeight: 600,
|
||||
fontSize: '0.875rem',
|
||||
lineHeight: '1.5',
|
||||
padding: 0,
|
||||
transition: 'all 150ms ease',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
variants: {
|
||||
variant: {
|
||||
primary: {
|
||||
backgroundColor: 'var(--button-primary-background-color)',
|
||||
color: 'var(--button-primary-text-color)',
|
||||
borderRadius: 'var(--button-primary-border-radius)',
|
||||
border: `1px solid var(--button-primary-border-color)`,
|
||||
'&:hover': {
|
||||
backgroundColor: 'var(--button-primary-background-color-hover)',
|
||||
borderColor: 'var(--button-primary-border-color-hover)',
|
||||
},
|
||||
},
|
||||
secondary: {
|
||||
backgroundColor: 'transparent',
|
||||
color: 'var(--button-secondary-text-color)',
|
||||
borderRadius: 'var(--button-secondary-border-radius)',
|
||||
border: `1px solid var(--button-secondary-border-color)`,
|
||||
'&:hover': {
|
||||
backgroundColor: 'var(--button-secondary-background-color-hover)',
|
||||
borderColor: 'var(--button-secondary-border-color-hover)',
|
||||
},
|
||||
},
|
||||
},
|
||||
size: {
|
||||
small: {
|
||||
paddingLeft: '16px',
|
||||
paddingRight: '16px',
|
||||
height: 'var(--button-size-small)',
|
||||
},
|
||||
medium: {
|
||||
paddingLeft: '32px',
|
||||
paddingRight: '32px',
|
||||
height: 'var(--button-size-medium)',
|
||||
},
|
||||
},
|
||||
disabled: {
|
||||
true: {
|
||||
opacity: 0.3,
|
||||
cursor: 'not-allowed',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { Button } from './Button';
|
||||
export type { ButtonProps } from './Button';
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
:root {
|
||||
--button-primary-background-color: #1ed760;
|
||||
--button-primary-background-color-hover: #3be477;
|
||||
--button-primary-background-color-active: #1abc54;
|
||||
--button-primary-border-color: #1ed760;
|
||||
--button-primary-border-color-hover: #3be477;
|
||||
--button-primary-border-color-active: #1abc54;
|
||||
--button-primary-text-color: #000;
|
||||
--button-primary-border-radius: 9999px;
|
||||
--button-secondary-background-color: transparent;
|
||||
--button-secondary-background-color-hover: transparent;
|
||||
--button-secondary-background-color-active: rgba(0, 0, 0, 0.03);
|
||||
--button-secondary-border-color: #000;
|
||||
--button-secondary-border-color-hover: #000;
|
||||
--button-secondary-border-color-active: #000;
|
||||
--button-secondary-text-color: #000;
|
||||
--button-secondary-border-radius: 9999px;
|
||||
--button-size-small: 32px;
|
||||
--button-size-medium: 48px;
|
||||
}
|
||||
|
||||
[data-theme='dark'] {
|
||||
--button-primary-background-color: #1ed760;
|
||||
--button-primary-background-color-hover: #3be477;
|
||||
--button-primary-background-color-active: #1abc54;
|
||||
--button-primary-border-color: #1ed760;
|
||||
--button-primary-border-color-hover: #3be477;
|
||||
--button-primary-border-color-active: #1abc54;
|
||||
--button-primary-text-color: #000;
|
||||
--button-primary-border-radius: 9999px;
|
||||
--button-secondary-background-color: transparent;
|
||||
--button-secondary-background-color-hover: transparent;
|
||||
--button-secondary-background-color-active: rgba(255, 255, 255, 0.1);
|
||||
--button-secondary-border-color: #fff;
|
||||
--button-secondary-border-color-hover: #fff;
|
||||
--button-secondary-border-color-active: #fff;
|
||||
--button-secondary-text-color: #fff;
|
||||
--button-secondary-border-radius: 9999px;
|
||||
--button-size-small: 32px;
|
||||
--button-size-medium: 48px;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { Icon } from './Icon';
|
||||
import { IconProvider } from './context';
|
||||
import * as LucideIcons from 'lucide-react';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Icon',
|
||||
component: Icon,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
name: {
|
||||
control: 'select',
|
||||
options: Object.keys(LucideIcons),
|
||||
},
|
||||
},
|
||||
args: {
|
||||
name: 'ArrowDown',
|
||||
},
|
||||
} satisfies Meta<typeof Icon>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Primary: Story = {
|
||||
args: {
|
||||
name: 'ArrowDown',
|
||||
},
|
||||
};
|
||||
|
||||
export const CustomIcon: Story = {
|
||||
args: {
|
||||
name: 'CustomIcon',
|
||||
},
|
||||
};
|
||||
|
||||
export const WithCustomIcon: Story = {
|
||||
args: {
|
||||
name: 'ArrowDown',
|
||||
},
|
||||
decorators: [
|
||||
Story => (
|
||||
<IconProvider overrides={{ ArrowDown: () => <div>Custom Icon</div> }}>
|
||||
<Story />
|
||||
</IconProvider>
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
export const WithCustomIconOverride: Story = {
|
||||
args: {
|
||||
name: 'CustomIcon',
|
||||
},
|
||||
decorators: [
|
||||
Story => (
|
||||
<IconProvider
|
||||
overrides={{ CustomIcon: () => <div>Custom Super Icon</div> }}
|
||||
>
|
||||
<Story />
|
||||
</IconProvider>
|
||||
),
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useIcons, IconNames } from './context';
|
||||
|
||||
/** @public */
|
||||
export const Icon = ({ name }: { name: IconNames }) => {
|
||||
const { icons } = useIcons();
|
||||
|
||||
const LucideIcon = icons[name];
|
||||
|
||||
if (!LucideIcon) {
|
||||
console.error(`Icon "${name}" not found.`);
|
||||
return <svg />; // Return default icon perhaps?
|
||||
}
|
||||
|
||||
return <LucideIcon />;
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { createContext, useContext, ReactNode } from 'react';
|
||||
import { ArrowUp, ArrowDown, ArrowLeft, ArrowRight, Cloud } from 'lucide-react';
|
||||
import { CustomIcon } from './custom-icon';
|
||||
|
||||
// List of icons available that can also be overridden.
|
||||
/** @public */
|
||||
export type IconNames =
|
||||
| 'ArrowDown'
|
||||
| 'ArrowLeft'
|
||||
| 'ArrowRight'
|
||||
| 'ArrowUp'
|
||||
| 'Cloud'
|
||||
| 'CustomIcon';
|
||||
|
||||
type IconMap = Partial<Record<IconNames, React.ComponentType>>;
|
||||
|
||||
interface IconContextProps {
|
||||
icons: IconMap;
|
||||
}
|
||||
|
||||
// Create a default icon map with only the necessary icons
|
||||
const defaultIcons: IconMap = {
|
||||
ArrowDown,
|
||||
ArrowLeft,
|
||||
ArrowRight,
|
||||
ArrowUp,
|
||||
Cloud,
|
||||
CustomIcon,
|
||||
};
|
||||
|
||||
const IconContext = createContext<IconContextProps>({ icons: defaultIcons });
|
||||
|
||||
/** @public */
|
||||
export const IconProvider = ({
|
||||
children,
|
||||
overrides,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
overrides: Partial<Record<IconNames, React.ComponentType>>;
|
||||
}) => {
|
||||
// Merge provided overrides with default icons
|
||||
const combinedIcons = { ...defaultIcons, ...overrides };
|
||||
|
||||
return (
|
||||
<IconContext.Provider value={{ icons: combinedIcons }}>
|
||||
{children}
|
||||
</IconContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export const useIcons = () => useContext(IconContext);
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export const CustomIcon = () => (
|
||||
<svg viewBox="0 0 24 24" fill="red" width={24} height={24}>
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" />
|
||||
</svg>
|
||||
);
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './Icon';
|
||||
export { IconProvider } from './context';
|
||||
export type { IconNames } from './context';
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { Inline } from './Inline';
|
||||
import { Box } from '../Box/Box';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Inline',
|
||||
component: Inline,
|
||||
} satisfies Meta<typeof Inline>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
const FakeBox = () => (
|
||||
<Box
|
||||
paddingX="xl"
|
||||
paddingY="md"
|
||||
borderRadius="small"
|
||||
style={{ background: '#1f47ff', color: 'white' }}
|
||||
>
|
||||
Fake Box
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: (
|
||||
<>
|
||||
<FakeBox />
|
||||
<FakeBox />
|
||||
<FakeBox />
|
||||
</>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
export const LargeGap: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
gap: 'xl',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Box, BoxProps } from '../Box/Box';
|
||||
import { alignToFlexAlign } from '../../utils/align';
|
||||
|
||||
export const validInlineComponents = [
|
||||
'div',
|
||||
'span',
|
||||
'p',
|
||||
'nav',
|
||||
'ul',
|
||||
'ol',
|
||||
'li',
|
||||
] as const;
|
||||
|
||||
export interface InlineProps extends Omit<BoxProps, 'alignItems'> {
|
||||
as?: (typeof validInlineComponents)[number];
|
||||
children: React.ReactNode;
|
||||
align?: 'left' | 'center' | 'right';
|
||||
gap?: BoxProps['gap'];
|
||||
}
|
||||
|
||||
export const Inline = ({
|
||||
align,
|
||||
as = 'div',
|
||||
children,
|
||||
gap = 'xs',
|
||||
...restProps
|
||||
}: InlineProps) => {
|
||||
return (
|
||||
<Box
|
||||
as={as}
|
||||
display="flex"
|
||||
alignItems={align !== 'left' ? alignToFlexAlign(align) : undefined}
|
||||
justifyContent="flex-start"
|
||||
flexWrap="wrap"
|
||||
gap={gap}
|
||||
{...restProps}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { Inline } from './Inline';
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { Stack } from './Stack';
|
||||
import { Box } from '../Box/Box';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Stack',
|
||||
component: Stack,
|
||||
} satisfies Meta<typeof Stack>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
const FakeBox = () => (
|
||||
<Box
|
||||
paddingX="xl"
|
||||
paddingY="md"
|
||||
borderRadius="small"
|
||||
style={{ background: '#1f47ff', color: 'white' }}
|
||||
>
|
||||
Fake Box
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: (
|
||||
<>
|
||||
<FakeBox />
|
||||
<FakeBox />
|
||||
<FakeBox />
|
||||
</>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
export const LargeGap: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
gap: 'xl',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Box, BoxProps } from '../Box/Box';
|
||||
import { alignToFlexAlign } from '../../utils/align';
|
||||
|
||||
const validStackComponents = [
|
||||
'div',
|
||||
'span',
|
||||
'p',
|
||||
'article',
|
||||
'section',
|
||||
'main',
|
||||
'nav',
|
||||
'aside',
|
||||
'ul',
|
||||
'ol',
|
||||
'li',
|
||||
'details',
|
||||
'summary',
|
||||
'dd',
|
||||
'dl',
|
||||
'dt',
|
||||
] as const;
|
||||
|
||||
export interface StackProps extends Omit<BoxProps, 'alignItems'> {
|
||||
children: React.ReactNode;
|
||||
as?: (typeof validStackComponents)[number];
|
||||
align?: 'left' | 'center' | 'right';
|
||||
gap?: BoxProps['gap'];
|
||||
}
|
||||
|
||||
export const Stack = ({
|
||||
as = 'div',
|
||||
children,
|
||||
align: alignProp,
|
||||
gap = 'xs',
|
||||
...restProps
|
||||
}: StackProps) => {
|
||||
/**
|
||||
* Creating a seam between the provided prop and the default value
|
||||
* to enable only setting the text alignment when the `align` prop
|
||||
* is provided — not when it's defaulted.
|
||||
*/
|
||||
const align = alignProp || 'left';
|
||||
|
||||
return (
|
||||
<Box
|
||||
as={as}
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
alignItems={align !== 'left' ? alignToFlexAlign(align) : undefined}
|
||||
gap={gap}
|
||||
// textAlign={alignProp}
|
||||
{...restProps}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { Stack } from './Stack';
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { defineProperties, createSprinkles } from '@vanilla-extract/sprinkles';
|
||||
import { breakpoints, space } from '../Box/properties';
|
||||
import { colorProperties } from '../Box/sprinkles.css';
|
||||
|
||||
const responsiveProperties = defineProperties({
|
||||
conditions: breakpoints,
|
||||
defaultCondition: 'xs',
|
||||
properties: {
|
||||
flexDirection: ['row', 'column'],
|
||||
justifyContent: [
|
||||
'stretch',
|
||||
'flex-start',
|
||||
'center',
|
||||
'flex-end',
|
||||
'space-around',
|
||||
'space-between',
|
||||
],
|
||||
alignItems: ['stretch', 'flex-start', 'center', 'flex-end'],
|
||||
borderRadius: {
|
||||
none: 0,
|
||||
small: '4px',
|
||||
medium: '8px',
|
||||
full: '9999px',
|
||||
},
|
||||
boxShadow: {
|
||||
small: 'var(--canon-box-shadow-small)',
|
||||
medium: 'var(--canon-box-shadow-medium)',
|
||||
large: 'var(--canon-box-shadow-large)',
|
||||
},
|
||||
border: {
|
||||
none: 'none',
|
||||
thin: '1px solid var(--canon-outline)',
|
||||
error: '1px solid var(--canon-error)',
|
||||
},
|
||||
display: ['none', 'flex', 'block', 'inline'],
|
||||
paddingTop: space,
|
||||
paddingBottom: space,
|
||||
paddingLeft: space,
|
||||
paddingRight: space,
|
||||
marginTop: space,
|
||||
marginBottom: space,
|
||||
marginLeft: space,
|
||||
marginRight: space,
|
||||
gap: space,
|
||||
flexWrap: ['wrap', 'nowrap'],
|
||||
},
|
||||
shorthands: {
|
||||
p: ['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight'],
|
||||
pt: ['paddingTop'],
|
||||
pr: ['paddingRight'],
|
||||
pb: ['paddingBottom'],
|
||||
pl: ['paddingLeft'],
|
||||
px: ['paddingLeft', 'paddingRight'],
|
||||
py: ['paddingTop', 'paddingBottom'],
|
||||
m: ['marginTop', 'marginBottom', 'marginLeft', 'marginRight'],
|
||||
mt: ['marginTop'],
|
||||
mr: ['marginRight'],
|
||||
mb: ['marginBottom'],
|
||||
ml: ['marginLeft'],
|
||||
mx: ['marginLeft', 'marginRight'],
|
||||
my: ['marginTop', 'marginBottom'],
|
||||
direction: ['flexDirection'],
|
||||
items: ['alignItems'],
|
||||
justify: ['justifyContent'],
|
||||
},
|
||||
});
|
||||
|
||||
export const sprinkles = createSprinkles(responsiveProperties, colorProperties);
|
||||
|
||||
// It's a good idea to export the Sprinkles type too
|
||||
export type Sprinkles = Parameters<typeof sprinkles>[0];
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Components used by Backstage plugins and apps
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export * from './components/Button';
|
||||
export * from './components/Box';
|
||||
export * from './components/Icon';
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* Geist font */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Geist:wght@100..900&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&display=swap');
|
||||
|
||||
/* Theme */
|
||||
@import './theme.css';
|
||||
|
||||
/* Components */
|
||||
@import '../components/Button/styles.css';
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* Light theme */
|
||||
:root {
|
||||
--canon-accent: #1ed760;
|
||||
--canon-background: #f8f8f8;
|
||||
--canon-surface-1: #fff;
|
||||
--canon-surface-2: #f4f4f4;
|
||||
--canon-outline: #666;
|
||||
--canon-outline-focus: #ccc;
|
||||
--canon-error: #f50000;
|
||||
--canon-text-primary: #000;
|
||||
--canon-text-secondary: #646464;
|
||||
--canon-box-shadow-small: 0 0 0 1px rgba(0, 0, 0, 0.05);
|
||||
--canon-box-shadow-medium: 0 0 0 1px rgba(0, 0, 0, 0.1);
|
||||
--canon-box-shadow-large: 0 0 0 1px rgba(0, 0, 0, 0.2);
|
||||
|
||||
--canon-font-sans: 'Geist', serif;
|
||||
--canon-font-monospace: 'Monospace', monospace;
|
||||
--canon-font-emoji: 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
|
||||
'Noto Color Emoji';
|
||||
|
||||
--canon-space-unit: 1em;
|
||||
--canon-space-xxs: calc(0.25 * var(--canon-space-unit));
|
||||
--canon-space-xs: calc(0.5 * var(--canon-space-unit));
|
||||
--canon-space-sm: calc(0.75 * var(--canon-space-unit));
|
||||
--canon-space-md: calc(1.25 * var(--canon-space-unit));
|
||||
--canon-space-lg: calc(2 * var(--canon-space-unit));
|
||||
--canon-space-xl: calc(3.25 * var(--canon-space-unit));
|
||||
--canon-space-xxl: calc(5.25 * var(--canon-space-unit));
|
||||
}
|
||||
|
||||
/* Dark theme */
|
||||
[data-theme='dark'] {
|
||||
--canon-accent: #1ed760;
|
||||
--canon-background: #000;
|
||||
--canon-surface-1: #121212;
|
||||
--canon-surface-2: #292929;
|
||||
--canon-outline: #666;
|
||||
--canon-outline-focus: #ccc;
|
||||
--canon-text-primary: #f0f0f0;
|
||||
--canon-text-secondary: #666;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
type OptionalResponsiveValue,
|
||||
mapResponsiveValue,
|
||||
} from '../components/Box/sprinkles.css';
|
||||
|
||||
export type Align = 'left' | 'center' | 'right';
|
||||
export type AlignY = 'top' | 'center' | 'bottom';
|
||||
|
||||
const alignToFlexAlignLookup = {
|
||||
left: 'flex-start',
|
||||
center: 'center',
|
||||
right: 'flex-end',
|
||||
} as const;
|
||||
|
||||
export const alignToFlexAlign = (
|
||||
align: OptionalResponsiveValue<Align> | undefined,
|
||||
) =>
|
||||
align
|
||||
? mapResponsiveValue(align, value => alignToFlexAlignLookup[value])
|
||||
: undefined;
|
||||
|
||||
const alignYToFlexAlignLookup = {
|
||||
top: 'flex-start',
|
||||
center: 'center',
|
||||
bottom: 'flex-end',
|
||||
} as const;
|
||||
|
||||
export const alignYToFlexAlign = (
|
||||
alignY: OptionalResponsiveValue<AlignY> | undefined,
|
||||
) =>
|
||||
alignY
|
||||
? mapResponsiveValue(alignY, value => alignYToFlexAlignLookup[value])
|
||||
: undefined;
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
responsiveProperties,
|
||||
colorProperties,
|
||||
} from '../components/Box/sprinkles.css';
|
||||
|
||||
export const listResponsiveValues = (
|
||||
value: keyof typeof responsiveProperties.styles,
|
||||
) => {
|
||||
const values = responsiveProperties.styles[value];
|
||||
|
||||
if ('values' in values) {
|
||||
return Object.keys(values.values);
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
export const listColorValues = (value: keyof typeof colorProperties.styles) => {
|
||||
const values = colorProperties.styles[value];
|
||||
|
||||
if ('values' in values) {
|
||||
return Object.keys(values.values);
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
Reference in New Issue
Block a user