first commit
Copilot Setup Steps / copilot-setup-steps (push) Has been cancelled

This commit is contained in:
2026-04-22 19:51:20 +07:00
commit 93d1b7c3d3
579 changed files with 99797 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
const tailwindCssConfig = `${__dirname}/../admin/src/index.css`;
module.exports = {
extends: [
'plugin:ghost/ts',
'plugin:react/recommended',
'plugin:react-hooks/recommended'
],
plugins: [
'ghost',
'react-refresh',
'tailwindcss'
],
settings: {
react: {
version: 'detect'
},
tailwindcss: {
config: tailwindCssConfig
}
},
rules: {
// suppress errors for missing 'import React' in JSX files, as we don't need it
'react/react-in-jsx-scope': 'off',
// ignore prop-types for now
'react/prop-types': 'off',
'react/jsx-sort-props': ['error', {
reservedFirst: true,
callbacksLast: true,
shorthandLast: true,
locale: 'en'
}],
'react/button-has-type': 'error',
'react/no-array-index-key': 'error',
'react/jsx-key': 'off',
// Enforce kebab-case (lowercase with hyphens) for all filenames
'ghost/filenames/match-regex': ['error', '^[a-z0-9.-]+$', false],
'tailwindcss/classnames-order': 'error',
'tailwindcss/enforces-negative-arbitrary-values': 'warn',
'tailwindcss/enforces-shorthand': 'warn',
'tailwindcss/migration-from-tailwind-2': 'warn',
'tailwindcss/no-arbitrary-value': 'off',
'tailwindcss/no-custom-classname': 'off',
'tailwindcss/no-contradicting-classname': 'error'
}
};
+2
View File
@@ -0,0 +1,2 @@
es
types
Binary file not shown.
@@ -0,0 +1,38 @@
import {create} from '@storybook/theming/create';
export default create({
base: 'light',
// Typography
fontBase: '"Inter", sans-serif',
fontCode: 'monospace',
brandTitle: 'AdminX Design System',
brandUrl: 'https://ghost.org',
brandImage: 'https://github.com/peterzimon/playground/assets/353959/c4358b4e-232f-4dba-8abb-adb3142ccd89',
brandTarget: '_self',
//
colorPrimary: '#30CF43',
colorSecondary: '#15171A',
// UI
appBg: '#ffffff',
appContentBg: '#ffffff',
appBorderColor: '#EBEEF0',
appBorderRadius: 0,
// Text colors
textColor: '#15171A',
textInverseColor: '#ffffff',
// Toolbar default and active colors
barTextColor: '#9E9E9E',
barSelectedColor: '#15171A',
barBg: '#ffffff',
// Form colors
inputBg: '#ffffff',
inputBorder: '#15171A',
inputTextColor: '#15171A',
inputBorderRadius: 2,
});
@@ -0,0 +1,27 @@
import type { StorybookConfig } from "@storybook/react-vite";
const config: StorybookConfig = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
{
name: '@storybook/addon-styling',
},
],
framework: {
name: "@storybook/react-vite",
options: {},
},
docs: {
autodocs: "tag",
},
async viteFinal(config, options) {
config.resolve!.alias = {
crypto: require.resolve('rollup-plugin-node-builtins')
}
return config;
}
};
export default config;
@@ -0,0 +1,6 @@
import {addons} from '@storybook/manager-api';
import adminxTheme from './adminx-theme';
addons.setConfig({
theme: adminxTheme
});
@@ -0,0 +1,107 @@
import React from 'react';
import '../styles.css';
import './storybook.css';
import type { Preview } from "@storybook/react";
import DesignSystemProvider from '../src/providers/design-system-provider';
import adminxTheme from './adminx-theme';
// import { MINIMAL_VIEWPORTS } from '@storybook/addon-viewport';
const customViewports = {
sm: {
name: 'sm',
styles: {
width: '480px',
height: '801px',
},
},
md: {
name: 'md',
styles: {
width: '640px',
height: '801px',
},
},
lg: {
name: 'lg',
styles: {
width: '1024px',
height: '801px',
},
},
xl: {
name: 'xl',
styles: {
width: '1320px',
height: '801px',
},
},
tablet: {
name: 'tablet',
styles: {
width: '860px',
height: '801px',
},
},
};
const preview: Preview = {
parameters: {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
options: {
storySort: {
method: 'alphabetical',
order: ['Welcome', 'Foundations', ['Style Guide', 'Colors', 'Icons', 'ErrorHandling'], 'Global', ['Form', 'Chrome', 'Modal', 'Layout', ['View Container', 'Page Header', 'Page'], 'List', 'Table', '*'], 'Settings', ['Setting Section', 'Setting Group', '*'], 'Experimental'],
},
},
docs: {
theme: adminxTheme,
},
viewport: {
viewports: {
...customViewports,
},
},
},
decorators: [
(Story, context) => {
let {scheme} = context.globals;
return (
<div className={`admin-x-design-system admin-x-base ${scheme === 'dark' ? 'dark' : ''}`} style={{
// padding: '24px',
// width: 'unset',
height: 'unset',
// overflow: 'unset',
background: (scheme === 'dark' ? '#131416' : '')
}}>
{/* 👇 Decorators in Storybook also accept a function. Replace <Story/> with Story() to enable it */}
<DesignSystemProvider fetchKoenigLexical={async () => {}}>
<Story />
</DesignSystemProvider>
</div>);
},
],
globalTypes: {
scheme: {
name: "Scheme",
description: "Select light or dark mode",
defaultValue: "light",
toolbar: {
icon: "mirror",
items: ["light", "dark"],
dynamicTitle: true
}
}
}
};
export default preview;
@@ -0,0 +1,247 @@
/*
* We load Inter in Ember admin, so loading it explicitly here makes the final rendering
* in Storybook match the final rendering when embedded in Ember
*/
@font-face {
font-family: "Inter";
src: url("./Inter.ttf") format("truetype-variations");
font-weight: 100 900;
}
:root {
font-size: 62.5%;
line-height: 1.5;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
html, body, #root {
width: 100%;
height: 100%;
margin: 0;
letter-spacing: unset;
}
.sbdocs-wrapper {
padding: 3vmin !important;
}
.sbdocs-wrapper .sbdocs-content {
max-width: 1320px;
}
.sb-doc {
max-width: 740px;
width: 100%;
margin: 0 auto !important;
}
.sb-doc,
.sb-doc a,
.sb-doc h1,
.sb-doc h2,
.sb-doc h3,
.sb-doc h4,
.sb-doc h5,
.sb-doc h6,
.sb-doc p,
.sb-doc ul li,
.sbdocs-title,
.sb-doc ol li {
font-family: Inter, sans-serif !important;
padding: 0 !important;
}
.sb-doc a {
color: #30CF43;
}
.sb-doc h1 {
font-size: 48px !important;
letter-spacing: -0.04em !important;
margin-bottom: 20px;
}
.sb-doc h2 {
margin-top: 40px !important;
font-size: 27px;
border: none;
margin-bottom: 2px;
}
.sb-doc h3 {
margin-top: 40px !important;
margin-bottom: 4px !important;
font-size: 20px;
}
.sb-doc h4 {
margin: 0 0 4px !important;
}
.sb-doc p,
.sb-doc div,
.sb-doc ul li,
.sb-doc ol li {
font-size: 15px;
line-height: 1.5em;
}
.sb-doc ul li,
.sb-doc ol li {
margin-bottom: 8px;
}
.sb-doc h2 + p,
.sb-doc h3 + p {
margin-top: 8px;
}
.sb-doc img,
.sb-wide img {
margin-top: 40px !important;
margin-bottom: 40px !important;
}
.sb-doc img.small {
max-width: 520px;
margin: 0 auto;
display: block;
}
.sb-doc p.excerpt {
font-size: 19px;
letter-spacing: -0.02em;
}
.sb-doc .highlight {
padding: 12px 20px;
border-radius: 4px;
background: #EBEEF0;
}
.sb-doc .highlight.purple {
background: #F0E9FA;
}
.sb-doc .highlight.purple a {
color: #8E42FF;
}
/* Welcome */
.sb-doc img.main-image {
margin-top: -2vmin !important;
margin-left: -44px;
margin-right: -32px;
margin-bottom: 0 !important;
max-width: unset;
width: calc(100% + 64px);
}
.sb-doc .main-structure-container {
display: flex;
gap: 32px;
margin: 32px 0 80px;
}
.sb-doc .main-structure-container div {
flex-basis: 33%;
}
.sb-doc .main-structure-container div p {
display: flex;
flex-direction: column;
gap: 4px;
}
.sb-doc .main-structure-container img {
margin: 12px 0 !important;
width: 32px;
height: 32px;
}
.sb-doc .main-structure-container div h4 {
border-bottom: 1px solid #EBEEF0;
padding-bottom: 8px !important;
margin-bottom: 8px !important;
}
.sb-doc .main-structure-container div p {
margin: 0;
font-size: 13.5px;
}
/* Colors */
.color-grid {
display: flex;
gap: 20px;
flex-wrap: wrap;
margin-top: 20px;
}
.color-grid div {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 12px;
border-radius: 4px;
border: 1px solid #EBEEF0;
}
.color-grid .swatch {
display: block;
background: #EFEFEF;
border-radius: 100%;
width: 28px;
height: 28px;
}
.swatch.green {
background: #30CF43;
}
.swatch.black {
background: #15171A;
}
.swatch.white {
background: #FFFFFF;
border: 1px solid #EBEEF0;
}
.swatch.lime {
background: #B5FF18;
}
.swatch.blue {
background: #14B8FF;
}
.swatch.purple {
background: #8E42FF;
}
.swatch.pink {
background: #FB2D8D;
}
.swatch.yellow {
background: #FFB41F;
}
.swatch.red {
background: #F50B23;
}
/* Icons */
.sb-doc .streamline {
display: grid;
grid-template-columns: auto 240px;
gap: 32px;
}
.sbdocs-a {
color: #30CF43 !important;
}
+17
View File
@@ -0,0 +1,17 @@
# Admin X Design
Components, design guidelines and documentation for building apps in Ghost Admin
## Develop
This is a monorepo package.
Follow the instructions for the top-level repo.
1. `git clone` this repo & `cd` into it as usual
2. Run `pnpm` to install top-level dependencies.
## Test
- `pnpm lint` run just eslint
- `pnpm test` run lint and tests
+112
View File
@@ -0,0 +1,112 @@
{
"name": "@tryghost/admin-x-design-system",
"type": "module",
"version": "0.0.0",
"repository": "https://github.com/TryGhost/Ghost/tree/main/packages/admin-x-design-system",
"author": "Ghost Foundation",
"private": true,
"main": "es/index.js",
"types": "types/index.d.ts",
"sideEffects": false,
"scripts": {
"dev": "vite build --watch",
"build": "tsc -p tsconfig.declaration.json && vite build",
"test": "pnpm test:unit",
"test:unit": "pnpm test:types && vitest run",
"test:types": "tsc --noEmit",
"lint:code": "eslint --ext .js,.ts,.cjs,.tsx src/ --cache",
"lint": "pnpm lint:code && pnpm lint:test",
"lint:test": "eslint -c test/.eslintrc.cjs --ext .js,.ts,.cjs,.tsx test/ --cache",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"files": [
"es",
"types"
],
"devDependencies": {
"@codemirror/lang-html": "6.4.11",
"@codemirror/state": "6.6.0",
"@dnd-kit/utilities": "^3.2.2",
"@radix-ui/react-tooltip": "1.2.8",
"@storybook/addon-essentials": "8.6.14",
"@storybook/addon-interactions": "8.6.14",
"@storybook/addon-links": "8.6.14",
"@storybook/addon-styling": "1.3.7",
"@storybook/blocks": "8.6.14",
"@storybook/preview-api": "^8.6.14",
"@storybook/react": "8.6.14",
"@storybook/react-vite": "8.6.14",
"@storybook/testing-library": "0.2.2",
"@tailwindcss/postcss": "4.2.1",
"@testing-library/react": "14.3.1",
"@testing-library/react-hooks": "8.0.1",
"@types/lodash-es": "4.17.12",
"@types/react": "18.3.28",
"@types/react-dom": "18.3.7",
"@types/validator": "13.15.10",
"@vitejs/plugin-react": "4.7.0",
"autoprefixer": "10.4.21",
"c8": "10.1.3",
"chai": "4.5.0",
"eslint": "catalog:",
"eslint-plugin-react-hooks": "4.6.2",
"eslint-plugin-react-refresh": "0.4.24",
"eslint-plugin-tailwindcss": "4.0.0-beta.0",
"glob": "^10.5.0",
"jsdom": "28.1.0",
"lodash-es": "4.18.1",
"postcss": "8.5.6",
"postcss-import": "16.1.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"rollup-plugin-node-builtins": "2.1.2",
"sinon": "18.0.1",
"storybook": "8.6.15",
"tailwindcss": "4.2.1",
"typescript": "5.9.3",
"validator": "13.12.0",
"vite": "5.4.21",
"vite-plugin-svgr": "3.3.0",
"vitest": "1.6.1"
},
"dependencies": {
"@dnd-kit/core": "6.3.1",
"@dnd-kit/sortable": "7.0.2",
"@ebay/nice-modal-react": "1.2.13",
"@radix-ui/react-avatar": "1.1.11",
"@radix-ui/react-checkbox": "1.3.3",
"@radix-ui/react-form": "0.1.8",
"@radix-ui/react-popover": "1.1.15",
"@radix-ui/react-radio-group": "1.3.8",
"@radix-ui/react-separator": "1.1.8",
"@radix-ui/react-switch": "1.2.6",
"@radix-ui/react-tabs": "1.1.13",
"@radix-ui/react-tooltip": "1.2.8",
"@sentry/react": "7.120.4",
"@tryghost/shade": "workspace:*",
"@uiw/react-codemirror": "4.25.2",
"clsx": "2.1.1",
"react-colorful": "5.6.1",
"react-hot-toast": "2.6.0",
"react-select": "5.10.2"
},
"peerDependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"nx": {
"targets": {
"build": {
"dependsOn": [
"^build"
]
},
"test:unit": {
"dependsOn": [
"^build"
]
}
}
}
}
@@ -0,0 +1,7 @@
module.exports = {
plugins: {
'postcss-import': {},
'@tailwindcss/postcss': {},
autoprefixer: {}
}
};
+381
View File
@@ -0,0 +1,381 @@
.admin-x-base {
/*
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
*/
*,
::before,
::after {
box-sizing: border-box; /* 1 */
max-width: revert;
max-height: revert;
min-width: revert;
min-height: revert;
border-width: 0; /* 2 */
border-style: solid; /* 2 */
border-color: theme('borderColor.DEFAULT', currentColor); /* 2 */
}
::before,
::after {
--tw-content: '';
}
/*
1. Use a consistent sensible line-height in all browsers.
2. Prevent adjustments of font size after orientation changes in iOS.
3. Use a more readable tab size.
4. Use the user's configured `sans` font-family by default.
*/
html {
line-height: 1.5; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
-moz-tab-size: 4; /* 3 */
tab-size: 4; /* 3 */
font-family: theme('fontFamily.sans', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); /* 4 */
}
/*
1. Remove the margin in all browsers.
2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
*/
body {
margin: 0; /* 1 */
line-height: inherit; /* 2 */
}
/*
1. Add the correct height in Firefox.
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
3. Ensure horizontal rules are visible by default.
*/
hr {
height: 0; /* 1 */
color: inherit; /* 2 */
border-top-width: 1px; /* 3 */
}
/*
Add the correct text decoration in Chrome, Edge, and Safari.
*/
abbr:where([title]) {
text-decoration: underline dotted;
}
/*
Remove the default font size and weight for headings.
*/
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
padding: 0;
}
/*
Reset links to optimize for opt-in styling instead of opt-out.
*/
a {
color: inherit;
text-decoration: inherit;
}
/*
Add the correct font weight in Edge and Safari.
*/
b,
strong {
font-weight: bolder;
}
/*
1. Use the user's configured `mono` font family by default.
2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp,
pre {
font-family: theme('fontFamily.mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace); /* 1 */
font-size: 1em; /* 2 */
}
/*
Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/*
Prevent `sub` and `sup` elements from affecting the line height in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/*
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
3. Remove gaps between table borders by default.
*/
table {
text-indent: 0; /* 1 */
border-color: inherit; /* 2 */
border-collapse: collapse; /* 3 */
margin: 0;
width: auto;
max-width: auto;
}
table td, table th {
padding: unset;
vertical-align: middle;
text-align: left;
line-height: auto;
-webkit-user-select: text;
-moz-user-select: text;
user-select: text;
}
/*
1. Change the font styles in all browsers.
2. Remove the margin in Firefox and Safari.
3. Remove default padding in all browsers.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
font-weight: inherit; /* 1 */
line-height: inherit; /* 1 */
color: inherit; /* 1 */
margin: 0; /* 2 */
padding: 0; /* 3 */
outline: none;
}
/*
Remove the inheritance of text transform in Edge and Firefox.
*/
button,
select {
text-transform: none;
letter-spacing: inherit;
border-radius: inherit;
appearance: auto;
-webkit-appearance: auto;
background: unset;
}
/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Remove default button styles.
*/
button,
/* [type='button'], */
[type='reset'],
[type='submit'] {
-webkit-appearance: button; /* 1 */
background-color: transparent; /* 2 */
background-image: none; /* 2 */
}
/*
Use the modern Firefox focus style for all focusable elements.
*/
:-moz-focusring {
outline: none;
}
/*
Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
*/
:-moz-ui-invalid {
box-shadow: none;
}
/*
Add the correct vertical alignment in Chrome and Firefox.
*/
progress {
vertical-align: baseline;
}
/*
Correct the cursor style of increment and decrement buttons in Safari.
*/
::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
height: auto;
}
/*
1. Correct the odd appearance in Chrome and Safari.
2. Correct the outline style in Safari.
*/
[type='search'] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/*
Remove the inner padding in Chrome and Safari on macOS.
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/*
Add the correct display in Chrome and Safari.
*/
summary {
display: list-item;
}
/*
Removes the default spacing and border for appropriate elements.
*/
blockquote,
dl,
dd,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
figure,
p,
pre {
margin: 0;
}
fieldset {
margin: 0;
padding: 0;
}
legend {
padding: 0;
}
ol,
ul,
menu {
list-style: none;
margin: 0;
padding: 0;
}
li {
margin: unset;
line-height: unset;
}
/*
Prevent resizing textareas horizontally by default.
*/
textarea {
resize: vertical;
}
/*
1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
2. Set the default placeholder color to the user's configured gray 400 color.
*/
input::placeholder,
textarea::placeholder {
opacity: 1; /* 1 */
@apply text-grey-500; /* 2 */
}
button:focus-visible,
input:focus-visible {
outline: none;
}
/*
1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
This can trigger a poorly considered lint error in some tools but is included by design.
*/
img,
svg,
video,
canvas,
audio,
iframe,
embed,
object {
display: block; /* 1 */
vertical-align: middle; /* 2 */
}
/*
Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
*/
img,
video {
max-width: 100%;
height: auto;
}
}
@@ -0,0 +1,18 @@
import type {Meta, StoryObj} from '@storybook/react';
import BoilerPlate from './boilerplate';
const meta = {
title: 'Meta / Boilerplate',
component: BoilerPlate,
tags: ['autodocs']
} satisfies Meta<typeof BoilerPlate>;
export default meta;
type Story = StoryObj<typeof BoilerPlate>;
export const Default: Story = {
args: {
children: 'This is a boilerplate component. Use as a basis to create new components.'
}
};
@@ -0,0 +1,15 @@
import React from 'react';
interface BoilerPlateProps {
children?: React.ReactNode;
}
const BoilerPlate: React.FC<BoilerPlateProps> = ({children}) => {
return (
<>
{children}
</>
);
};
export default BoilerPlate;
@@ -0,0 +1,26 @@
import clsx from 'clsx';
import React from 'react';
import {FetchKoenigLexical} from './global/form/html-editor';
import DesignSystemProvider from './providers/design-system-provider';
export interface DesignSystemAppProps extends React.HTMLProps<HTMLDivElement> {
darkMode: boolean;
fetchKoenigLexical: FetchKoenigLexical;
}
const DesignSystemApp: React.FC<DesignSystemAppProps> = ({darkMode, fetchKoenigLexical, className, children, ...props}) => {
const appClassName = clsx(
'admin-x-base',
className
);
return (
<div className={appClassName} {...props}>
<DesignSystemProvider darkMode={darkMode} fetchKoenigLexical={fetchKoenigLexical}>
{children}
</DesignSystemProvider>
</div>
);
};
export default DesignSystemApp;
+177
View File
@@ -0,0 +1,177 @@
export {ReactComponent as FacebookLogo} from './assets/images/facebook-logo.svg';
export {ReactComponent as GhostLogo} from './assets/images/ghost-logo.svg';
export {ReactComponent as GhostOrb} from './assets/images/ghost-orb.svg';
export {ReactComponent as GoogleLogo} from './assets/images/google-logo.svg';
export {ReactComponent as TwitterLogo} from './assets/images/twitter-logo.svg';
export {ReactComponent as XLogo} from './assets/images/x-logo.svg';
export {default as DesktopChrome} from './global/chrome/desktop-chrome';
export type {DesktopChromeProps} from './global/chrome/desktop-chrome';
export {default as DesktopChromeHeader} from './global/chrome/desktop-chrome-header';
export type {DesktopChromeHeaderProps} from './global/chrome/desktop-chrome-header';
export {default as MobileChrome} from './global/chrome/mobile-chrome';
export type {MobileChromeProps} from './global/chrome/mobile-chrome';
export {default as Checkbox} from './global/form/checkbox';
export type {CheckboxProps} from './global/form/checkbox';
export {default as CheckboxGroup} from './global/form/checkbox-group';
export type {CheckboxGroupProps} from './global/form/checkbox-group';
export {default as CodeEditor} from './global/form/code-editor';
export type {CodeEditorProps, FetchKoenigLexical} from './global/form/code-editor';
export {default as ColorIndicator} from './global/form/color-indicator';
export type {ColorIndicatorProps} from './global/form/color-indicator';
export {default as ColorPicker} from './global/form/color-picker';
export type {ColorPickerProps} from './global/form/color-picker';
export {default as ColorPickerField} from './global/form/color-picker-field';
export type {ColorPickerFieldProps} from './global/form/color-picker-field';
export {default as CurrencyField} from './global/form/currency-field';
export type {CurrencyFieldProps} from './global/form/currency-field';
export {default as FileUpload} from './global/form/file-upload';
export type {FileUploadProps} from './global/form/file-upload';
export {default as Form} from './global/form/form';
export type {FormProps} from './global/form/form';
export {default as HtmlEditor} from './global/form/html-editor';
export type {HtmlEditorProps} from './global/form/html-editor';
export {default as HtmlField} from './global/form/html-field';
export type {HtmlFieldProps} from './global/form/html-field';
export {default as KoenigEditorBase, loadKoenig} from './global/form/koenig-editor-base';
export type {KoenigEditorBaseProps, KoenigInstance, NodeType} from './global/form/koenig-editor-base';
export {default as ImageUpload} from './global/form/image-upload';
export type {ImageUploadProps} from './global/form/image-upload';
export {default as MultiSelect} from './global/form/multi-select';
export type {LoadMultiSelectOptions, MultiSelectOption, MultiSelectProps} from './global/form/multi-select';
export {default as Radio} from './global/form/radio';
export type {RadioProps} from './global/form/radio';
export {default as Select} from './global/form/select';
export type {LoadSelectOptions, SelectOption, SelectOptionGroup, SelectProps} from './global/form/select';
export {default as SelectWithOther} from './global/form/select-with-other';
export type {SelectWithOtherProps} from './global/form/select-with-other';
export {default as TextArea} from './global/form/text-area';
export type {TextAreaProps} from './global/form/text-area';
export {default as TextField} from './global/form/text-field';
export type {TextFieldProps} from './global/form/text-field';
export {default as Toggle} from './global/form/toggle';
export type {ToggleProps} from './global/form/toggle';
export {default as ToggleGroup} from './global/form/toggle-group';
export type {ToggleGroupProps} from './global/form/toggle-group';
export {default as URLTextField} from './global/form/url-text-field';
export type {URLTextFieldProps} from './global/form/url-text-field';
export {default as ConfirmationModal, ConfirmationModalContent} from './global/modal/confirmation-modal';
export type {ConfirmationModalProps} from './global/modal/confirmation-modal';
export {default as LimitModal, LimitModalContent} from './global/modal/limit-modal';
export type {LimitModalProps} from './global/modal/limit-modal';
export {default as Modal, topLevelBackdropClasses} from './global/modal/modal';
export type {ModalProps} from './global/modal/modal';
export {default as ModalPage} from './global/modal/modal-page';
export type {ModalPageProps} from './global/modal/modal-page';
export {default as PreviewModal, PreviewModalContent} from './global/modal/preview-modal';
export type {PreviewModalProps} from './global/modal/preview-modal';
export {default as Avatar} from './global/avatar';
export type {AvatarProps} from './global/avatar';
export {default as Banner} from './global/banner';
export type {BannerProps} from './global/banner';
export {default as Breadcrumbs} from './global/breadcrumbs';
export type {BreadcrumbItem, BreadcrumbsProps} from './global/breadcrumbs';
export {default as Button} from './global/button';
export type {ButtonColor, ButtonProps} from './global/button';
export {default as ButtonGroup} from './global/button-group';
export type {ButtonGroupProps} from './global/button-group';
export {default as ErrorBoundary, withErrorBoundary} from './global/error-boundary';
export type {ErrorBoundaryProps} from './global/error-boundary';
export {default as Heading} from './global/heading';
export type {HeadingProps} from './global/heading';
export {default as Hint} from './global/hint';
export type {HintProps} from './global/hint';
export {default as Icon} from './global/icon';
export type {IconProps} from './global/icon';
export {default as IconLabel} from './global/icon-label';
export type {IconLabelProps} from './global/icon-label';
export {default as InfiniteScrollListener} from './global/infinite-scroll-listener';
export type {InfiniteScrollListenerProps} from './global/infinite-scroll-listener';
export {default as Link} from './global/link';
export type {LinkProps} from './global/link';
export {default as List} from './global/list';
export type {ListProps} from './global/list';
export {default as ListHeading} from './global/list-heading';
export type {ListHeadingProps} from './global/list-heading';
export {default as ListItem} from './global/list-item';
export type {ListItemProps} from './global/list-item';
export {LoadingIndicator} from './global/loading-indicator';
export type {LoadingIndicatorProps} from './global/loading-indicator';
export {default as Menu} from './global/menu';
export type {MenuItem, MenuProps} from './global/menu';
export {default as NoValueLabel} from './global/no-value-label';
export type {NoValueLabelProps} from './global/no-value-label';
export {default as Pagination} from './global/pagination';
export type {PaginationProps} from './global/pagination';
export {default as Popover} from './global/popover';
export type {PopoverProps} from './global/popover';
export {default as Separator} from './global/separator';
export type {SeparatorProps} from './global/separator';
export {DragIndicator, default as SortableList} from './global/sortable-list';
export type {DragIndicatorProps, SortableItemContainerProps, SortableListProps} from './global/sortable-list';
export {default as SortMenu} from './global/sort-menu';
export type {SortMenuProps} from './global/sort-menu';
export {default as StickyFooter} from './global/sticky-footer';
export type {StickyFooterProps} from './global/sticky-footer';
export {default as TabView} from './global/tab-view';
export type {Tab, TabViewProps} from './global/tab-view';
export {default as Table} from './global/table';
export type {ShowMoreData, TableProps} from './global/table';
export {default as TableCell} from './global/table-cell';
export type {TableCellProps} from './global/table-cell';
export {default as TableHead} from './global/table-head';
export type {TableHeadProps} from './global/table-head';
export {default as TableRow} from './global/table-row';
export type {TableRowProps} from './global/table-row';
export {default as Toast, dismissAllToasts, showToast} from './global/toast';
export type {ToastProps} from './global/toast';
export {default as Tooltip} from './global/tooltip';
export type {TooltipProps} from './global/tooltip';
export {default as PageHeader} from './global/layout/page-header';
export type {PageHeaderProps} from './global/layout/page-header';
export {default as Page} from './global/layout/page';
export type {PageTab} from './global/layout/page';
export type {CustomGlobalAction} from './global/layout/page';
export {default as ViewContainer} from './global/layout/view-container';
export type {View} from './global/layout/view-container';
export type {ViewTab} from './global/layout/view-container';
export type {PrimaryActionProps} from './global/layout/view-container';
export {default as DynamicTable} from './global/table/dynamic-table';
export type {DynamicTableProps} from './global/table/dynamic-table';
export type {DynamicTableColumn} from './global/table/dynamic-table';
export type {DynamicTableRow} from './global/table/dynamic-table';
export {default as SettingGroup} from './settings/setting-group';
export type {SettingGroupProps} from './settings/setting-group';
export {default as SettingGroupContent} from './settings/setting-group-content';
export type {SettingGroupContentProps} from './settings/setting-group-content';
export {default as SettingGroupHeader} from './settings/setting-group-header';
export type {SettingGroupHeaderProps} from './settings/setting-group-header';
export {default as SettingNavItem} from './settings/setting-nav-item';
export type {SettingNavItemProps} from './settings/setting-nav-item';
export {default as SettingNavSection} from './settings/setting-nav-section';
export type {SettingNavSectionProps} from './settings/setting-nav-section';
export {default as SettingSection} from './settings/setting-section';
export type {SettingSectionProps} from './settings/setting-section';
export {default as SettingSectionHeader} from './settings/setting-section-header';
export type {SettingSectionHeaderProps} from './settings/setting-section-header';
export {default as SettingValue} from './settings/setting-value';
export type {SettingValueProps} from './settings/setting-value';
export {default as StripeButton} from './settings/stripe-button';
export type {StripeButtonProps} from './settings/stripe-button';
export {default as useGlobalDirtyState} from './hooks/use-global-dirty-state';
export {usePagination} from './hooks/use-pagination';
export type {PaginationData} from './hooks/use-pagination';
export {default as useSortableIndexedList} from './hooks/use-sortable-indexed-list';
export {debounce} from './utils/debounce';
export {confirmIfDirty} from './utils/modals';
export {formatUrl} from './utils/format-url';
export {default as DesignSystemApp} from './design-system-app';
export type {DesignSystemAppProps} from './design-system-app';
export {useFocusContext, useDesignSystem} from './providers/design-system-provider';
+7
View File
@@ -0,0 +1,7 @@
declare module '*.svg' {
// eslint-disable-next-line @typescript-eslint/no-require-imports
import React = require('react');
export const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>;
const src: string;
export default src;
}
+122
View File
@@ -0,0 +1,122 @@
@import './preflight.css';
@import 'tailwindcss/theme.css';
@import '@tryghost/shade/tailwind.theme.css';
@import url(https://fonts.bunny.net/css?family=cardo:400,700);
@import url(https://fonts.bunny.net/css?family=manrope:300,500,700);
@import url(https://fonts.bunny.net/css?family=merriweather:300,700);
@import url(https://fonts.bunny.net/css?family=nunito:400,600,700);
@import url(https://fonts.bunny.net/css?family=old-standard-tt:400,700);
@import url(https://fonts.bunny.net/css?family=prata:400);
@import url(https://fonts.bunny.net/css?family=roboto:400,500,700);
@import url(https://fonts.bunny.net/css?family=rufina:400,500,700);
@import url(https://fonts.bunny.net/css?family=tenor-sans:400);
@import url(https://fonts.bunny.net/css?family=space-grotesk:700);
@import url(https://fonts.bunny.net/css?family=chakra-petch:400);
@import url(https://fonts.bunny.net/css?family=noto-sans:400,700);
@import url(https://fonts.bunny.net/css?family=poppins:400,700);
@import url(https://fonts.bunny.net/css?family=fira-sans:400,700);
@import url(https://fonts.bunny.net/css?family=inter:400,700);
@import url(https://fonts.bunny.net/css?family=noto-serif:400,700);
@import url(https://fonts.bunny.net/css?family=lora:400,700);
@import url(https://fonts.bunny.net/css?family=ibm-plex-serif:400,700);
@import url(https://fonts.bunny.net/css?family=space-mono:400,700);
@import url(https://fonts.bunny.net/css?family=fira-mono:400,700);
@import url(https://fonts.bunny.net/css?family=jetbrains-mono:400,700);
/* Defaults */
@layer base {
/* This just serves as a placeholder; we actually load Inter from a font file in Ember admin */
@font-face {
font-family: "Inter";
src: local("Inter") format("truetype-variations");
font-weight: 100 900;
}
.admin-x-base {
& {
@apply font-sans text-black text-base leading-normal;
}
h1, h2, h3, h4, h5 {
@apply font-bold tracking-tight leading-tighter;
}
h1 {
@apply text-4xl leading-supertight;
}
h2 {
@apply text-2xl;
}
h3 {
@apply text-xl;
}
h4 {
@apply text-lg;
}
h5 {
@apply text-md leading-supertight;
}
h6 {
@apply text-md leading-normal;
}
}
}
.admin-x-base {
line-height: 1.5;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
letter-spacing: unset;
height: 100vh;
width: 100%;
overflow-x: hidden;
overflow-y: auto;
}
/*
Used to be for fixed bottom mobile menu bar
@media (max-width: 800px) {
.admin-x-base {
height: calc(100vh - 55px);
}
} */
.dark .admin-x-base {
color: #FAFAFB;
}
.dark .admin-x-base .gh-loading-orb-container {
background-color: #000000;
}
.dark .admin-x-base .gh-loading-orb {
filter: invert(100%);
}
.admin-x-base .no-scrollbar::-webkit-scrollbar {
display: none; /* Chrome */
}
.admin-x-base .no-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
/* Prose classes are for formatting arbitrary HTML that comes from the API */
.gh-prose-links a {
color: #30CF43;
}
+123
View File
@@ -0,0 +1,123 @@
@import './preflight.css';
@import 'tailwindcss/theme.css';
@import '@tryghost/shade/tailwind.theme.css';
@import 'tailwindcss/utilities.css';
@import url(https://fonts.bunny.net/css?family=cardo:400,700);
@import url(https://fonts.bunny.net/css?family=manrope:300,500,700);
@import url(https://fonts.bunny.net/css?family=merriweather:300,700);
@import url(https://fonts.bunny.net/css?family=nunito:400,600,700);
@import url(https://fonts.bunny.net/css?family=old-standard-tt:400,700);
@import url(https://fonts.bunny.net/css?family=prata:400);
@import url(https://fonts.bunny.net/css?family=roboto:400,500,700);
@import url(https://fonts.bunny.net/css?family=rufina:400,500,700);
@import url(https://fonts.bunny.net/css?family=tenor-sans:400);
@import url(https://fonts.bunny.net/css?family=space-grotesk:700);
@import url(https://fonts.bunny.net/css?family=chakra-petch:400);
@import url(https://fonts.bunny.net/css?family=noto-sans:400,700);
@import url(https://fonts.bunny.net/css?family=poppins:400,700);
@import url(https://fonts.bunny.net/css?family=fira-sans:400,700);
@import url(https://fonts.bunny.net/css?family=inter:400,700);
@import url(https://fonts.bunny.net/css?family=noto-serif:400,700);
@import url(https://fonts.bunny.net/css?family=lora:400,700);
@import url(https://fonts.bunny.net/css?family=ibm-plex-serif:400,700);
@import url(https://fonts.bunny.net/css?family=space-mono:400,700);
@import url(https://fonts.bunny.net/css?family=fira-mono:400,700);
@import url(https://fonts.bunny.net/css?family=jetbrains-mono:400,700);
/* Defaults */
@layer base {
/* This just serves as a placeholder; we actually load Inter from a font file in Ember admin */
@font-face {
font-family: "Inter";
src: local("Inter") format("truetype-variations");
font-weight: 100 900;
}
.admin-x-base {
& {
@apply font-sans text-black text-base leading-normal;
}
h1, h2, h3, h4, h5 {
@apply font-bold tracking-tight leading-tighter;
}
h1 {
@apply text-4xl leading-supertight;
}
h2 {
@apply text-2xl;
}
h3 {
@apply text-xl;
}
h4 {
@apply text-lg;
}
h5 {
@apply text-md leading-supertight;
}
h6 {
@apply text-md leading-normal;
}
}
}
.admin-x-base {
line-height: 1.5;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
letter-spacing: unset;
height: 100vh;
width: 100%;
overflow-x: hidden;
overflow-y: auto;
}
/*
Used to be for fixed bottom mobile menu bar
@media (max-width: 800px) {
.admin-x-base {
height: calc(100vh - 55px);
}
} */
.dark .admin-x-base {
color: #FAFAFB;
}
.dark .admin-x-base .gh-loading-orb-container {
background-color: #000000;
}
.dark .admin-x-base .gh-loading-orb {
filter: invert(100%);
}
.admin-x-base .no-scrollbar::-webkit-scrollbar {
display: none; /* Chrome */
}
.admin-x-base .no-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
/* Prose classes are for formatting arbitrary HTML that comes from the API */
.gh-prose-links a {
color: #30CF43;
}
@@ -0,0 +1,11 @@
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['ghost'],
extends: [
'plugin:ghost/test'
],
rules: {
// Enforce kebab-case (lowercase with hyphens) for all filenames
'ghost/filenames/match-regex': ['error', '^[a-z0-9.-]+$', false]
}
};
@@ -0,0 +1,15 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": false,
"composite": true,
"declaration": true,
"declarationMap": true,
"declarationDir": "./types",
"emitDeclarationOnly": true,
"tsBuildInfoFile": "./types/tsconfig.tsbuildinfo",
"rootDir": "./src"
},
"include": ["src"],
"exclude": ["src/**/*.stories.tsx", "src/**/*.test.ts", "src/**/*.test.tsx"]
}
+24
View File
@@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "ESNext",
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"module": "ESNext",
"skipLibCheck": true,
"types": ["vite/client"],
/* Bundler mode */
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts", "package.json"]
}
+67
View File
@@ -0,0 +1,67 @@
import react from '@vitejs/plugin-react';
import {globSync} from 'glob';
import {resolve} from 'path';
import svgr from 'vite-plugin-svgr';
import {defineConfig} from 'vitest/config';
// https://vitejs.dev/config/
export default (function viteConfig() {
return defineConfig({
logLevel: process.env.CI ? 'info' : 'warn',
plugins: [
svgr(),
react()
],
define: {
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'process.env.VITEST_SEGFAULT_RETRY': 3
},
preview: {
port: 4174
},
build: {
reportCompressedSize: false,
minify: false,
sourcemap: true,
outDir: 'es',
lib: {
formats: ['es'],
entry: globSync(resolve(__dirname, 'src/**/*.{ts,tsx}')).reduce((entries, path) => {
if (path.includes('.stories.') || path.endsWith('.d.ts')) {
return entries;
}
const outPath = path.replace(resolve(__dirname, 'src') + '/', '').replace(/\.(ts|tsx)$/, '');
entries[outPath] = path;
return entries;
}, {} as Record<string, string>)
},
commonjsOptions: {
include: [/packages/, /node_modules/]
},
rollupOptions: {
external: (source) => {
if (source.startsWith('.')) {
return false;
}
if (source.includes('node_modules')) {
return true;
}
return !source.includes(__dirname);
}
}
},
test: {
globals: true, // required for @testing-library/jest-dom extensions
environment: 'jsdom',
include: ['./test/unit/**/*'],
testTimeout: process.env.TIMEOUT ? parseInt(process.env.TIMEOUT) : 10000,
...(process.env.CI && { // https://github.com/vitest-dev/vitest/issues/1674
minThreads: 1,
maxThreads: 2
})
}
});
});