mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-30 22:34:01 -05:00
Adding Posts analytics React app (#21878)
ref https://linear.app/ghost/issue/DES-1021/create-posts-app Part of establishing React patterns in Ghost is to build a well-defined and fairly self-encapsulated app through which we can test assumptions and define best practices. Our guinea pig is Post analytics for this purpose. This PR creates a new React app (posts) using Shade (the new design system).
This commit is contained in:
parent
ab2c7f18e2
commit
252918b70c
73 changed files with 2017 additions and 473 deletions
2
.github/scripts/dev.js
vendored
2
.github/scripts/dev.js
vendored
|
@ -74,7 +74,7 @@ const COMMAND_TYPESCRIPT = {
|
|||
env: {}
|
||||
};
|
||||
|
||||
const adminXApps = '@tryghost/admin-x-demo,@tryghost/admin-x-settings,@tryghost/admin-x-activitypub';
|
||||
const adminXApps = '@tryghost/admin-x-demo,@tryghost/admin-x-settings,@tryghost/admin-x-activitypub,@tryghost/posts';
|
||||
|
||||
const COMMANDS_ADMINX = [{
|
||||
name: 'adminXDeps',
|
||||
|
|
|
@ -1,119 +1,119 @@
|
|||
{
|
||||
"name": "@tryghost/admin-x-framework",
|
||||
"type": "module",
|
||||
"version": "0.0.0",
|
||||
"repository": "https://github.com/TryGhost/Ghost/tree/main/apps/admin-x-framework",
|
||||
"author": "Ghost Foundation",
|
||||
"private": true,
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs",
|
||||
"types": "./types/index.d.ts"
|
||||
"name": "@tryghost/admin-x-framework",
|
||||
"type": "module",
|
||||
"version": "0.0.0",
|
||||
"repository": "https://github.com/TryGhost/Ghost/tree/main/apps/admin-x-framework",
|
||||
"author": "Ghost Foundation",
|
||||
"private": true,
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs",
|
||||
"types": "./types/index.d.ts"
|
||||
},
|
||||
"./errors": {
|
||||
"import": "./dist/errors.js",
|
||||
"require": "./dist/errors.cjs",
|
||||
"types": "./types/errors.d.ts"
|
||||
},
|
||||
"./helpers": {
|
||||
"import": "./dist/helpers.js",
|
||||
"require": "./dist/helpers.cjs",
|
||||
"types": "./types/helpers.d.ts"
|
||||
},
|
||||
"./hooks": {
|
||||
"import": "./dist/hooks.js",
|
||||
"require": "./dist/hooks.cjs",
|
||||
"types": "./types/hooks.d.ts"
|
||||
},
|
||||
"./routing": {
|
||||
"import": "./dist/routing.js",
|
||||
"require": "./dist/routing.cjs",
|
||||
"types": "./types/routing.d.ts"
|
||||
},
|
||||
"./api/*": {
|
||||
"import": "./dist/api/*.js",
|
||||
"require": "./dist/api/*.cjs",
|
||||
"types": "./types/api/*.d.ts"
|
||||
},
|
||||
"./vite": {
|
||||
"import": "./dist/vite.js",
|
||||
"require": "./dist/vite.cjs",
|
||||
"types": "./types/vite.d.ts"
|
||||
},
|
||||
"./playwright": {
|
||||
"import": "./dist/playwright.js",
|
||||
"require": "./dist/playwright.cjs",
|
||||
"types": "./types/playwright.d.ts"
|
||||
},
|
||||
"./test/*": {
|
||||
"import": "./dist/test/*.js",
|
||||
"require": "./dist/test/*.cjs",
|
||||
"types": "./types/test/*.d.ts"
|
||||
}
|
||||
},
|
||||
"./errors": {
|
||||
"import": "./dist/errors.js",
|
||||
"require": "./dist/errors.cjs",
|
||||
"types": "./types/errors.d.ts"
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.declaration.json && vite build",
|
||||
"prepare": "yarn build",
|
||||
"test": "yarn test:types && yarn test:unit",
|
||||
"test:types": "tsc --noEmit",
|
||||
"test:unit": "vitest run --coverage",
|
||||
"lint:code": "eslint --ext .js,.ts,.cjs,.tsx src/ --cache",
|
||||
"lint": "yarn lint:code && yarn lint:test",
|
||||
"lint:test": "eslint -c test/.eslintrc.cjs --ext .js,.ts,.cjs,.tsx test/ --cache"
|
||||
},
|
||||
"./helpers": {
|
||||
"import": "./dist/helpers.js",
|
||||
"require": "./dist/helpers.cjs",
|
||||
"types": "./types/helpers.d.ts"
|
||||
"files": [
|
||||
"es",
|
||||
"types"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@testing-library/react": "14.3.1",
|
||||
"@types/mocha": "10.0.1",
|
||||
"c8": "8.0.1",
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"eslint-plugin-react-refresh": "0.4.3",
|
||||
"jsdom": "24.1.3",
|
||||
"mocha": "10.2.0",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"sinon": "17.0.0",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.4.5"
|
||||
},
|
||||
"./hooks": {
|
||||
"import": "./dist/hooks.js",
|
||||
"require": "./dist/hooks.cjs",
|
||||
"types": "./types/hooks.d.ts"
|
||||
"dependencies": {
|
||||
"@sentry/react": "7.119.2",
|
||||
"@tanstack/react-query": "4.36.1",
|
||||
"@tryghost/admin-x-design-system": "0.0.0",
|
||||
"@tryghost/shade": "0.0.0",
|
||||
"@types/react": "18.3.3",
|
||||
"@types/react-dom": "18.3.0",
|
||||
"@vitejs/plugin-react": "4.2.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"vite": "4.5.3",
|
||||
"vite-plugin-css-injected-by-js": "^3.3.0",
|
||||
"vite-plugin-svgr": "3.3.0",
|
||||
"vitest": "0.34.3"
|
||||
},
|
||||
"./routing": {
|
||||
"import": "./dist/routing.js",
|
||||
"require": "./dist/routing.cjs",
|
||||
"types": "./types/routing.d.ts"
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"./api/*": {
|
||||
"import": "./dist/api/*.js",
|
||||
"require": "./dist/api/*.cjs",
|
||||
"types": "./types/api/*.d.ts"
|
||||
},
|
||||
"./vite": {
|
||||
"import": "./dist/vite.js",
|
||||
"require": "./dist/vite.cjs",
|
||||
"types": "./types/vite.d.ts"
|
||||
},
|
||||
"./playwright": {
|
||||
"import": "./dist/playwright.js",
|
||||
"require": "./dist/playwright.cjs",
|
||||
"types": "./types/playwright.d.ts"
|
||||
},
|
||||
"./test/*": {
|
||||
"import": "./dist/test/*.js",
|
||||
"require": "./dist/test/*.cjs",
|
||||
"types": "./types/test/*.d.ts"
|
||||
"nx": {
|
||||
"targets": {
|
||||
"dev": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
},
|
||||
"test:unit": {
|
||||
"dependsOn": [
|
||||
"test:unit",
|
||||
"^build",
|
||||
"@tryghost/admin-x-design-system:test:unit"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.declaration.json && vite build",
|
||||
"prepare": "yarn build",
|
||||
"test": "yarn test:types && yarn test:unit",
|
||||
"test:types": "tsc --noEmit",
|
||||
"test:unit": "vitest run --coverage",
|
||||
"lint:code": "eslint --ext .js,.ts,.cjs,.tsx src/ --cache",
|
||||
"lint": "yarn lint:code && yarn lint:test",
|
||||
"lint:test": "eslint -c test/.eslintrc.cjs --ext .js,.ts,.cjs,.tsx test/ --cache"
|
||||
},
|
||||
"files": [
|
||||
"es",
|
||||
"types"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@testing-library/react": "14.3.1",
|
||||
"@types/mocha": "10.0.1",
|
||||
"c8": "8.0.1",
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"eslint-plugin-react-refresh": "0.4.3",
|
||||
"jsdom": "24.1.3",
|
||||
"mocha": "10.2.0",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"sinon": "17.0.0",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.4.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sentry/react": "7.119.2",
|
||||
"@tanstack/react-query": "4.36.1",
|
||||
"@tryghost/admin-x-design-system": "0.0.0",
|
||||
"@tryghost/shade": "0.0.0",
|
||||
"@types/react": "18.3.3",
|
||||
"@types/react-dom": "18.3.0",
|
||||
"@vitejs/plugin-react": "4.2.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"vite": "4.5.3",
|
||||
"vite-plugin-css-injected-by-js": "^3.3.0",
|
||||
"vite-plugin-svgr": "3.3.0",
|
||||
"vitest": "0.34.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"nx": {
|
||||
"targets": {
|
||||
"dev": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
},
|
||||
"test:unit": {
|
||||
"dependsOn": [
|
||||
"test:unit",
|
||||
"^build",
|
||||
"@tryghost/admin-x-design-system:test:unit"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
62
apps/admin-x-framework/src/test/render-shade.tsx
Normal file
62
apps/admin-x-framework/src/test/render-shade.tsx
Normal file
|
@ -0,0 +1,62 @@
|
|||
import {ShadeAppProps} from '@tryghost/shade';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import {TopLevelFrameworkProps} from '../providers/FrameworkProvider';
|
||||
|
||||
export default function renderShadeApp<Props extends object>(
|
||||
App: React.ComponentType<Props & {
|
||||
framework: TopLevelFrameworkProps;
|
||||
designSystem: ShadeAppProps;
|
||||
}>,
|
||||
props: Props
|
||||
) {
|
||||
const style = document.createElement('style');
|
||||
style.appendChild(document.createTextNode(`
|
||||
: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;
|
||||
}
|
||||
`));
|
||||
document.head.appendChild(style);
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<App
|
||||
designSystem={{darkMode: false, fetchKoenigLexical: null}}
|
||||
framework={{
|
||||
externalNavigate: (link) => {
|
||||
// Use the expectExternalNavigate helper to test this dummy external linking
|
||||
window.location.href = `/external/${encodeURIComponent(JSON.stringify(link))}`;
|
||||
},
|
||||
ghostVersion: '5.x',
|
||||
sentryDSN: null,
|
||||
unsplashConfig: {
|
||||
Authorization: '',
|
||||
'Accept-Version': '',
|
||||
'Content-Type': '',
|
||||
'App-Pragma': '',
|
||||
'X-Unsplash-Cache': false
|
||||
},
|
||||
onDelete: () => {},
|
||||
onInvalidate: () => {},
|
||||
onUpdate: () => {}
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
</React.StrictMode>
|
||||
);
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
import path from 'path';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import glob from 'glob';
|
||||
import {resolve} from 'path';
|
||||
|
@ -10,6 +11,11 @@ export default (function viteConfig() {
|
|||
plugins: [
|
||||
react()
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src')
|
||||
}
|
||||
},
|
||||
preview: {
|
||||
port: 4174
|
||||
},
|
||||
|
@ -20,13 +26,13 @@ export default (function viteConfig() {
|
|||
outDir: 'dist',
|
||||
lib: {
|
||||
formats: ['es', 'cjs'],
|
||||
entry: glob.sync(resolve(__dirname, 'src/**/*.{ts,tsx}')).reduce((entries, path) => {
|
||||
if (path.endsWith('.d.ts')) {
|
||||
entry: glob.sync(resolve(__dirname, 'src/**/*.{ts,tsx}')).reduce((entries, libpath) => {
|
||||
if (libpath.endsWith('.d.ts')) {
|
||||
return entries;
|
||||
}
|
||||
|
||||
const outPath = path.replace(resolve(__dirname, 'src') + '/', '').replace(/\.(ts|tsx)$/, '');
|
||||
entries[outPath] = path;
|
||||
const outPath = libpath.replace(resolve(__dirname, 'src') + '/', '').replace(/\.(ts|tsx)$/, '');
|
||||
entries[outPath] = libpath;
|
||||
return entries;
|
||||
}, {} as Record<string, string>)
|
||||
},
|
||||
|
|
|
@ -51,6 +51,10 @@ const features = [{
|
|||
title: 'Comment Improvements',
|
||||
description: 'Enables new comment features',
|
||||
flag: 'commentImprovements'
|
||||
}, {
|
||||
title: 'Post analytics redesign',
|
||||
description: 'Enables redesigned Post analytics page',
|
||||
flag: 'postsX'
|
||||
}];
|
||||
|
||||
const AlphaFeatures: React.FC = () => {
|
||||
|
|
56
apps/posts/.eslintrc.cjs
Normal file
56
apps/posts/.eslintrc.cjs
Normal file
|
@ -0,0 +1,56 @@
|
|||
/* eslint-env node */
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'plugin:ghost/ts',
|
||||
'plugin:react/recommended',
|
||||
'plugin:react-hooks/recommended'
|
||||
],
|
||||
plugins: [
|
||||
'ghost',
|
||||
'react-refresh',
|
||||
'tailwindcss'
|
||||
],
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect'
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
// sort multiple import lines into alphabetical groups
|
||||
'ghost/sort-imports-es6-autofix/sort-imports-es6': ['error', {
|
||||
memberSyntaxSortOrder: ['none', 'all', 'single', 'multiple']
|
||||
}],
|
||||
|
||||
// TODO: re-enable this (maybe fixed fast refresh?)
|
||||
'react-refresh/only-export-components': 'off',
|
||||
|
||||
// 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',
|
||||
|
||||
// TODO: re-enable these if deemed useful
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
|
||||
// custom react rules
|
||||
'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',
|
||||
|
||||
'tailwindcss/classnames-order': ['error', {config: 'tailwind.config.cjs'}],
|
||||
'tailwindcss/enforces-negative-arbitrary-values': ['warn', {config: 'tailwind.config.cjs'}],
|
||||
'tailwindcss/enforces-shorthand': ['warn', {config: 'tailwind.config.cjs'}],
|
||||
'tailwindcss/migration-from-tailwind-2': ['warn', {config: 'tailwind.config.cjs'}],
|
||||
'tailwindcss/no-arbitrary-value': 'off',
|
||||
'tailwindcss/no-custom-classname': 'off',
|
||||
'tailwindcss/no-contradicting-classname': ['error', {config: 'tailwind.config.cjs'}]
|
||||
}
|
||||
};
|
3
apps/posts/.gitignore
vendored
Normal file
3
apps/posts/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
dist
|
||||
playwright-report
|
||||
test-results
|
16
apps/posts/index.html
Normal file
16
apps/posts/index.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Posts</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/standalone.tsx"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
55
apps/posts/package.json
Normal file
55
apps/posts/package.json
Normal file
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"name": "@tryghost/posts",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/TryGhost/Ghost/tree/main/apps/posts"
|
||||
},
|
||||
"author": "Ghost Foundation",
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"dist/"
|
||||
],
|
||||
"main": "./dist/posts.umd.cjs",
|
||||
"module": "./dist/posts.js",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
"dev:start": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "yarn run lint:code && yarn run lint:test",
|
||||
"lint:code": "eslint --ext .js,.ts,.cjs,.tsx --cache src",
|
||||
"lint:test": "eslint -c test/.eslintrc.cjs --ext .js,.ts,.cjs,.tsx --cache test",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/react": "14.3.1",
|
||||
"@tryghost/shade": "0.0.0",
|
||||
"@tryghost/admin-x-framework": "0.0.0",
|
||||
"@types/react": "18.3.3",
|
||||
"@types/react-dom": "18.3.0",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1"
|
||||
},
|
||||
"nx": {
|
||||
"targets": {
|
||||
"dev": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
},
|
||||
"test:unit": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
},
|
||||
"test:acceptance": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
apps/posts/playwright.config.mjs
Normal file
3
apps/posts/playwright.config.mjs
Normal file
|
@ -0,0 +1,3 @@
|
|||
import {adminXPlaywrightConfig} from '@tryghost/admin-x-framework/playwright';
|
||||
|
||||
export default adminXPlaywrightConfig();
|
1
apps/posts/postcss.config.cjs
Normal file
1
apps/posts/postcss.config.cjs
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = require('@tryghost/shade/postcss.config.cjs');
|
23
apps/posts/src/App.tsx
Normal file
23
apps/posts/src/App.tsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import PostAnalytics from './pages/PostAnalytics';
|
||||
import {FrameworkProvider, TopLevelFrameworkProps} from '@tryghost/admin-x-framework';
|
||||
import {RoutingProvider} from '@tryghost/admin-x-framework/routing';
|
||||
import {ShadeApp, ShadeAppProps} from '@tryghost/shade';
|
||||
|
||||
interface AppProps {
|
||||
framework: TopLevelFrameworkProps;
|
||||
designSystem: ShadeAppProps;
|
||||
}
|
||||
|
||||
const App: React.FC<AppProps> = ({framework, designSystem}) => {
|
||||
return (
|
||||
<FrameworkProvider {...framework}>
|
||||
<RoutingProvider basePath='posts-x'>
|
||||
<ShadeApp className='posts' {...designSystem}>
|
||||
<PostAnalytics />
|
||||
</ShadeApp>
|
||||
</RoutingProvider>
|
||||
</FrameworkProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
26
apps/posts/src/components/layout/Header.tsx
Normal file
26
apps/posts/src/components/layout/Header.tsx
Normal file
|
@ -0,0 +1,26 @@
|
|||
import {Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, H1} from '@tryghost/shade';
|
||||
|
||||
const Header = () => {
|
||||
return (
|
||||
<div className="pt-9">
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="/ghost/posts">
|
||||
Posts
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>
|
||||
Analytics
|
||||
</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
<H1 className='mt-1 max-w-[1024px]'>The Evolution of Basketball: From Pastime to Professional and One of the Most Popular Sports</H1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
17
apps/posts/src/components/post-analytics/Overview.tsx
Normal file
17
apps/posts/src/components/post-analytics/Overview.tsx
Normal file
|
@ -0,0 +1,17 @@
|
|||
import ClickPerformance from './overview/ClickPerformance';
|
||||
import Conversions from './overview/Conversions';
|
||||
import Feedback from './overview/Feedback';
|
||||
import NewsletterPerformance from './overview/NewsletterPerformance';
|
||||
|
||||
const Overview = () => {
|
||||
return (
|
||||
<div className="grid w-full grid-cols-3 gap-6 py-4">
|
||||
<NewsletterPerformance className='col-span-2' />
|
||||
<Feedback />
|
||||
<ClickPerformance className='col-span-2' />
|
||||
<Conversions />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Overview;
|
|
@ -0,0 +1,22 @@
|
|||
import * as React from 'react';
|
||||
import {Card, CardContent, CardDescription, CardHeader, CardTitle} from '@tryghost/shade';
|
||||
|
||||
interface ClickPerformanceProps extends React.ComponentProps<typeof Card> {};
|
||||
|
||||
const ClickPerformance: React.FC<ClickPerformanceProps> = (props) => {
|
||||
return (
|
||||
<Card {...props}>
|
||||
<CardHeader>
|
||||
<CardTitle>Click performance</CardTitle>
|
||||
<CardDescription>
|
||||
Links in this newsletter
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
Card contents
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default ClickPerformance;
|
|
@ -0,0 +1,22 @@
|
|||
import * as React from 'react';
|
||||
import {Card, CardContent, CardDescription, CardHeader, CardTitle} from '@tryghost/shade';
|
||||
|
||||
interface ConversionsProps extends React.ComponentProps<typeof Card> {};
|
||||
|
||||
const Conversions: React.FC<ConversionsProps> = (props) => {
|
||||
return (
|
||||
<Card {...props}>
|
||||
<CardHeader>
|
||||
<CardTitle>Conversions</CardTitle>
|
||||
<CardDescription>
|
||||
3 members signed up on this post
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
Card contents
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default Conversions;
|
|
@ -0,0 +1,22 @@
|
|||
import * as React from 'react';
|
||||
import {Card, CardContent, CardDescription, CardHeader, CardTitle} from '@tryghost/shade';
|
||||
|
||||
interface FeedbackProps extends React.ComponentProps<typeof Card> {};
|
||||
|
||||
const Feedback: React.FC<FeedbackProps> = (props) => {
|
||||
return (
|
||||
<Card {...props}>
|
||||
<CardHeader>
|
||||
<CardTitle>Feedback</CardTitle>
|
||||
<CardDescription>
|
||||
188 reactions
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
Card contents
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default Feedback;
|
|
@ -0,0 +1,22 @@
|
|||
import * as React from 'react';
|
||||
import {Card, CardContent, CardDescription, CardHeader, CardTitle} from '@tryghost/shade';
|
||||
|
||||
interface NewsletterPerformanceProps extends React.ComponentProps<typeof Card> {};
|
||||
|
||||
const NewsletterPerformance: React.FC<NewsletterPerformanceProps> = (props) => {
|
||||
return (
|
||||
<Card {...props}>
|
||||
<CardHeader>
|
||||
<CardTitle>Newsletter performance</CardTitle>
|
||||
<CardDescription>
|
||||
Sent 19 Sept 2024
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
Card contents
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewsletterPerformance;
|
6
apps/posts/src/index.tsx
Normal file
6
apps/posts/src/index.tsx
Normal file
|
@ -0,0 +1,6 @@
|
|||
import './styles/index.css';
|
||||
import App from './App';
|
||||
|
||||
export {
|
||||
App as AdminXApp
|
||||
};
|
47
apps/posts/src/pages/PostAnalytics.tsx
Normal file
47
apps/posts/src/pages/PostAnalytics.tsx
Normal file
|
@ -0,0 +1,47 @@
|
|||
import Header from '../components/layout/Header';
|
||||
import Overview from '../components/post-analytics/Overview';
|
||||
import {Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuTrigger, Icon, Page, Tabs, TabsContent, TabsList, TabsTrigger} from '@tryghost/shade';
|
||||
|
||||
const PostAnalytics = () => {
|
||||
return (
|
||||
<Page>
|
||||
<Header />
|
||||
<Tabs className='mt-7' defaultValue="overview" variant="page">
|
||||
<div className='flex w-full items-center border-b pb-2'>
|
||||
<TabsList className='flex w-full items-center justify-start border-none pb-0'>
|
||||
<TabsTrigger value="overview">Overview</TabsTrigger>
|
||||
<TabsTrigger value="newsletter">Newsletter</TabsTrigger>
|
||||
</TabsList>
|
||||
<div className='flex items-center gap-1'>
|
||||
<Button variant='outline'><Icon.Share className='-mt-0.5' />Share</Button>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger>
|
||||
<Button variant='outline'><Icon.Dotdotdot /></Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="min-w-48">
|
||||
<DropdownMenuItem>
|
||||
<span>Edit post</span>
|
||||
<DropdownMenuShortcut>⇧⌘E</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<span>View in browser</span>
|
||||
<DropdownMenuShortcut>⇧⌘O</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem className="text-red">Delete</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
<TabsContent value="overview">
|
||||
<Overview />
|
||||
</TabsContent>
|
||||
<TabsContent value="newsletter">
|
||||
Newsletter details
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default PostAnalytics;
|
5
apps/posts/src/standalone.tsx
Normal file
5
apps/posts/src/standalone.tsx
Normal file
|
@ -0,0 +1,5 @@
|
|||
import './styles/index.css';
|
||||
import App from './App';
|
||||
import renderShadeApp from '@tryghost/admin-x-framework/test/render-shade';
|
||||
|
||||
renderShadeApp(App, {});
|
1
apps/posts/src/styles/index.css
Normal file
1
apps/posts/src/styles/index.css
Normal file
|
@ -0,0 +1 @@
|
|||
@import "@tryghost/shade/styles.css";
|
6
apps/posts/tailwind.config.cjs
Normal file
6
apps/posts/tailwind.config.cjs
Normal file
|
@ -0,0 +1,6 @@
|
|||
import shadePreset from '@tryghost/shade/tailwind.cjs';
|
||||
|
||||
module.exports = {
|
||||
presets: [shadePreset('.shade')],
|
||||
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}', '../../node_modules/@tryghost/shade/es/**/*.{js,ts,jsx,tsx}']
|
||||
};
|
6
apps/posts/test/.eslintrc.cjs
Normal file
6
apps/posts/test/.eslintrc.cjs
Normal file
|
@ -0,0 +1,6 @@
|
|||
module.exports = {
|
||||
plugins: ['ghost'],
|
||||
extends: [
|
||||
'plugin:ghost/ts-test'
|
||||
]
|
||||
};
|
23
apps/posts/tsconfig.json
Normal file
23
apps/posts/tsconfig.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src", "test"]
|
||||
}
|
10
apps/posts/vite.config.mjs
Normal file
10
apps/posts/vite.config.mjs
Normal file
|
@ -0,0 +1,10 @@
|
|||
import adminXViteConfig from '@tryghost/admin-x-framework/vite';
|
||||
import pkg from './package.json';
|
||||
import {resolve} from 'path';
|
||||
|
||||
export default (function viteConfig() {
|
||||
return adminXViteConfig({
|
||||
packageName: pkg.name,
|
||||
entry: resolve(__dirname, 'src/index.tsx')
|
||||
});
|
||||
});
|
|
@ -25,6 +25,6 @@ const config: StorybookConfig = {
|
|||
crypto: require.resolve('rollup-plugin-node-builtins')
|
||||
}
|
||||
return config;
|
||||
}
|
||||
},
|
||||
};
|
||||
export default config;
|
||||
|
|
|
@ -3,4 +3,4 @@ import shadeTheme from './shade-theme';
|
|||
|
||||
addons.setConfig({
|
||||
theme: shadeTheme
|
||||
});
|
||||
});
|
||||
|
|
|
@ -59,7 +59,9 @@ const preview: Preview = {
|
|||
options: {
|
||||
storySort: {
|
||||
method: 'alphabetical',
|
||||
order: ['Welcome', 'Adding components', 'Component usage', 'Conventions', 'Icons', 'Components', 'Meta', 'Experimental'],
|
||||
order: [
|
||||
'Welcome', 'Adding components', 'Component usage', 'Conventions', 'Icons',
|
||||
'Components', 'Layout', 'Experimental', 'Meta'],
|
||||
},
|
||||
},
|
||||
docs: {
|
||||
|
@ -76,7 +78,7 @@ const preview: Preview = {
|
|||
let {scheme} = context.globals;
|
||||
|
||||
return (
|
||||
<div className={`shade shade-base ${scheme === 'dark' ? 'dark' : ''}`} style={{
|
||||
<div className={`shade ${scheme === 'dark' ? 'dark' : ''}`} style={{
|
||||
// padding: '24px',
|
||||
// width: 'unset',
|
||||
height: 'unset',
|
||||
|
|
|
@ -34,4 +34,4 @@ export default create({
|
|||
inputBorder: '#15171A',
|
||||
inputTextColor: '#15171A',
|
||||
inputBorderRadius: 2,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -318,3 +318,7 @@ body,
|
|||
.sbdocs hr {
|
||||
margin: 40px 0;
|
||||
}
|
||||
|
||||
.docs-story .shade {
|
||||
overflow: unset;
|
||||
}
|
||||
|
|
|
@ -1,99 +1,100 @@
|
|||
{
|
||||
"name": "@tryghost/shade",
|
||||
"type": "module",
|
||||
"version": "0.0.0",
|
||||
"repository": "https://github.com/TryGhost/Ghost/tree/main/packages/shade",
|
||||
"author": "Ghost Foundation",
|
||||
"private": true,
|
||||
"main": "es/index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.declaration.json && vite build",
|
||||
"prepare": "yarn build",
|
||||
"test": "yarn test:unit",
|
||||
"test:unit": "yarn test:types && yarn nx build && vitest run",
|
||||
"test:types": "tsc --noEmit",
|
||||
"lint:code": "eslint --ext .js,.ts,.cjs,.tsx src/ --cache",
|
||||
"lint": "yarn lint:code && yarn 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",
|
||||
"tailwind.cjs",
|
||||
"tailwind.config.cjs"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@codemirror/lang-html": "6.4.9",
|
||||
"@radix-ui/react-tooltip": "1.1.2",
|
||||
"@storybook/addon-essentials": "7.6.20",
|
||||
"@storybook/addon-interactions": "7.6.20",
|
||||
"@storybook/addon-links": "7.6.20",
|
||||
"@storybook/addon-styling": "1.3.7",
|
||||
"@storybook/blocks": "7.6.20",
|
||||
"@storybook/react": "7.6.20",
|
||||
"@storybook/react-vite": "7.6.4",
|
||||
"@storybook/testing-library": "0.2.2",
|
||||
"@testing-library/react": "14.3.1",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/node": "^22.10.1",
|
||||
"@vitejs/plugin-react": "4.2.1",
|
||||
"c8": "8.0.1",
|
||||
"chai": "4.3.8",
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"eslint-plugin-react-refresh": "0.4.3",
|
||||
"eslint-plugin-tailwindcss": "3.13.0",
|
||||
"jsdom": "24.1.3",
|
||||
"lodash-es": "4.17.21",
|
||||
"mocha": "10.2.0",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"rollup-plugin-node-builtins": "2.1.2",
|
||||
"sinon": "17.0.0",
|
||||
"storybook": "7.6.20",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.4.5",
|
||||
"validator": "13.12.0",
|
||||
"vite": "4.5.3",
|
||||
"vite-plugin-svgr": "3.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "6.1.0",
|
||||
"@dnd-kit/sortable": "7.0.2",
|
||||
"@ebay/nice-modal-react": "1.2.13",
|
||||
"@radix-ui/react-avatar": "1.1.0",
|
||||
"@radix-ui/react-checkbox": "1.1.1",
|
||||
"@radix-ui/react-form": "0.0.3",
|
||||
"@radix-ui/react-popover": "1.1.1",
|
||||
"@radix-ui/react-radio-group": "1.2.0",
|
||||
"@radix-ui/react-separator": "1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@radix-ui/react-switch": "1.1.0",
|
||||
"@radix-ui/react-tabs": "1.1.0",
|
||||
"@radix-ui/react-tooltip": "1.1.2",
|
||||
"@sentry/react": "7.119.2",
|
||||
"@tailwindcss/forms": "0.5.9",
|
||||
"@tailwindcss/line-clamp": "0.4.4",
|
||||
"@uiw/react-codemirror": "4.23.7",
|
||||
"autoprefixer": "10.4.19",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.468.0",
|
||||
"postcss": "8.4.39",
|
||||
"postcss-import": "16.1.0",
|
||||
"react-colorful": "5.6.1",
|
||||
"react-hot-toast": "2.4.1",
|
||||
"react-select": "5.8.2",
|
||||
"tailwind-merge": "^2.5.5",
|
||||
"tailwindcss": "3.4.14",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
}
|
||||
}
|
||||
"name": "@tryghost/shade",
|
||||
"type": "module",
|
||||
"version": "0.0.0",
|
||||
"repository": "https://github.com/TryGhost/Ghost/tree/main/packages/shade",
|
||||
"author": "Ghost Foundation",
|
||||
"private": true,
|
||||
"main": "es/index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.declaration.json && vite build",
|
||||
"prepare": "yarn build",
|
||||
"test": "yarn test:unit",
|
||||
"test:unit": "yarn test:types && yarn nx build && vitest run",
|
||||
"test:types": "tsc --noEmit",
|
||||
"lint:code": "eslint --ext .js,.ts,.cjs,.tsx src/ --cache",
|
||||
"lint": "yarn lint:code && yarn 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",
|
||||
"tailwind.cjs",
|
||||
"tailwind.config.cjs"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@codemirror/lang-html": "6.4.9",
|
||||
"@radix-ui/react-tooltip": "1.1.2",
|
||||
"@storybook/addon-essentials": "7.6.20",
|
||||
"@storybook/addon-interactions": "7.6.20",
|
||||
"@storybook/addon-links": "7.6.20",
|
||||
"@storybook/addon-styling": "1.3.7",
|
||||
"@storybook/blocks": "7.6.20",
|
||||
"@storybook/react": "7.6.20",
|
||||
"@storybook/react-vite": "7.6.4",
|
||||
"@storybook/testing-library": "0.2.2",
|
||||
"@testing-library/react": "14.3.1",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/node": "^22.10.1",
|
||||
"@vitejs/plugin-react": "4.2.1",
|
||||
"c8": "8.0.1",
|
||||
"chai": "4.3.8",
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"eslint-plugin-react-refresh": "0.4.3",
|
||||
"eslint-plugin-tailwindcss": "3.13.0",
|
||||
"jsdom": "24.1.3",
|
||||
"lodash-es": "4.17.21",
|
||||
"mocha": "10.2.0",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"rollup-plugin-node-builtins": "2.1.2",
|
||||
"sinon": "17.0.0",
|
||||
"storybook": "7.6.20",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.4.5",
|
||||
"validator": "13.12.0",
|
||||
"vite": "4.5.3",
|
||||
"vite-plugin-svgr": "3.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "6.1.0",
|
||||
"@dnd-kit/sortable": "7.0.2",
|
||||
"@ebay/nice-modal-react": "1.2.13",
|
||||
"@radix-ui/react-avatar": "1.1.0",
|
||||
"@radix-ui/react-checkbox": "1.1.1",
|
||||
"@radix-ui/react-dropdown-menu": "2.1.3",
|
||||
"@radix-ui/react-form": "0.0.3",
|
||||
"@radix-ui/react-popover": "1.1.1",
|
||||
"@radix-ui/react-radio-group": "1.2.0",
|
||||
"@radix-ui/react-separator": "1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@radix-ui/react-switch": "1.1.0",
|
||||
"@radix-ui/react-tabs": "1.1.2",
|
||||
"@radix-ui/react-tooltip": "1.1.2",
|
||||
"@sentry/react": "7.119.2",
|
||||
"@tailwindcss/forms": "0.5.9",
|
||||
"@tailwindcss/line-clamp": "0.4.4",
|
||||
"@uiw/react-codemirror": "4.23.7",
|
||||
"autoprefixer": "10.4.19",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.468.0",
|
||||
"postcss": "8.4.39",
|
||||
"postcss-import": "16.1.0",
|
||||
"react-colorful": "5.6.1",
|
||||
"react-hot-toast": "2.4.1",
|
||||
"react-select": "5.8.2",
|
||||
"tailwind-merge": "^2.5.5",
|
||||
"tailwindcss": "3.4.14",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
.shade-base {
|
||||
.shade {
|
||||
/*
|
||||
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)
|
||||
|
|
|
@ -5,12 +5,13 @@ import ShadeProvider from './providers/ShadeProvider';
|
|||
|
||||
export interface ShadeAppProps extends React.HTMLProps<HTMLDivElement> {
|
||||
darkMode: boolean;
|
||||
// fetchKoenigLexical: FetchKoenigLexical;
|
||||
fetchKoenigLexical: null;
|
||||
}
|
||||
|
||||
const ShadeApp: React.FC<ShadeAppProps> = ({darkMode, className, children, ...props}) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const ShadeApp: React.FC<ShadeAppProps> = ({darkMode, fetchKoenigLexical, className, children, ...props}) => {
|
||||
const appClassName = clsx(
|
||||
'shade-base',
|
||||
'shade',
|
||||
darkMode && 'dark',
|
||||
className
|
||||
);
|
||||
|
|
|
@ -12,4 +12,4 @@ const BoilerPlate: React.FC<BoilerPlateProps> = ({children}) => {
|
|||
);
|
||||
};
|
||||
|
||||
export default BoilerPlate;
|
||||
export default BoilerPlate;
|
||||
|
|
42
apps/shade/src/components/layout/heading.stories.tsx
Normal file
42
apps/shade/src/components/layout/heading.stories.tsx
Normal file
|
@ -0,0 +1,42 @@
|
|||
import type {Meta, StoryObj} from '@storybook/react';
|
||||
import {H1, H2, H3, H4, HeadingProps} from './heading';
|
||||
|
||||
const meta = {
|
||||
title: 'Layout / Heading',
|
||||
tags: ['autodocs']
|
||||
} satisfies Meta<HeadingProps>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<HeadingProps>;
|
||||
|
||||
export const HeadingOne = {
|
||||
render: (args: Story['args']) => {
|
||||
return (
|
||||
<H1 {...args}>The Joke Tax Chronicles</H1>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const HeadingTwo = {
|
||||
render: (args: Story['args']) => {
|
||||
return (
|
||||
<H2 {...args}>The Plan</H2>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const HeadingThree = {
|
||||
render: (args: Story['args']) => {
|
||||
return (
|
||||
<H3 {...args}>The Joke Tax</H3>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const HeadingFour = {
|
||||
render: (args: Story['args']) => {
|
||||
return (
|
||||
<H4 {...args}>Jokester Revolt</H4>
|
||||
);
|
||||
}
|
||||
};
|
60
apps/shade/src/components/layout/heading.tsx
Normal file
60
apps/shade/src/components/layout/heading.tsx
Normal file
|
@ -0,0 +1,60 @@
|
|||
import {cn} from '@/lib/utils';
|
||||
import * as React from 'react';
|
||||
|
||||
export interface HeadingProps
|
||||
extends React.HTMLAttributes<HTMLHeadingElement> {}
|
||||
|
||||
const H1 = React.forwardRef<HTMLHeadingElement, HeadingProps>(
|
||||
({className, ...props}, ref) => {
|
||||
return (
|
||||
<h1
|
||||
ref={ref}
|
||||
className={cn('scroll-m-20 text-3xl leading-supertight tracking-tight font-bold', className)}
|
||||
{...props} />
|
||||
);
|
||||
}
|
||||
);
|
||||
H1.displayName = 'H1';
|
||||
|
||||
const H2 = React.forwardRef<HTMLHeadingElement, HeadingProps>(
|
||||
({className, ...props}, ref) => {
|
||||
return (
|
||||
<h2
|
||||
ref={ref}
|
||||
className={cn('scroll-m-20 text-2xl font-bold tracking-tight first:mt-0', className)}
|
||||
{...props} />
|
||||
);
|
||||
}
|
||||
);
|
||||
H2.displayName = 'H2';
|
||||
|
||||
const H3 = React.forwardRef<HTMLHeadingElement, HeadingProps>(
|
||||
({className, ...props}, ref) => {
|
||||
return (
|
||||
<h3
|
||||
ref={ref}
|
||||
className={cn('scroll-m-20 text-xl font-semibold tracking-tight', className)}
|
||||
{...props} />
|
||||
);
|
||||
}
|
||||
);
|
||||
H3.displayName = 'H3';
|
||||
|
||||
const H4 = React.forwardRef<HTMLHeadingElement, HeadingProps>(
|
||||
({className, ...props}, ref) => {
|
||||
return (
|
||||
<h4
|
||||
ref={ref}
|
||||
className={cn('scroll-m-20 text-lg font-semibold tracking-tight', className)}
|
||||
{...props} />
|
||||
);
|
||||
}
|
||||
);
|
||||
H4.displayName = 'H4';
|
||||
|
||||
export {
|
||||
H1,
|
||||
H2,
|
||||
H3,
|
||||
H4
|
||||
};
|
21
apps/shade/src/components/layout/page.stories.tsx
Normal file
21
apps/shade/src/components/layout/page.stories.tsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import type {Meta, StoryObj} from '@storybook/react';
|
||||
import {Page} from './page';
|
||||
|
||||
const meta = {
|
||||
title: 'Layout / Page',
|
||||
component: Page,
|
||||
tags: ['autodocs']
|
||||
} satisfies Meta<typeof Page>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Page>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: (
|
||||
<>
|
||||
Page container with a max width of <code>max-w-content</code>
|
||||
</>
|
||||
)
|
||||
}
|
||||
};
|
21
apps/shade/src/components/layout/page.tsx
Normal file
21
apps/shade/src/components/layout/page.tsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import {cn} from '@/lib/utils';
|
||||
import * as React from 'react';
|
||||
|
||||
export interface PageProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {}
|
||||
|
||||
const Page = React.forwardRef<HTMLDivElement, PageProps>(
|
||||
({className, ...props}, ref) => {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('max-w-page mx-auto w-full px-12', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Page.displayName = 'Page';
|
||||
|
||||
export {Page};
|
31
apps/shade/src/components/ui/breadcrumb.stories.tsx
Normal file
31
apps/shade/src/components/ui/breadcrumb.stories.tsx
Normal file
|
@ -0,0 +1,31 @@
|
|||
import type {Meta, StoryObj} from '@storybook/react';
|
||||
import {Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator} from './breadcrumb';
|
||||
|
||||
const meta = {
|
||||
title: 'Components / Breadcrumb',
|
||||
component: Breadcrumb,
|
||||
tags: ['autodocs']
|
||||
} satisfies Meta<typeof Breadcrumb>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Breadcrumb>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: (
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="/components">Components</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
)
|
||||
}
|
||||
};
|
115
apps/shade/src/components/ui/breadcrumb.tsx
Normal file
115
apps/shade/src/components/ui/breadcrumb.tsx
Normal file
|
@ -0,0 +1,115 @@
|
|||
import * as React from 'react';
|
||||
import {Slot} from '@radix-ui/react-slot';
|
||||
import {ChevronRight, MoreHorizontal} from 'lucide-react';
|
||||
|
||||
import {cn} from '@/lib/utils';
|
||||
|
||||
const Breadcrumb = React.forwardRef<
|
||||
HTMLElement,
|
||||
React.ComponentPropsWithoutRef<'nav'> & {
|
||||
separator?: React.ReactNode
|
||||
}
|
||||
>(({...props}, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />);
|
||||
Breadcrumb.displayName = 'Breadcrumb';
|
||||
|
||||
const BreadcrumbList = React.forwardRef<
|
||||
HTMLOListElement,
|
||||
React.ComponentPropsWithoutRef<'ol'>
|
||||
>(({className, ...props}, ref) => (
|
||||
<ol
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
BreadcrumbList.displayName = 'BreadcrumbList';
|
||||
|
||||
const BreadcrumbItem = React.forwardRef<
|
||||
HTMLLIElement,
|
||||
React.ComponentPropsWithoutRef<'li'>
|
||||
>(({className, ...props}, ref) => (
|
||||
<li
|
||||
ref={ref}
|
||||
className={cn('inline-flex items-center gap-1.5', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
BreadcrumbItem.displayName = 'BreadcrumbItem';
|
||||
|
||||
const BreadcrumbLink = React.forwardRef<
|
||||
HTMLAnchorElement,
|
||||
React.ComponentPropsWithoutRef<'a'> & {
|
||||
asChild?: boolean
|
||||
}
|
||||
>(({asChild, className, ...props}, ref) => {
|
||||
const Comp = asChild ? Slot : 'a';
|
||||
|
||||
return (
|
||||
<Comp
|
||||
ref={ref}
|
||||
className={cn('transition-colors hover:text-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
BreadcrumbLink.displayName = 'BreadcrumbLink';
|
||||
|
||||
const BreadcrumbPage = React.forwardRef<
|
||||
HTMLSpanElement,
|
||||
React.ComponentPropsWithoutRef<'span'>
|
||||
>(({className, ...props}, ref) => (
|
||||
<span
|
||||
ref={ref}
|
||||
aria-current="page"
|
||||
aria-disabled="true"
|
||||
className={cn('font-normal text-foreground', className)}
|
||||
role="link"
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
BreadcrumbPage.displayName = 'BreadcrumbPage';
|
||||
|
||||
const BreadcrumbSeparator = ({
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<'li'>) => (
|
||||
<li
|
||||
aria-hidden="true"
|
||||
className={cn('[&>svg]:w-3.5 [&>svg]:h-3.5', className)}
|
||||
role="presentation"
|
||||
{...props}
|
||||
>
|
||||
{children ?? <ChevronRight />}
|
||||
</li>
|
||||
);
|
||||
BreadcrumbSeparator.displayName = 'BreadcrumbSeparator';
|
||||
|
||||
const BreadcrumbEllipsis = ({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<'span'>) => (
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={cn('flex h-9 w-9 items-center justify-center', className)}
|
||||
role="presentation"
|
||||
{...props}
|
||||
>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
<span className="sr-only">More</span>
|
||||
</span>
|
||||
);
|
||||
BreadcrumbEllipsis.displayName = 'BreadcrumbElipssis';
|
||||
|
||||
export {
|
||||
Breadcrumb,
|
||||
BreadcrumbList,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
BreadcrumbEllipsis
|
||||
};
|
|
@ -21,6 +21,7 @@ export const Default: Story = {
|
|||
|
||||
export const IconOnly: Story = {
|
||||
args: {
|
||||
size: 'icon',
|
||||
children: (
|
||||
<Icon.ArrowUp />
|
||||
)
|
||||
|
@ -47,4 +48,4 @@ export const LucideIcon: Story = {
|
|||
</>
|
||||
)
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -9,16 +9,16 @@ const buttonVariants = cva(
|
|||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
|
||||
destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
|
||||
outline: 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
|
||||
secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
|
||||
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
||||
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
||||
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
||||
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
||||
link: 'text-primary underline-offset-4 hover:underline'
|
||||
},
|
||||
size: {
|
||||
default: 'h-9 px-4 py-2',
|
||||
sm: 'h-8 rounded-md px-3 text-xs',
|
||||
default: 'h-[34px] px-3 py-2',
|
||||
sm: 'h-7 rounded-md px-3 text-xs [&_svg]:size-3',
|
||||
lg: 'h-10 rounded-md px-8',
|
||||
icon: 'h-9 w-9'
|
||||
}
|
||||
|
|
33
apps/shade/src/components/ui/card.stories.tsx
Normal file
33
apps/shade/src/components/ui/card.stories.tsx
Normal file
|
@ -0,0 +1,33 @@
|
|||
import type {Meta, StoryObj} from '@storybook/react';
|
||||
import {Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle} from './card';
|
||||
import {Button} from './button';
|
||||
|
||||
const meta = {
|
||||
title: 'Components / Card',
|
||||
component: Card,
|
||||
tags: ['autodocs']
|
||||
} satisfies Meta<typeof Card>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Card>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
className: 'w-[350px]',
|
||||
children: [
|
||||
<CardHeader key="header">
|
||||
<CardTitle>Create project</CardTitle>
|
||||
<CardDescription>Deploy your new project in one-click.</CardDescription>
|
||||
</CardHeader>,
|
||||
|
||||
<CardContent key="content">
|
||||
Card contents
|
||||
</CardContent>,
|
||||
|
||||
<CardFooter key="footer" className="flex justify-between">
|
||||
<Button variant="outline">Cancel</Button>
|
||||
<Button>Deploy</Button>
|
||||
</CardFooter>
|
||||
]
|
||||
}
|
||||
};
|
76
apps/shade/src/components/ui/card.tsx
Normal file
76
apps/shade/src/components/ui/card.tsx
Normal file
|
@ -0,0 +1,76 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import {cn} from '@/lib/utils';
|
||||
|
||||
const Card = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({className, ...props}, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'rounded-xl border bg-card text-card-foreground',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
Card.displayName = 'Card';
|
||||
|
||||
const CardHeader = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({className, ...props}, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('flex flex-col space-y-1.5 p-6', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
CardHeader.displayName = 'CardHeader';
|
||||
|
||||
const CardTitle = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({className, ...props}, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('font-semibold leading-none', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
CardTitle.displayName = 'CardTitle';
|
||||
|
||||
const CardDescription = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({className, ...props}, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('text-sm text-muted-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
CardDescription.displayName = 'CardDescription';
|
||||
|
||||
const CardContent = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({className, ...props}, ref) => (
|
||||
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
|
||||
));
|
||||
CardContent.displayName = 'CardContent';
|
||||
|
||||
const CardFooter = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({className, ...props}, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('flex items-center p-6 pt-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
CardFooter.displayName = 'CardFooter';
|
||||
|
||||
export {Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent};
|
71
apps/shade/src/components/ui/dropdown-menu.stories.tsx
Normal file
71
apps/shade/src/components/ui/dropdown-menu.stories.tsx
Normal file
|
@ -0,0 +1,71 @@
|
|||
import type {Meta, StoryObj} from '@storybook/react';
|
||||
import {DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger} from './dropdown-menu';
|
||||
import {Button} from './button';
|
||||
|
||||
const meta = {
|
||||
title: 'Components / Dropdown menu',
|
||||
component: DropdownMenu,
|
||||
tags: ['autodocs']
|
||||
} satisfies Meta<typeof DropdownMenu>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof DropdownMenu>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: [
|
||||
<DropdownMenuTrigger key="trigger" asChild>
|
||||
<Button variant="outline">Open</Button>
|
||||
</DropdownMenuTrigger>,
|
||||
|
||||
<DropdownMenuContent key="content" className="w-56">
|
||||
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>
|
||||
Profile
|
||||
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
Billing
|
||||
<DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
Settings
|
||||
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
Keyboard shortcuts
|
||||
<DropdownMenuShortcut>⌘K</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>Team</DropdownMenuItem>
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger>Invite users</DropdownMenuSubTrigger>
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuSubContent>
|
||||
<DropdownMenuItem>Email</DropdownMenuItem>
|
||||
<DropdownMenuItem>Message</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>More...</DropdownMenuItem>
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenuSub>
|
||||
<DropdownMenuItem>New Team
|
||||
<DropdownMenuShortcut>⌘+T</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>GitHub</DropdownMenuItem>
|
||||
<DropdownMenuItem>Support</DropdownMenuItem>
|
||||
<DropdownMenuItem disabled>API</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>Log out
|
||||
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
]
|
||||
}
|
||||
};
|
203
apps/shade/src/components/ui/dropdown-menu.tsx
Normal file
203
apps/shade/src/components/ui/dropdown-menu.tsx
Normal file
|
@ -0,0 +1,203 @@
|
|||
import * as React from 'react';
|
||||
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
||||
import {Check, ChevronRight, Circle} from 'lucide-react';
|
||||
|
||||
import {cn} from '@/lib/utils';
|
||||
|
||||
const DropdownMenu = DropdownMenuPrimitive.Root;
|
||||
|
||||
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
||||
|
||||
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
||||
|
||||
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
||||
|
||||
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
||||
|
||||
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
||||
|
||||
const DropdownMenuSubTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||
inset?: boolean
|
||||
}
|
||||
>(({className, inset, children, ...props}, ref) => (
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
inset && 'pl-8',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRight className="ml-auto" />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
));
|
||||
DropdownMenuSubTrigger.displayName =
|
||||
DropdownMenuPrimitive.SubTrigger.displayName;
|
||||
|
||||
const DropdownMenuSubContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
|
||||
>(({className, ...props}, ref) => (
|
||||
<div className='shade'>
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
DropdownMenuSubContent.displayName =
|
||||
DropdownMenuPrimitive.SubContent.displayName;
|
||||
|
||||
const DropdownMenuContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
|
||||
>(({className, sideOffset = 4, ...props}, ref) => (
|
||||
<DropdownMenuPrimitive.Portal>
|
||||
<div className='shade'>
|
||||
<DropdownMenuPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md',
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
className
|
||||
)}
|
||||
sideOffset={sideOffset}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
));
|
||||
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
||||
|
||||
const DropdownMenuItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
||||
inset?: boolean
|
||||
}
|
||||
>(({className, inset, ...props}, ref) => (
|
||||
<DropdownMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
inset && 'pl-8',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
||||
|
||||
const DropdownMenuCheckboxItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
|
||||
>(({className, children, checked, ...props}, ref) => (
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
ref={ref}
|
||||
checked={checked}
|
||||
className={cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Check className="h-4 w-4" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
));
|
||||
DropdownMenuCheckboxItem.displayName =
|
||||
DropdownMenuPrimitive.CheckboxItem.displayName;
|
||||
|
||||
const DropdownMenuRadioItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
|
||||
>(({className, children, ...props}, ref) => (
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Circle className="h-2 w-2 fill-current" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
));
|
||||
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
||||
|
||||
const DropdownMenuLabel = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
||||
inset?: boolean
|
||||
}
|
||||
>(({className, inset, ...props}, ref) => (
|
||||
<DropdownMenuPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'px-2 py-1.5 text-sm font-semibold',
|
||||
inset && 'pl-8',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
||||
|
||||
const DropdownMenuSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
|
||||
>(({className, ...props}, ref) => (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn('-mx-1 my-1 h-px bg-muted', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
||||
|
||||
const DropdownMenuShortcut = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||
return (
|
||||
<span
|
||||
className={cn('ml-auto text-xs tracking-widest opacity-60', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut';
|
||||
|
||||
export {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuRadioGroup
|
||||
};
|
|
@ -50,4 +50,4 @@ const Icon = Object.entries(iconModules).reduce((acc, [path, module]) => {
|
|||
export type IconName = keyof typeof Icon;
|
||||
|
||||
export const IconComponents = Icon;
|
||||
export default Icon;
|
||||
export default Icon;
|
||||
|
|
31
apps/shade/src/components/ui/tabs.stories.tsx
Normal file
31
apps/shade/src/components/ui/tabs.stories.tsx
Normal file
|
@ -0,0 +1,31 @@
|
|||
import type {Meta, StoryObj} from '@storybook/react';
|
||||
import {Tabs, TabsContent, TabsList, TabsTrigger} from './tabs';
|
||||
|
||||
const meta = {
|
||||
title: 'Components / Tabs',
|
||||
component: Tabs,
|
||||
tags: ['autodocs']
|
||||
} satisfies Meta<typeof Tabs>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Tabs>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
defaultValue: 'account',
|
||||
children: [
|
||||
<TabsList key="list" className="grid w-full grid-cols-2">
|
||||
<TabsTrigger value="account">Account</TabsTrigger>
|
||||
<TabsTrigger value="password">Password</TabsTrigger>
|
||||
</TabsList>,
|
||||
|
||||
<TabsContent key="account" value="account">
|
||||
Account contents
|
||||
</TabsContent>,
|
||||
|
||||
<TabsContent key="password" value="password">
|
||||
Password contents
|
||||
</TabsContent>
|
||||
]
|
||||
}
|
||||
};
|
130
apps/shade/src/components/ui/tabs.tsx
Normal file
130
apps/shade/src/components/ui/tabs.tsx
Normal file
|
@ -0,0 +1,130 @@
|
|||
import * as React from 'react';
|
||||
import * as TabsPrimitive from '@radix-ui/react-tabs';
|
||||
|
||||
import {cn} from '@/lib/utils';
|
||||
import {cva} from 'class-variance-authority';
|
||||
|
||||
type TabsVariant = 'segmented' | 'page';
|
||||
|
||||
const TabsVariantContext = React.createContext<TabsVariant>('segmented');
|
||||
|
||||
export interface TabsProps extends React.ComponentPropsWithoutRef<typeof TabsPrimitive.Root> {
|
||||
variant?: TabsVariant;
|
||||
}
|
||||
|
||||
const tabsVariants = cva(
|
||||
'',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
segmented: '',
|
||||
page: ''
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'segmented'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const Tabs = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Root>,
|
||||
TabsProps
|
||||
>(({variant = 'segmented', ...props}, ref) => (
|
||||
<TabsVariantContext.Provider value={variant}>
|
||||
<TabsPrimitive.Root ref={ref} {...props} />
|
||||
</TabsVariantContext.Provider>
|
||||
));
|
||||
Tabs.displayName = TabsPrimitive.Root.displayName;
|
||||
|
||||
const tabsListVariants = cva(
|
||||
'inline-flex items-center justify-center text-muted-foreground',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
segmented: 'h-[34px] rounded-lg bg-muted px-[3px]',
|
||||
page: 'gap-2 border-b pb-2'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'segmented'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const TabsList = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
||||
>(({className, ...props}, ref) => {
|
||||
const variant = React.useContext(TabsVariantContext);
|
||||
return (
|
||||
<TabsPrimitive.List
|
||||
ref={ref}
|
||||
className={cn(tabsListVariants({variant, className}))}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
TabsList.displayName = TabsPrimitive.List.displayName;
|
||||
|
||||
const tabsTriggerVariants = cva(
|
||||
'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
segmented: 'h-7 data-[state=active]:shadow-md',
|
||||
page: 'h-[34px] border data-[state=active]:bg-muted/70'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'segmented'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const TabsTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
||||
>(({className, ...props}, ref) => {
|
||||
const variant = React.useContext(TabsVariantContext);
|
||||
return (
|
||||
<TabsPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(tabsTriggerVariants({variant, className}))}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
||||
|
||||
const tabsContentVariants = cva(
|
||||
'mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
segmented: '',
|
||||
page: ''
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'segmented'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const TabsContent = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
||||
>(({className, ...props}, ref) => {
|
||||
const variant = React.useContext(TabsVariantContext);
|
||||
return (
|
||||
<TabsPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(tabsContentVariants({variant, className}))}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
||||
|
||||
export {Tabs, TabsList, TabsTrigger, TabsContent, tabsVariants};
|
|
@ -83,7 +83,15 @@ export interface ButtonProps
|
|||
}
|
||||
```
|
||||
|
||||
That's it — you've just added a new component to Shade.
|
||||
**Step 5:** Export the component
|
||||
|
||||
In order to be able to import the new component in apps, you'll need to export it in the `index.ts` file of Shade, like this:
|
||||
|
||||
```
|
||||
export * from './components/ui/button';
|
||||
```
|
||||
|
||||
That's it — you've just added a new component to Shade and made it ready to use in other React apps.
|
||||
|
||||
## Stories
|
||||
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
// UI components
|
||||
export * from './components/ui/button';
|
||||
export {IconComponents as Icon} from './components/ui/icon';
|
||||
export * from './components/ui/breadcrumb';
|
||||
export * from './components/ui/dropdown-menu';
|
||||
export * from './components/ui/card';
|
||||
export * from './components/ui/tabs';
|
||||
|
||||
export * from './components/layout/page';
|
||||
export * from './components/layout/heading';
|
||||
|
||||
// Assets
|
||||
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';
|
||||
|
@ -7,9 +19,11 @@ export {ReactComponent as XLogo} from './assets/images/x-logo.svg';
|
|||
|
||||
export {default as useGlobalDirtyState} from './hooks/useGlobalDirtyState';
|
||||
|
||||
// Utils
|
||||
export * from '@/lib/utils';
|
||||
export {debounce} from './utils/debounce';
|
||||
export {formatUrl} from './utils/formatUrl';
|
||||
|
||||
export {default as ShadeApp} from './ShadeApp';
|
||||
export type {ShadeAppProps} from './ShadeApp';
|
||||
export {useFocusContext} from './providers/ShadeProvider';
|
||||
export {useFocusContext} from './providers/ShadeProvider';
|
||||
|
|
|
@ -3,4 +3,4 @@ export const kebabToPascalCase = (str: string): string => {
|
|||
const processed = str
|
||||
.replace(/[-_]([a-z0-9])/gi, (_, char) => char.toUpperCase());
|
||||
return processed.charAt(0).toUpperCase() + processed.slice(1);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -80,42 +80,10 @@
|
|||
font-weight: 100 900;
|
||||
}
|
||||
|
||||
.shade-base {
|
||||
.shade {
|
||||
& {
|
||||
@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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,7 +96,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.shade-base {
|
||||
.shade {
|
||||
line-height: 1.5;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
|
@ -146,28 +114,28 @@
|
|||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.shade-base {
|
||||
.shade {
|
||||
height: calc(100vh - 55px);
|
||||
}
|
||||
}
|
||||
|
||||
.shade-base.dark {
|
||||
.shade.dark {
|
||||
color: #fafafb;
|
||||
}
|
||||
|
||||
.shade-base.dark .gh-loading-orb-container {
|
||||
.shade.dark .gh-loading-orb-container {
|
||||
background-color: #000000;
|
||||
}
|
||||
|
||||
.shade-base.dark .gh-loading-orb {
|
||||
.shade.dark .gh-loading-orb {
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
||||
.shade-base .no-scrollbar::-webkit-scrollbar {
|
||||
.shade .no-scrollbar::-webkit-scrollbar {
|
||||
display: none; /* Chrome */
|
||||
}
|
||||
|
||||
.shade-base .no-scrollbar {
|
||||
.shade .no-scrollbar {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
|
|
@ -287,7 +287,8 @@ module.exports = {
|
|||
min: 'min-content',
|
||||
max: 'max-content',
|
||||
fit: 'fit-content',
|
||||
prose: '65ch'
|
||||
prose: '65ch',
|
||||
page: '128rem'
|
||||
},
|
||||
borderRadius: {
|
||||
sm: 'calc(var(--radius) - 4px)',
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": false,
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"declarationDir": "./types"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["src/**/*.stories.tsx"]
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": false,
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"declarationDir": "./types"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["src/**/*.stories.tsx"]
|
||||
}
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"types": ["vite/client"],
|
||||
"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",
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts", "package.json"]
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts", "package.json"]
|
||||
}
|
||||
|
|
|
@ -47,6 +47,10 @@ export default (function viteConfig() {
|
|||
},
|
||||
rollupOptions: {
|
||||
external: (source) => {
|
||||
if (source.startsWith('@/')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (source.startsWith('.')) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -193,3 +193,11 @@ add|ember-template-lint|no-action|5|14|5|14|a90edd9a99596008f60bfcdbc6befe7fe8d2
|
|||
remove|ember-template-lint|no-action|5|14|5|14|88b11bf43be33d97824ebac071a563affac0b97d|1730678400000|1741046400000|1746230400000|app/components/gh-psm-tags-input.hbs
|
||||
add|ember-template-lint|no-action|80|92|80|92|f30d469e4ae668f05aca2f92a124a6b4748847a3|1730678400000|1741046400000|1746230400000|app/components/gh-post-settings-menu.hbs
|
||||
remove|ember-template-lint|no-action|8|50|8|50|de589665046a78748832e8f93d2e495d1d259265|1728345600000|1738717200000|1743897600000|app/components/gh-mobile-nav-bar.hbs
|
||||
add|ember-template-lint|no-action|12|16|12|16|3696846a8a04d429559abebaaf5dab2c6387c21f|1734307200000|1744671600000|1749855600000|app/components/gh-nav-menu/main.hbs
|
||||
add|ember-template-lint|no-action|34|26|34|26|3b76c38861ddcdfaa277e272a1d27293c2659524|1734307200000|1744671600000|1749855600000|app/components/gh-nav-menu/main.hbs
|
||||
add|ember-template-lint|no-invalid-interactive|34|26|34|26|0c04fbca90264398b6b5033632f3170c73d1769b|1734307200000|1744671600000|1749855600000|app/components/gh-nav-menu/main.hbs
|
||||
add|ember-template-lint|no-invalid-link-title|29|20|29|20|593cda92786c7440169712eadac45a452b967dd5|1734307200000|1744671600000|1749855600000|app/components/gh-nav-menu/main.hbs
|
||||
remove|ember-template-lint|no-action|10|110|10|110|4bbb6ad1f623335866ac715f34f2ce9829b1d55b|1728345600000|1738717200000|1743897600000|app/components/gh-nav-menu/main.hbs
|
||||
remove|ember-template-lint|no-action|31|30|31|30|8eee88dbd40609f8ddc00330f4a47b1d30c483f0|1728345600000|1738717200000|1743897600000|app/components/gh-nav-menu/main.hbs
|
||||
remove|ember-template-lint|no-invalid-interactive|31|30|31|30|929269f70336deb9f640bc0b5d54a0bd5336d27a|1728345600000|1738717200000|1743897600000|app/components/gh-nav-menu/main.hbs
|
||||
remove|ember-template-lint|no-invalid-link-title|27|24|27|24|17a357b69040eb9e19a79fe08468c698eab84939|1728345600000|1738717200000|1743897600000|app/components/gh-nav-menu/main.hbs
|
||||
|
|
1
ghost/admin/app/components/admin-x/posts.hbs
Normal file
1
ghost/admin/app/components/admin-x/posts.hbs
Normal file
|
@ -0,0 +1 @@
|
|||
<div {{react-render this.ReactComponent}}></div>
|
8
ghost/admin/app/components/admin-x/posts.js
Normal file
8
ghost/admin/app/components/admin-x/posts.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import AdminXComponent from './admin-x-component';
|
||||
import {inject as service} from '@ember/service';
|
||||
|
||||
export default class Posts extends AdminXComponent {
|
||||
@service upgradeStatus;
|
||||
|
||||
static packageName = '@tryghost/posts';
|
||||
}
|
|
@ -1,171 +1,200 @@
|
|||
<div class="flex flex-column h-100" {{css-transition (unless @firstRender "gh-nav-main")}} data-test-nav-menu="main" ...attributes>
|
||||
<div class="flex flex-column h-100" {{css-transition (unless @firstRender "gh-nav-main" )}} data-test-nav-menu="main"
|
||||
...attributes>
|
||||
|
||||
{{#unless this.session.user.isContributor}}
|
||||
<header class="gh-nav-menu">
|
||||
<div class="gh-nav-menu-details">
|
||||
<div class="gh-nav-menu-icon {{this.iconClass}}" style={{this.iconStyle}}></div>
|
||||
<div class="gh-nav-menu-details-sitetitle">{{this.config.blogTitle}}</div>
|
||||
</div>
|
||||
<div class="gh-nav-menu-search">
|
||||
<button class="gh-nav-btn-search" title="Search site (Ctrl/⌘ + K)" type="button" {{on "click" (action "openSearchModal")}} data-test-button="search"><span>{{svg-jar "search"}}</span></button>
|
||||
</div>
|
||||
</header>
|
||||
<header class="gh-nav-menu">
|
||||
<div class="gh-nav-menu-details">
|
||||
<div class="gh-nav-menu-icon {{this.iconClass}}" style={{this.iconStyle}}></div>
|
||||
<div class="gh-nav-menu-details-sitetitle">{{this.config.blogTitle}}</div>
|
||||
</div>
|
||||
<div class="gh-nav-menu-search">
|
||||
<button class="gh-nav-btn-search" title="Search site (Ctrl/⌘ + K)" type="button" {{on "click"
|
||||
(action "openSearchModal" )}} data-test-button="search"><span>{{svg-jar "search"}}</span></button>
|
||||
</div>
|
||||
</header>
|
||||
{{/unless}}
|
||||
|
||||
<section class="gh-nav-body">
|
||||
|
||||
{{#unless this.session.user.isContributor}}
|
||||
<div class="gh-nav-top">
|
||||
<ul class="gh-nav-list gh-nav-main">
|
||||
{{#if (feature "ActivityPub")}}
|
||||
<li>
|
||||
<LinkTo @route="activitypub-x" @current-when="activitypub-x">{{svg-jar "star"}}ActivityPub</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if (gh-user-can-admin this.session.user)}}
|
||||
<li class="relative gh-nav-list-home">
|
||||
<LinkTo @route="dashboard" @alt="Dashboard" title="Dashboard" data-test-nav="dashboard">{{svg-jar "house"}} Dashboard</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
<li class="relative">
|
||||
<span {{action "transitionToOrRefreshSite" on="click"}}>
|
||||
<LinkTo @route="site" data-test-nav="site" @current-when={{this.isOnSite}} @preventDefault={{false}}>
|
||||
{{svg-jar "view-site"}} View site
|
||||
</LinkTo>
|
||||
</span>
|
||||
<a href="{{this.config.blogUrl}}/" class="gh-secondary-action" title="Open site in new tab" target="_blank" rel="noopener noreferrer">
|
||||
<span>{{svg-jar "external"}}</span>
|
||||
</a>
|
||||
</li>
|
||||
{{#if (and (gh-user-can-admin this.session.user) this.config.stats)}}
|
||||
<li class="relative">
|
||||
<LinkTo @route="stats">{{svg-jar "stats-outline"}}Stats</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if (gh-user-can-admin this.session.user)}}
|
||||
<li class="relative">
|
||||
<a href="javascript:void(0)" class={{if this.explore.exploreWindowOpen "active"}} {{on "click" (fn this.toggleExploreWindow "")}} data-test-nav="explore">
|
||||
{{svg-jar "globe-simple"}} Explore
|
||||
</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
<ul class="gh-nav-list gh-nav-manage">
|
||||
<li class="gh-nav-list-new relative">
|
||||
<GhLinkToCustomViewsIndex @route="posts" @query={{reset-query-params "posts"}} data-test-nav="posts">{{svg-jar "posts"}}Posts</GhLinkToCustomViewsIndex>
|
||||
<LinkTo @route="lexical-editor.new" @model="post" class="gh-secondary-action gh-nav-new-post" @alt="New post" title="New post" data-test-nav="new-story"><span>{{svg-jar "plus"}}</span></LinkTo>
|
||||
{{#if this.session.user.isAuthorOrContributor}}
|
||||
{{#if this.customViews.forPosts}}
|
||||
<ul class="gh-nav-view-list">
|
||||
{{#each this.customViews.forPosts as |view|}}
|
||||
<li>
|
||||
<LinkTo @route="posts" @query={{reset-query-params "posts" view.filter}} data-test-nav-custom="{{view.route}}-{{view.name}}" title="{{view.name}}">
|
||||
<span class="gh-nav-viewname">{{view.name}}</span>
|
||||
<span class="flex items-center svg-{{view.color}}">
|
||||
{{#unless view.icon}}
|
||||
<span class="absolute circle"></span>
|
||||
{{/unless}}
|
||||
</span>
|
||||
</LinkTo>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{#if this.customViews.forPosts}}
|
||||
<button type="button" class="gh-nav-button-expand {{if this.navigation.settings.expanded.posts "expanded"}}" {{on "click" (fn this.navigation.toggleExpansion "posts")}} aria-label="{{if this.navigation.settings.expanded.posts "Collapse custom post types" "Expand custom post types"}}">
|
||||
{{svg-jar (if this.navigation.settings.expanded.posts "arrow-down-stroke" "arrow-right-stroke")}}
|
||||
</button>
|
||||
{{#liquid-if this.navigation.settings.expanded.posts}}
|
||||
<ul class="gh-nav-view-list">
|
||||
{{#each this.customViews.forPosts as |view|}}
|
||||
<li>
|
||||
<LinkTo @route="posts" @query={{reset-query-params "posts" view.filter}} data-test-nav-custom="{{view.route}}-{{view.name}}" title="{{view.name}}">
|
||||
<span class="gh-nav-viewname">{{view.name}}</span>
|
||||
<span class="flex items-center svg-{{view.color}}">
|
||||
{{#unless view.icon}}
|
||||
<span class="absolute circle"></span>
|
||||
{{/unless}}
|
||||
</span>
|
||||
</LinkTo>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/liquid-if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</li>
|
||||
<li>
|
||||
{{!-- clicking the Content link whilst on the content screen should reset the filter --}}
|
||||
{{#if (eq this.router.currentRouteName "pages")}}
|
||||
<LinkTo @route="pages" @query={{reset-query-params "pages"}} class="active" data-test-nav="pages">{{svg-jar "page"}}Pages</LinkTo>
|
||||
{{else}}
|
||||
<LinkTo @route="pages" data-test-nav="pages">{{svg-jar "page"}}Pages</LinkTo>
|
||||
{{/if}}
|
||||
</li>
|
||||
{{#if this.showTagsNavigation}}
|
||||
<li><LinkTo @route="tags" @current-when="tags tag tag.new" data-test-nav="tags">{{svg-jar "tag"}}Tags</LinkTo></li>
|
||||
{{/if}}
|
||||
{{#if (gh-user-can-admin this.session.user)}}
|
||||
<li class="relative">
|
||||
{{#if (eq this.router.currentRouteName "members.index")}}
|
||||
<LinkTo @route="members" @current-when="members member member.new" @query={{reset-query-params "members.index"}} data-test-nav="members">{{svg-jar "members"}}Members
|
||||
{{#let (members-count-fetcher) as |count|}}
|
||||
{{#unless count.isLoading}}
|
||||
<span class="gh-nav-member-count">{{format-number count.count}}</span>
|
||||
{{/unless}}
|
||||
{{/let}}
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<LinkTo @route="members" @current-when="members member member.new" data-test-nav="members">{{svg-jar "members"}}Members
|
||||
{{#let (members-count-fetcher) as |count|}}
|
||||
{{#unless count.isLoading}}
|
||||
<span class="gh-nav-member-count">{{format-number count.count}}</span>
|
||||
{{/unless}}
|
||||
{{/let}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</li>
|
||||
|
||||
{{/if}}
|
||||
{{#if (feature "adminXDemo")}}
|
||||
<li>
|
||||
<LinkTo @route="demo-x" @current-when="demo-x">{{svg-jar "star"}}AdminX Demo</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
|
||||
{{#if this.session.user.isOwnerOnly}}
|
||||
<ul class="gh-nav-list">
|
||||
{{#if this.showBilling}}
|
||||
<li class="relative">
|
||||
<a href="javascript:void(0)" class={{if this.billing.billingWindowOpen "active"}} {{action "toggleBillingModal" }} data-test-nav="billing">
|
||||
{{svg-jar "credit-card"}} Ghost(Pro)
|
||||
</a>
|
||||
</li>
|
||||
<li class="relative gh-nav-pro">
|
||||
<GhBillingUpdateButton />
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
<div class="gh-nav-top">
|
||||
<ul class="gh-nav-list gh-nav-main">
|
||||
{{#if (feature "ActivityPub")}}
|
||||
<li>
|
||||
<LinkTo @route="activitypub-x" @current-when="activitypub-x">{{svg-jar "star"}}ActivityPub</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.showMenuExtension}}
|
||||
<ul class="gh-nav-list gh-nav-settings">
|
||||
{{#if this.config.clientExtensions.menu.title}}
|
||||
<li class="gh-nav-list-h">{{this.config.clientExtensions.menu.title}}</li>
|
||||
{{/if}}
|
||||
{{#each this.config.clientExtensions.menu.items as |menuItem| }}
|
||||
<li>
|
||||
<a href="{{menuItem.href}}" target="_blank" rel="noopener noreferrer">{{svg-jar menuItem.icon}}{{menuItem.text}}</a>
|
||||
</li>
|
||||
{{#if (gh-user-can-admin this.session.user)}}
|
||||
<li class="relative gh-nav-list-home">
|
||||
<LinkTo @route="dashboard" @alt="Dashboard" title="Dashboard" data-test-nav="dashboard">{{svg-jar
|
||||
"house"}} Dashboard</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
<li class="relative">
|
||||
<span {{action "transitionToOrRefreshSite" on="click" }}>
|
||||
<LinkTo @route="site" data-test-nav="site" @current-when={{this.isOnSite}}
|
||||
@preventDefault={{false}}>
|
||||
{{svg-jar "view-site"}} View site
|
||||
</LinkTo>
|
||||
</span>
|
||||
<a href="{{this.config.blogUrl}}/" class="gh-secondary-action" title="Open site in new tab"
|
||||
target="_blank" rel="noopener noreferrer">
|
||||
<span>{{svg-jar "external"}}</span>
|
||||
</a>
|
||||
</li>
|
||||
{{#if (and (gh-user-can-admin this.session.user) this.config.stats)}}
|
||||
<li class="relative">
|
||||
<LinkTo @route="stats">{{svg-jar "stats-outline"}}Stats</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if (gh-user-can-admin this.session.user)}}
|
||||
<li class="relative">
|
||||
<a href="javascript:void(0)" class={{if this.explore.exploreWindowOpen "active" }} {{on "click" (fn
|
||||
this.toggleExploreWindow "" )}} data-test-nav="explore">
|
||||
{{svg-jar "globe-simple"}} Explore
|
||||
</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
<ul class="gh-nav-list gh-nav-manage">
|
||||
<li class="gh-nav-list-new relative">
|
||||
<GhLinkToCustomViewsIndex @route="posts" @query={{reset-query-params "posts" }}
|
||||
data-test-nav="posts">{{svg-jar "posts"}}Posts</GhLinkToCustomViewsIndex>
|
||||
<LinkTo @route="lexical-editor.new" @model="post" class="gh-secondary-action gh-nav-new-post"
|
||||
@alt="New post" title="New post" data-test-nav="new-story"><span>{{svg-jar "plus"}}</span>
|
||||
</LinkTo>
|
||||
{{#if this.session.user.isAuthorOrContributor}}
|
||||
{{#if this.customViews.forPosts}}
|
||||
<ul class="gh-nav-view-list">
|
||||
{{#each this.customViews.forPosts as |view|}}
|
||||
<li>
|
||||
<LinkTo @route="posts" @query={{reset-query-params "posts" view.filter}}
|
||||
data-test-nav-custom="{{view.route}}-{{view.name}}" title="{{view.name}}">
|
||||
<span class="gh-nav-viewname">{{view.name}}</span>
|
||||
<span class="flex items-center svg-{{view.color}}">
|
||||
{{#unless view.icon}}
|
||||
<span class="absolute circle"></span>
|
||||
{{/unless}}
|
||||
</span>
|
||||
</LinkTo>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{#if this.customViews.forPosts}}
|
||||
<button type="button" class="gh-nav-button-expand {{if this.navigation.settings.expanded.posts "
|
||||
expanded"}}" {{on "click" (fn this.navigation.toggleExpansion "posts" )}}
|
||||
aria-label="{{if this.navigation.settings.expanded.posts " Collapse custom post
|
||||
types" "Expand custom post types" }}">
|
||||
{{svg-jar (if this.navigation.settings.expanded.posts "arrow-down-stroke"
|
||||
"arrow-right-stroke")}}
|
||||
</button>
|
||||
{{#liquid-if this.navigation.settings.expanded.posts}}
|
||||
<ul class="gh-nav-view-list">
|
||||
{{#each this.customViews.forPosts as |view|}}
|
||||
<li>
|
||||
<LinkTo @route="posts" @query={{reset-query-params "posts" view.filter}}
|
||||
data-test-nav-custom="{{view.route}}-{{view.name}}" title="{{view.name}}">
|
||||
<span class="gh-nav-viewname">{{view.name}}</span>
|
||||
<span class="flex items-center svg-{{view.color}}">
|
||||
{{#unless view.icon}}
|
||||
<span class="absolute circle"></span>
|
||||
{{/unless}}
|
||||
</span>
|
||||
</LinkTo>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/liquid-if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</li>
|
||||
<li>
|
||||
{{!-- clicking the Content link whilst on the content screen should reset the filter --}}
|
||||
{{#if (eq this.router.currentRouteName "pages")}}
|
||||
<LinkTo @route="pages" @query={{reset-query-params "pages" }} class="active" data-test-nav="pages">
|
||||
{{svg-jar "page"}}Pages</LinkTo>
|
||||
{{else}}
|
||||
<LinkTo @route="pages" data-test-nav="pages">{{svg-jar "page"}}Pages</LinkTo>
|
||||
{{/if}}
|
||||
</li>
|
||||
{{#if this.showTagsNavigation}}
|
||||
<li>
|
||||
<LinkTo @route="tags" @current-when="tags tag tag.new" data-test-nav="tags">{{svg-jar "tag"}}Tags
|
||||
</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#if (gh-user-can-admin this.session.user)}}
|
||||
<li class="relative">
|
||||
{{#if (eq this.router.currentRouteName "members.index")}}
|
||||
<LinkTo @route="members" @current-when="members member member.new"
|
||||
@query={{reset-query-params "members.index" }} data-test-nav="members">{{svg-jar
|
||||
"members"}}Members
|
||||
{{#let (members-count-fetcher) as |count|}}
|
||||
{{#unless count.isLoading}}
|
||||
<span class="gh-nav-member-count">{{format-number count.count}}</span>
|
||||
{{/unless}}
|
||||
{{/let}}
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<LinkTo @route="members" @current-when="members member member.new" data-test-nav="members">{{svg-jar
|
||||
"members"}}Members
|
||||
{{#let (members-count-fetcher) as |count|}}
|
||||
{{#unless count.isLoading}}
|
||||
<span class="gh-nav-member-count">{{format-number count.count}}</span>
|
||||
{{/unless}}
|
||||
{{/let}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</li>
|
||||
|
||||
{{/if}}
|
||||
{{#if (feature "adminXDemo")}}
|
||||
<li>
|
||||
<LinkTo @route="demo-x" @current-when="demo-x">{{svg-jar "star"}}AdminX Demo</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if (feature "postsX")}}
|
||||
<li>
|
||||
<LinkTo @route="posts-x" @current-when="posts-x">{{svg-jar "chart"}}Post analytics</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
|
||||
{{#if this.session.user.isOwnerOnly}}
|
||||
<ul class="gh-nav-list">
|
||||
{{#if this.showBilling}}
|
||||
<li class="relative">
|
||||
<a href="javascript:void(0)" class={{if this.billing.billingWindowOpen "active" }}
|
||||
{{action "toggleBillingModal" }} data-test-nav="billing">
|
||||
{{svg-jar "credit-card"}} Ghost(Pro)
|
||||
</a>
|
||||
</li>
|
||||
<li class="relative gh-nav-pro">
|
||||
<GhBillingUpdateButton />
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.showMenuExtension}}
|
||||
<ul class="gh-nav-list gh-nav-settings">
|
||||
{{#if this.config.clientExtensions.menu.title}}
|
||||
<li class="gh-nav-list-h">{{this.config.clientExtensions.menu.title}}</li>
|
||||
{{/if}}
|
||||
{{#each this.config.clientExtensions.menu.items as |menuItem| }}
|
||||
<li>
|
||||
<a href="{{menuItem.href}}" target="_blank" rel="noopener noreferrer">{{svg-jar
|
||||
menuItem.icon}}{{menuItem.text}}</a>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
<GhNavMenu::Footer />
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
3
ghost/admin/app/controllers/posts-x.js
Normal file
3
ghost/admin/app/controllers/posts-x.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import Controller from '@ember/controller';
|
||||
|
||||
export default class PostsXController extends Controller {}
|
|
@ -52,6 +52,10 @@ Router.map(function () {
|
|||
this.route('demo-x', {path: '/*sub'});
|
||||
});
|
||||
|
||||
this.route('posts-x', function () {
|
||||
this.route('posts-x', {path: '/*sub'});
|
||||
});
|
||||
|
||||
this.route('settings-x', {path: '/settings'}, function () {
|
||||
this.route('settings-x', {path: '/*sub'});
|
||||
});
|
||||
|
|
3
ghost/admin/app/routes/posts-x.js
Normal file
3
ghost/admin/app/routes/posts-x.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
|
||||
|
||||
export default class PostsXRoute extends AuthenticatedRoute {}
|
|
@ -76,6 +76,7 @@ export default class FeatureService extends Service {
|
|||
@feature('editorExcerpt') editorExcerpt;
|
||||
@feature('contentVisibility') contentVisibility;
|
||||
@feature('commentImprovements') commentImprovements;
|
||||
@feature('postsX') postsX;
|
||||
|
||||
_user = null;
|
||||
|
||||
|
|
1
ghost/admin/app/templates/posts-x.hbs
Normal file
1
ghost/admin/app/templates/posts-x.hbs
Normal file
|
@ -0,0 +1 @@
|
|||
<AdminX::Posts />
|
|
@ -6,7 +6,7 @@ const fs = require('fs');
|
|||
const path = require('path');
|
||||
const camelCase = require('lodash/camelCase');
|
||||
|
||||
const adminXApps = ['admin-x-demo', 'admin-x-settings', 'admin-x-activitypub'];
|
||||
const adminXApps = ['admin-x-demo', 'admin-x-settings', 'admin-x-activitypub', 'posts'];
|
||||
|
||||
function generateHash(filePath) {
|
||||
const fileContents = fs.readFileSync(filePath, 'utf8');
|
||||
|
|
|
@ -190,7 +190,8 @@
|
|||
"projects": [
|
||||
"@tryghost/admin-x-demo",
|
||||
"@tryghost/admin-x-settings",
|
||||
"@tryghost/admin-x-activitypub"
|
||||
"@tryghost/admin-x-activitypub",
|
||||
"@tryghost/posts"
|
||||
],
|
||||
"target": "build"
|
||||
}
|
||||
|
@ -207,7 +208,8 @@
|
|||
"projects": [
|
||||
"@tryghost/admin-x-demo",
|
||||
"@tryghost/admin-x-settings",
|
||||
"@tryghost/admin-x-activitypub"
|
||||
"@tryghost/admin-x-activitypub",
|
||||
"@tryghost/posts"
|
||||
],
|
||||
"target": "build"
|
||||
}
|
||||
|
|
|
@ -52,7 +52,8 @@ const ALPHA_FEATURES = [
|
|||
'collectionsCard',
|
||||
'lexicalIndicators',
|
||||
'adminXDemo',
|
||||
'commentImprovements'
|
||||
'commentImprovements',
|
||||
'postsX'
|
||||
];
|
||||
|
||||
module.exports.GA_KEYS = [...GA_FEATURES];
|
||||
|
|
|
@ -29,6 +29,7 @@ Object {
|
|||
"members": true,
|
||||
"newEmailAddresses": true,
|
||||
"outboundLinkTagging": true,
|
||||
"postsX": true,
|
||||
"stripeAutomaticTax": true,
|
||||
"themeErrorsNotification": true,
|
||||
"urlCache": true,
|
||||
|
|
182
yarn.lock
182
yarn.lock
|
@ -4230,6 +4230,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.0.tgz#42ef83b3b56dccad5d703ae8c42919a68798bbe2"
|
||||
integrity sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==
|
||||
|
||||
"@radix-ui/primitive@1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.1.tgz#fc169732d755c7fbad33ba8d0cd7fd10c90dc8e3"
|
||||
integrity sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==
|
||||
|
||||
"@radix-ui/react-arrow@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz#c24f7968996ed934d57fe6cde5d6ec7266e1d25d"
|
||||
|
@ -4245,6 +4250,13 @@
|
|||
dependencies:
|
||||
"@radix-ui/react-primitive" "2.0.0"
|
||||
|
||||
"@radix-ui/react-arrow@1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.1.tgz#2103721933a8bfc6e53bbfbdc1aaad5fc8ba0dd7"
|
||||
integrity sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w==
|
||||
dependencies:
|
||||
"@radix-ui/react-primitive" "2.0.1"
|
||||
|
||||
"@radix-ui/react-avatar@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-avatar/-/react-avatar-1.1.0.tgz#457c81334c93f4608df15f081e7baa286558d6a2"
|
||||
|
@ -4290,6 +4302,16 @@
|
|||
"@radix-ui/react-primitive" "2.0.0"
|
||||
"@radix-ui/react-slot" "1.1.0"
|
||||
|
||||
"@radix-ui/react-collection@1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.1.1.tgz#be2c7e01d3508e6d4b6d838f492e7d182f17d3b0"
|
||||
integrity sha512-LwT3pSho9Dljg+wY2KN2mrrh6y3qELfftINERIzBUO9e0N+t0oMTyn3k9iv+ZqgrwGkRnLpNJrsMv9BZlt2yuA==
|
||||
dependencies:
|
||||
"@radix-ui/react-compose-refs" "1.1.1"
|
||||
"@radix-ui/react-context" "1.1.1"
|
||||
"@radix-ui/react-primitive" "2.0.1"
|
||||
"@radix-ui/react-slot" "1.1.1"
|
||||
|
||||
"@radix-ui/react-compose-refs@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz#7ed868b66946aa6030e580b1ffca386dd4d21989"
|
||||
|
@ -4302,6 +4324,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz#656432461fc8283d7b591dcf0d79152fae9ecc74"
|
||||
integrity sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==
|
||||
|
||||
"@radix-ui/react-compose-refs@1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz#6f766faa975f8738269ebb8a23bad4f5a8d2faec"
|
||||
integrity sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==
|
||||
|
||||
"@radix-ui/react-context@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.1.tgz#fe46e67c96b240de59187dcb7a1a50ce3e2ec00c"
|
||||
|
@ -4314,6 +4341,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.0.tgz#6df8d983546cfd1999c8512f3a8ad85a6e7fcee8"
|
||||
integrity sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==
|
||||
|
||||
"@radix-ui/react-context@1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.1.tgz#82074aa83a472353bb22e86f11bcbd1c61c4c71a"
|
||||
integrity sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==
|
||||
|
||||
"@radix-ui/react-direction@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.0.1.tgz#9cb61bf2ccf568f3421422d182637b7f47596c9b"
|
||||
|
@ -4349,6 +4381,30 @@
|
|||
"@radix-ui/react-use-callback-ref" "1.1.0"
|
||||
"@radix-ui/react-use-escape-keydown" "1.1.0"
|
||||
|
||||
"@radix-ui/react-dismissable-layer@1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.2.tgz#771594b202f32bc8ffeb278c565f10c513814aee"
|
||||
integrity sha512-kEHnlhv7wUggvhuJPkyw4qspXLJOdYoAP4dO2c8ngGuXTq1w/HZp1YeVB+NQ2KbH1iEG+pvOCGYSqh9HZOz6hg==
|
||||
dependencies:
|
||||
"@radix-ui/primitive" "1.1.1"
|
||||
"@radix-ui/react-compose-refs" "1.1.1"
|
||||
"@radix-ui/react-primitive" "2.0.1"
|
||||
"@radix-ui/react-use-callback-ref" "1.1.0"
|
||||
"@radix-ui/react-use-escape-keydown" "1.1.0"
|
||||
|
||||
"@radix-ui/react-dropdown-menu@2.1.3":
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.3.tgz#02665f99bfdcefc33a8a15dc130e9b98ebdf7671"
|
||||
integrity sha512-eKyAfA9e4HOavzyGJC6kiDIlHMPzAU0zqSqTg+VwS0Okvb9nkTo7L4TugkCUqM3I06ciSpdtYQ73cgB7tyUgVw==
|
||||
dependencies:
|
||||
"@radix-ui/primitive" "1.1.1"
|
||||
"@radix-ui/react-compose-refs" "1.1.1"
|
||||
"@radix-ui/react-context" "1.1.1"
|
||||
"@radix-ui/react-id" "1.1.0"
|
||||
"@radix-ui/react-menu" "2.1.3"
|
||||
"@radix-ui/react-primitive" "2.0.1"
|
||||
"@radix-ui/react-use-controllable-state" "1.1.0"
|
||||
|
||||
"@radix-ui/react-focus-guards@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz#1ea7e32092216b946397866199d892f71f7f98ad"
|
||||
|
@ -4361,6 +4417,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz#8e9abb472a9a394f59a1b45f3dd26cfe3fc6da13"
|
||||
integrity sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==
|
||||
|
||||
"@radix-ui/react-focus-guards@1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz#8635edd346304f8b42cae86b05912b61aef27afe"
|
||||
integrity sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==
|
||||
|
||||
"@radix-ui/react-focus-scope@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.3.tgz#9c2e8d4ed1189a1d419ee61edd5c1828726472f9"
|
||||
|
@ -4380,6 +4441,15 @@
|
|||
"@radix-ui/react-primitive" "2.0.0"
|
||||
"@radix-ui/react-use-callback-ref" "1.1.0"
|
||||
|
||||
"@radix-ui/react-focus-scope@1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.1.tgz#5c602115d1db1c4fcfa0fae4c3b09bb8919853cb"
|
||||
integrity sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==
|
||||
dependencies:
|
||||
"@radix-ui/react-compose-refs" "1.1.1"
|
||||
"@radix-ui/react-primitive" "2.0.1"
|
||||
"@radix-ui/react-use-callback-ref" "1.1.0"
|
||||
|
||||
"@radix-ui/react-form@0.0.3":
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-form/-/react-form-0.0.3.tgz#328e7163e723ccc748459d66a2d685d7b4f85d5a"
|
||||
|
@ -4416,6 +4486,30 @@
|
|||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-primitive" "1.0.3"
|
||||
|
||||
"@radix-ui/react-menu@2.1.3":
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-menu/-/react-menu-2.1.3.tgz#5a3330cf5dc5d48666da31ba0e83fef99288e367"
|
||||
integrity sha512-wY5SY6yCiJYP+DMIy7RrjF4shoFpB9LJltliVwejBm8T2yepWDJgKBhIFYOGWYR/lFHOCtbstN9duZFu6gmveQ==
|
||||
dependencies:
|
||||
"@radix-ui/primitive" "1.1.1"
|
||||
"@radix-ui/react-collection" "1.1.1"
|
||||
"@radix-ui/react-compose-refs" "1.1.1"
|
||||
"@radix-ui/react-context" "1.1.1"
|
||||
"@radix-ui/react-direction" "1.1.0"
|
||||
"@radix-ui/react-dismissable-layer" "1.1.2"
|
||||
"@radix-ui/react-focus-guards" "1.1.1"
|
||||
"@radix-ui/react-focus-scope" "1.1.1"
|
||||
"@radix-ui/react-id" "1.1.0"
|
||||
"@radix-ui/react-popper" "1.2.1"
|
||||
"@radix-ui/react-portal" "1.1.3"
|
||||
"@radix-ui/react-presence" "1.1.2"
|
||||
"@radix-ui/react-primitive" "2.0.1"
|
||||
"@radix-ui/react-roving-focus" "1.1.1"
|
||||
"@radix-ui/react-slot" "1.1.1"
|
||||
"@radix-ui/react-use-callback-ref" "1.1.0"
|
||||
aria-hidden "^1.1.1"
|
||||
react-remove-scroll "2.6.0"
|
||||
|
||||
"@radix-ui/react-popover@1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-popover/-/react-popover-1.1.1.tgz#604b783cdb3494ed4f16a58c17f0e81e61ab7775"
|
||||
|
@ -4470,6 +4564,22 @@
|
|||
"@radix-ui/react-use-size" "1.1.0"
|
||||
"@radix-ui/rect" "1.1.0"
|
||||
|
||||
"@radix-ui/react-popper@1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.2.1.tgz#2fc66cfc34f95f00d858924e3bee54beae2dff0a"
|
||||
integrity sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==
|
||||
dependencies:
|
||||
"@floating-ui/react-dom" "^2.0.0"
|
||||
"@radix-ui/react-arrow" "1.1.1"
|
||||
"@radix-ui/react-compose-refs" "1.1.1"
|
||||
"@radix-ui/react-context" "1.1.1"
|
||||
"@radix-ui/react-primitive" "2.0.1"
|
||||
"@radix-ui/react-use-callback-ref" "1.1.0"
|
||||
"@radix-ui/react-use-layout-effect" "1.1.0"
|
||||
"@radix-ui/react-use-rect" "1.1.0"
|
||||
"@radix-ui/react-use-size" "1.1.0"
|
||||
"@radix-ui/rect" "1.1.0"
|
||||
|
||||
"@radix-ui/react-portal@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.3.tgz#ffb961244c8ed1b46f039e6c215a6c4d9989bda1"
|
||||
|
@ -4486,6 +4596,14 @@
|
|||
"@radix-ui/react-primitive" "2.0.0"
|
||||
"@radix-ui/react-use-layout-effect" "1.1.0"
|
||||
|
||||
"@radix-ui/react-portal@1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.3.tgz#b0ea5141103a1671b715481b13440763d2ac4440"
|
||||
integrity sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==
|
||||
dependencies:
|
||||
"@radix-ui/react-primitive" "2.0.1"
|
||||
"@radix-ui/react-use-layout-effect" "1.1.0"
|
||||
|
||||
"@radix-ui/react-presence@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.0.tgz#227d84d20ca6bfe7da97104b1a8b48a833bfb478"
|
||||
|
@ -4494,6 +4612,14 @@
|
|||
"@radix-ui/react-compose-refs" "1.1.0"
|
||||
"@radix-ui/react-use-layout-effect" "1.1.0"
|
||||
|
||||
"@radix-ui/react-presence@1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.2.tgz#bb764ed8a9118b7ec4512da5ece306ded8703cdc"
|
||||
integrity sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==
|
||||
dependencies:
|
||||
"@radix-ui/react-compose-refs" "1.1.1"
|
||||
"@radix-ui/react-use-layout-effect" "1.1.0"
|
||||
|
||||
"@radix-ui/react-primitive@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz#d49ea0f3f0b2fe3ab1cb5667eb03e8b843b914d0"
|
||||
|
@ -4509,6 +4635,13 @@
|
|||
dependencies:
|
||||
"@radix-ui/react-slot" "1.1.0"
|
||||
|
||||
"@radix-ui/react-primitive@2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz#6d9efc550f7520135366f333d1e820cf225fad9e"
|
||||
integrity sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==
|
||||
dependencies:
|
||||
"@radix-ui/react-slot" "1.1.1"
|
||||
|
||||
"@radix-ui/react-radio-group@1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-radio-group/-/react-radio-group-1.2.0.tgz#f937dd6b9436ded80c4bebdf3901c20cb8bcbb5a"
|
||||
|
@ -4556,6 +4689,21 @@
|
|||
"@radix-ui/react-use-callback-ref" "1.1.0"
|
||||
"@radix-ui/react-use-controllable-state" "1.1.0"
|
||||
|
||||
"@radix-ui/react-roving-focus@1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.1.tgz#3b3abb1e03646937f28d9ab25e96343667ca6520"
|
||||
integrity sha512-QE1RoxPGJ/Nm8Qmk0PxP8ojmoaS67i0s7hVssS7KuI2FQoc/uzVlZsqKfQvxPE6D8hICCPHJ4D88zNhT3OOmkw==
|
||||
dependencies:
|
||||
"@radix-ui/primitive" "1.1.1"
|
||||
"@radix-ui/react-collection" "1.1.1"
|
||||
"@radix-ui/react-compose-refs" "1.1.1"
|
||||
"@radix-ui/react-context" "1.1.1"
|
||||
"@radix-ui/react-direction" "1.1.0"
|
||||
"@radix-ui/react-id" "1.1.0"
|
||||
"@radix-ui/react-primitive" "2.0.1"
|
||||
"@radix-ui/react-use-callback-ref" "1.1.0"
|
||||
"@radix-ui/react-use-controllable-state" "1.1.0"
|
||||
|
||||
"@radix-ui/react-select@^1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-select/-/react-select-1.2.2.tgz#caa981fa0d672cf3c1b2a5240135524e69b32181"
|
||||
|
@ -4614,6 +4762,13 @@
|
|||
dependencies:
|
||||
"@radix-ui/react-compose-refs" "1.1.0"
|
||||
|
||||
"@radix-ui/react-slot@1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.1.tgz#ab9a0ffae4027db7dc2af503c223c978706affc3"
|
||||
integrity sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==
|
||||
dependencies:
|
||||
"@radix-ui/react-compose-refs" "1.1.1"
|
||||
|
||||
"@radix-ui/react-switch@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-switch/-/react-switch-1.1.0.tgz#fcf8e778500f1d60d4b2bec2fc3fad77a7c118e3"
|
||||
|
@ -4641,6 +4796,20 @@
|
|||
"@radix-ui/react-roving-focus" "1.1.0"
|
||||
"@radix-ui/react-use-controllable-state" "1.1.0"
|
||||
|
||||
"@radix-ui/react-tabs@1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.1.2.tgz#a72da059593cba30fccb30a226d63af686b32854"
|
||||
integrity sha512-9u/tQJMcC2aGq7KXpGivMm1mgq7oRJKXphDwdypPd/j21j/2znamPU8WkXgnhUaTrSFNIt8XhOyCAupg8/GbwQ==
|
||||
dependencies:
|
||||
"@radix-ui/primitive" "1.1.1"
|
||||
"@radix-ui/react-context" "1.1.1"
|
||||
"@radix-ui/react-direction" "1.1.0"
|
||||
"@radix-ui/react-id" "1.1.0"
|
||||
"@radix-ui/react-presence" "1.1.2"
|
||||
"@radix-ui/react-primitive" "2.0.1"
|
||||
"@radix-ui/react-roving-focus" "1.1.1"
|
||||
"@radix-ui/react-use-controllable-state" "1.1.0"
|
||||
|
||||
"@radix-ui/react-toggle-group@1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle-group/-/react-toggle-group-1.0.4.tgz#f5b5c8c477831b013bec3580c55e20a68179d6ec"
|
||||
|
@ -27118,7 +27287,7 @@ react-refresh@^0.14.0:
|
|||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
|
||||
integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==
|
||||
|
||||
react-remove-scroll-bar@^2.3.3, react-remove-scroll-bar@^2.3.4:
|
||||
react-remove-scroll-bar@^2.3.3, react-remove-scroll-bar@^2.3.4, react-remove-scroll-bar@^2.3.6:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz#3e585e9d163be84a010180b18721e851ac81a29c"
|
||||
integrity sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==
|
||||
|
@ -27148,6 +27317,17 @@ react-remove-scroll@2.5.7:
|
|||
use-callback-ref "^1.3.0"
|
||||
use-sidecar "^1.1.2"
|
||||
|
||||
react-remove-scroll@2.6.0:
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.6.0.tgz#fb03a0845d7768a4f1519a99fdb84983b793dc07"
|
||||
integrity sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==
|
||||
dependencies:
|
||||
react-remove-scroll-bar "^2.3.6"
|
||||
react-style-singleton "^2.2.1"
|
||||
tslib "^2.1.0"
|
||||
use-callback-ref "^1.3.0"
|
||||
use-sidecar "^1.1.2"
|
||||
|
||||
react-select@5.8.2:
|
||||
version "5.8.2"
|
||||
resolved "https://registry.yarnpkg.com/react-select/-/react-select-5.8.2.tgz#0d7ccb1895d61aafcd090fbf65aa9e506225a854"
|
||||
|
|
Loading…
Reference in a new issue