1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-29 22:01:14 +03:00

First-pass for Compound styles

This commit is contained in:
Germain
2023-06-07 12:07:01 +01:00
committed by Quentin Gliech
parent dfdfc25d00
commit 33da9f05fe
17 changed files with 493 additions and 259 deletions

View File

@ -14,7 +14,7 @@
"@urql/exchange-graphcache": "^6.0.4",
"@urql/exchange-refocus": "^1.0.2",
"@urql/exchange-request-policy": "^1.0.2",
"@vector-im/compound-web": "https://github.com/vector-im/compound-web.git#5208d2d442587efb9938265841574f73ce97dff1",
"@vector-im/compound-web": "https://github.com/vector-im/compound-web.git",
"date-fns": "^2.30.0",
"graphql": "^16.6.0",
"jotai": "^2.1.0",
@ -62,6 +62,7 @@
"vite": "^4.3.8",
"vite-plugin-eslint": "^1.8.1",
"vite-plugin-graphql-codegen": "^3.2.2",
"vite-plugin-svgr": "^3.2.0",
"vitest": "^0.31.1"
}
},
@ -6872,6 +6873,257 @@
"url": "https://opencollective.com/storybook"
}
},
"node_modules/@svgr/babel-plugin-add-jsx-attribute": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-7.0.0.tgz",
"integrity": "sha512-khWbXesWIP9v8HuKCl2NU2HNAyqpSQ/vkIl36Nbn4HIwEYSRWL0H7Gs6idJdha2DkpFDWlsqMELvoCE8lfFY6Q==",
"dev": true,
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@svgr/babel-plugin-remove-jsx-attribute": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-7.0.0.tgz",
"integrity": "sha512-iiZaIvb3H/c7d3TH2HBeK91uI2rMhZNwnsIrvd7ZwGLkFw6mmunOCoVnjdYua662MqGFxlN9xTq4fv9hgR4VXQ==",
"dev": true,
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-7.0.0.tgz",
"integrity": "sha512-sQQmyo+qegBx8DfFc04PFmIO1FP1MHI1/QEpzcIcclo5OAISsOJPW76ZIs0bDyO/DBSJEa/tDa1W26pVtt0FRw==",
"dev": true,
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-7.0.0.tgz",
"integrity": "sha512-i6MaAqIZXDOJeikJuzocByBf8zO+meLwfQ/qMHIjCcvpnfvWf82PFvredEZElErB5glQFJa2KVKk8N2xV6tRRA==",
"dev": true,
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@svgr/babel-plugin-svg-dynamic-title": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-7.0.0.tgz",
"integrity": "sha512-BoVSh6ge3SLLpKC0pmmN9DFlqgFy4NxNgdZNLPNJWBUU7TQpDWeBuyVuDW88iXydb5Cv0ReC+ffa5h3VrKfk1w==",
"dev": true,
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@svgr/babel-plugin-svg-em-dimensions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-7.0.0.tgz",
"integrity": "sha512-tNDcBa+hYn0gO+GkP/AuNKdVtMufVhU9fdzu+vUQsR18RIJ9RWe7h/pSBY338RO08wArntwbDk5WhQBmhf2PaA==",
"dev": true,
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@svgr/babel-plugin-transform-react-native-svg": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-7.0.0.tgz",
"integrity": "sha512-qw54u8ljCJYL2KtBOjI5z7Nzg8LnSvQOP5hPKj77H4VQL4+HdKbAT5pnkkZLmHKYwzsIHSYKXxHouD8zZamCFQ==",
"dev": true,
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@svgr/babel-plugin-transform-svg-component": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-7.0.0.tgz",
"integrity": "sha512-CcFECkDj98daOg9jE3Bh3uyD9kzevCAnZ+UtzG6+BQG/jOQ2OA3jHnX6iG4G1MCJkUQFnUvEv33NvQfqrb/F3A==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@svgr/babel-preset": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-7.0.0.tgz",
"integrity": "sha512-EX/NHeFa30j5UjldQGVQikuuQNHUdGmbh9kEpBKofGUtF0GUPJ4T4rhoYiqDAOmBOxojyot36JIFiDUHUK1ilQ==",
"dev": true,
"dependencies": {
"@svgr/babel-plugin-add-jsx-attribute": "^7.0.0",
"@svgr/babel-plugin-remove-jsx-attribute": "^7.0.0",
"@svgr/babel-plugin-remove-jsx-empty-expression": "^7.0.0",
"@svgr/babel-plugin-replace-jsx-attribute-value": "^7.0.0",
"@svgr/babel-plugin-svg-dynamic-title": "^7.0.0",
"@svgr/babel-plugin-svg-em-dimensions": "^7.0.0",
"@svgr/babel-plugin-transform-react-native-svg": "^7.0.0",
"@svgr/babel-plugin-transform-svg-component": "^7.0.0"
},
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@svgr/core": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@svgr/core/-/core-7.0.0.tgz",
"integrity": "sha512-ztAoxkaKhRVloa3XydohgQQCb0/8x9T63yXovpmHzKMkHO6pkjdsIAWKOS4bE95P/2quVh1NtjSKlMRNzSBffw==",
"dev": true,
"dependencies": {
"@babel/core": "^7.21.3",
"@svgr/babel-preset": "^7.0.0",
"camelcase": "^6.2.0",
"cosmiconfig": "^8.1.3"
},
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
}
},
"node_modules/@svgr/core/node_modules/camelcase": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
"dev": true,
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@svgr/core/node_modules/cosmiconfig": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz",
"integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==",
"dev": true,
"dependencies": {
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.0",
"parse-json": "^5.0.0",
"path-type": "^4.0.0"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
}
},
"node_modules/@svgr/hast-util-to-babel-ast": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-7.0.0.tgz",
"integrity": "sha512-42Ej9sDDEmsJKjrfQ1PHmiDiHagh/u9AHO9QWbeNx4KmD9yS5d1XHmXUNINfUcykAU+4431Cn+k6Vn5mWBYimQ==",
"dev": true,
"dependencies": {
"@babel/types": "^7.21.3",
"entities": "^4.4.0"
},
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
}
},
"node_modules/@svgr/hast-util-to-babel-ast/node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"dev": true,
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/@svgr/plugin-jsx": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-7.0.0.tgz",
"integrity": "sha512-SWlTpPQmBUtLKxXWgpv8syzqIU8XgFRvyhfkam2So8b3BE0OS0HPe5UfmlJ2KIC+a7dpuuYovPR2WAQuSyMoPw==",
"dev": true,
"dependencies": {
"@babel/core": "^7.21.3",
"@svgr/babel-preset": "^7.0.0",
"@svgr/hast-util-to-babel-ast": "^7.0.0",
"svg-parser": "^2.0.4"
},
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
}
},
"node_modules/@tabler/icons": {
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-2.17.0.tgz",
@ -7697,8 +7949,7 @@
},
"node_modules/@vector-im/compound-web": {
"version": "0.0.1",
"resolved": "git+ssh://git@github.com/vector-im/compound-web.git#5208d2d442587efb9938265841574f73ce97dff1",
"integrity": "sha512-qtJEPOjPeKgFX0YpPaddo4aqqYwQnzzSWE+TUl55a6XF3eupR3iOCZ4V+XR78gDJGlXmRLbg57rqqE3I53vd8A==",
"resolved": "git+ssh://git@github.com/vector-im/compound-web.git#51c611e84658c3d9a3bc8fecf97ceac7d45c5838",
"license": "Apache-2.0",
"dependencies": {
"@radix-ui/react-form": "^0.0.2",
@ -17611,6 +17862,12 @@
"resolved": "https://registry.npmjs.org/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz",
"integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g=="
},
"node_modules/svg-parser": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz",
"integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==",
"dev": true
},
"node_modules/svg-path-bounds": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/svg-path-bounds/-/svg-path-bounds-1.0.2.tgz",
@ -18855,6 +19112,48 @@
"vite": "^2.7.0 || ^3.0.0 || ^4.0.0"
}
},
"node_modules/vite-plugin-svgr": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-3.2.0.tgz",
"integrity": "sha512-Uvq6niTvhqJU6ga78qLKBFJSDvxWhOnyfQSoKpDPMAGxJPo5S3+9hyjExE5YDj6Lpa4uaLkGc1cBgxXov+LjSw==",
"dev": true,
"dependencies": {
"@rollup/pluginutils": "^5.0.2",
"@svgr/core": "^7.0.0",
"@svgr/plugin-jsx": "^7.0.0"
},
"peerDependencies": {
"vite": "^2.6.0 || 3 || 4"
}
},
"node_modules/vite-plugin-svgr/node_modules/@rollup/pluginutils": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz",
"integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==",
"dev": true,
"dependencies": {
"@types/estree": "^1.0.0",
"estree-walker": "^2.0.2",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/vite-plugin-svgr/node_modules/@types/estree": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz",
"integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==",
"dev": true
},
"node_modules/vitest": {
"version": "0.31.1",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-0.31.1.tgz",

View File

@ -22,7 +22,7 @@
"@urql/exchange-graphcache": "^6.0.4",
"@urql/exchange-refocus": "^1.0.2",
"@urql/exchange-request-policy": "^1.0.2",
"@vector-im/compound-web": "https://github.com/vector-im/compound-web.git#5208d2d442587efb9938265841574f73ce97dff1",
"@vector-im/compound-web": "https://github.com/vector-im/compound-web.git",
"date-fns": "^2.30.0",
"graphql": "^16.6.0",
"jotai": "^2.1.0",
@ -70,6 +70,7 @@
"vite": "^4.3.8",
"vite-plugin-eslint": "^1.8.1",
"vite-plugin-graphql-codegen": "^3.2.2",
"vite-plugin-svgr": "^3.2.0",
"vitest": "^0.31.1"
}
}

View File

@ -149,11 +149,15 @@ export const Link: React.FC<
return (
<a
href={path}
onClick={(e) => {
e.preventDefault();
startTransition(() => {
setRoute(route);
});
onClick={(e: React.MouseEvent) => {
// Local links should be handled by the internal routers
// external links do not require a transition
if (!path.startsWith("http")) {
e.preventDefault();
startTransition(() => {
setRoute(route);
});
}
}}
{...props}
>

View File

@ -20,13 +20,7 @@ type Props = {
const Block: React.FC<Props> = ({ children, highlight, className }) => {
return (
<div
className={`p-4 dark:text-white grid rounded ${className} ${
highlight
? "border-2 border-grey-50 dark:border-grey-450 bg-white dark:bg-black"
: "bg-grey-50 dark:bg-grey-450"
}`}
>
<div className={className} data-active={highlight}>
{children}
</div>
);

View File

@ -17,9 +17,7 @@ type Props = {
};
const BlockList: React.FC<Props> = ({ children }) => {
return (
<div className="grid grid-cols-1 gap-1 group content-start">{children}</div>
);
return <div className="my-2">{children}</div>;
};
export default BlockList;

View File

@ -12,12 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Link } from "../Router";
import IconWebBrowser from "@vector-im/compound-design-tokens/icons/web-browser.svg";
import { Body } from "@vector-im/compound-web";
import { FragmentType, graphql, useFragment } from "../gql";
import Block from "./Block";
import DateTime from "./DateTime";
import { Body, Subtitle } from "./Typography";
const FRAGMENT = graphql(/* GraphQL */ `
fragment BrowserSession_session on BrowserSession {
@ -38,28 +39,33 @@ type Props = {
const BrowserSession: React.FC<Props> = ({ session, isCurrent }) => {
const data = useFragment(FRAGMENT, session);
const lastAuthentication = data.lastAuthentication?.createdAt;
// const lastAuthentication = data.lastAuthentication?.createdAt;
const createdAt = data.createdAt;
return (
<Block>
{isCurrent && <Subtitle>Current session</Subtitle>}
<Body>
<Link
route={{ type: "session", id: data.id }}
className="text-links hover:text-links/75"
>
Started: <DateTime datetime={createdAt} />
</Link>
</Body>
<Body>
Last authentication:{" "}
{lastAuthentication ? (
<DateTime datetime={lastAuthentication} />
<Block className="my-2">
<IconWebBrowser
className="session-icon float-left mr-2"
width="24"
height="24"
/>
<Body size="md" weight="medium">
{isCurrent ? (
<>
<strong>Current</strong> browser session
</>
) : (
"never"
<>Browser Session</>
)}
</Body>
<div className="flex flex-row justify-between">
<Body size="sm" className="secondary-text">
Signed in <DateTime datetime={createdAt} />
</Body>
<Body as="a" size="sm" weight="medium" href="#" data-kind="critical">
Sign out
</Body>
</div>
</Block>
);
};

View File

@ -1,89 +0,0 @@
// Copyright 2022 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import type { Meta, StoryObj } from "@storybook/react";
import Button from "./Button";
const meta = {
title: "UI/Button",
component: Button,
tags: ["autodocs"],
argTypes: {
onClick: { action: true },
compact: {
defaultValue: false,
},
ghost: {
defaultValue: false,
},
disabled: {
defaultValue: false,
},
},
} satisfies Meta<typeof Button>;
export default meta;
type Story = StoryObj<typeof Button>;
export const Basic: Story = {
args: {
children: "Button",
},
};
export const Regular: Story = {
args: {
children: "Button",
compact: false,
ghost: false,
disabled: false,
},
};
export const Compact: Story = {
args: {
children: "Compact",
compact: true,
ghost: false,
disabled: false,
},
};
export const Disabled: Story = {
args: {
children: "Disabled",
compact: false,
ghost: false,
disabled: true,
},
};
export const Ghost: Story = {
args: {
children: "Ghost",
compact: false,
ghost: true,
disabled: false,
},
};
export const GhostDisabled: Story = {
args: {
children: "Ghost",
compact: false,
ghost: true,
disabled: true,
},
};

View File

@ -1,72 +0,0 @@
// Copyright 2022 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
type Props = {
/** The label of the button */
children: string;
/** Additional classes to apply to the button */
className?: string;
/** Makes the button more compact */
compact?: boolean;
/** Uses the 'ghost' (outline) alternative */
ghost?: boolean;
/** Disables all interactions with the button */
disabled?: boolean;
/** The type of the button */
type?: "button" | "submit" | "reset";
} & React.HTMLProps<HTMLButtonElement>;
const Button: React.FC<Props> = ({
children,
className: extraClassName,
compact,
ghost,
disabled,
type,
...props
}) => {
const sizeClass = compact ? "py-1 px-3" : "py-1 px-5";
let ghostClass;
let normalClass;
if (disabled) {
ghostClass = "opacity-30 border border-accent text-accent";
normalClass = "opacity-30 border border-accent bg-accent text-white";
} else {
ghostClass = "border-accent border hover:bg-accent/10 text-accent";
normalClass =
"bg-accent border border-accent hover:bg-accent/75 hover:border-accent/75 text-white";
}
const colors = ghost ? ghostClass : normalClass;
return (
<button
{...props}
type={type || "button"}
className={`rounded-lg font-semibold ${colors} ${sizeClass} ${extraClassName}`}
disabled={disabled}
>
{children}
</button>
);
};
export default Button;

View File

@ -17,14 +17,40 @@ import NavItem from "./NavItem";
const Layout: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
return (
<div className="bg-grey-25 text-black-900 dark:bg-black-800 dark:text-white flex flex-col min-h-screen">
<NavBar className="mx-auto px-3 py-4 container">
<>
<NavBar className="nav-bar container">
<NavItem route={{ type: "home" }}>Home</NavItem>
<NavItem route={{ type: "account" }}>My Account</NavItem>
</NavBar>
<main className="mx-auto p-4 container">{children}</main>
</div>
<hr className="my-2" />
<main className="container">{children}</main>
<hr className="my-2" />
<footer className="text-center">
<a href="https://matrix.org" target="_blank" rel="noreferrer noopener">
<img
className="inline my-2"
src="https://matrix.org/images/matrix-logo.svg"
alt="Matrix.org"
/>
</a>
<NavBar className="nav-bar container">
<NavItem href="https://matrix.org/legal/copyright-notice">
Info
</NavItem>
<NavItem href="https://matrix.org/legal/privacy-notice">
Privacy
</NavItem>
<NavItem href="https://matrix.org/legal/terms-and-conditions">
Terms & Conditions
</NavItem>
</NavBar>
</footer>
</>
);
};

View File

@ -17,7 +17,7 @@ const NavBar: React.FC<{
children: React.ReactNode;
}> = ({ className, children }) => (
<nav className={className}>
<ul className="flex bg-grey-50 dark:bg-black-950 rounded-lg">{children}</ul>
<ul className="flex flex-row gap-4 justify-center ">{children}</ul>
</nav>
);

View File

@ -16,24 +16,35 @@ import { useAtomValue } from "jotai";
import { Link, Route, routeAtom } from "../Router";
const NavItem: React.FC<{ route: Route; children: React.ReactNode }> = ({
route,
children,
}) => {
type NavItemProps = {
children: React.ReactNode;
} & ({ route: Route; href?: never } | { route?: never; href: string });
function isRoute(route: Route | undefined): route is Route {
return !!route?.type;
}
function isHref(href: string | undefined): href is string {
return typeof href === "string";
}
const NavItem: React.FC<NavItemProps> = ({ route, href, children }) => {
const currentRoute = useAtomValue(routeAtom);
return (
<li className="m-1 mr-0">
<Link
route={route}
className={
(currentRoute.type === route.type
? "bg-accent text-white"
: "hover:bg-grey-100 dark:hover:bg-grey-450 opacity-80 hover:opacity-100") +
" p-2 rounded block uppercase font-medium"
}
>
{children}
</Link>
{isRoute(route) && (
<Link
route={route}
className={currentRoute.type === route.type ? "active" : ""}
>
{children}
</Link>
)}
{isHref(href) && (
<a href={href} target="_blank" rel="noopenner noreferrer">
{children}
</a>
)}
</li>
);
};

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import Button from "./Button";
import { Button } from "@vector-im/compound-web";
type Props = {
onNext: (() => void) | null;
@ -28,30 +28,24 @@ const PaginationControls: React.FC<Props> = ({
disabled,
}) => {
return (
<div className="grid items-center grid-cols-3 gap-2">
{onPrev ? (
<Button compact disabled={disabled} ghost onClick={onPrev}>
Previous
</Button>
) : (
<Button compact disabled ghost>
Previous
</Button>
)}
{count !== undefined ? (
<div className="text-center">Total: {count}</div>
) : (
<div></div>
)}
{onNext ? (
<Button compact disabled={disabled} ghost onClick={onNext}>
Next
</Button>
) : (
<Button compact disabled ghost>
Next
</Button>
)}
<div className="grid items-center grid-cols-3 gap-2 my-2">
<Button
kind="secondary"
size="sm"
disabled={disabled || !onPrev}
onClick={() => onPrev?.()}
>
Previous
</Button>
<div className="text-center">{count && <>Total: {count}</>}</div>
<Button
kind="secondary"
size="sm"
disabled={disabled || !onNext}
onClick={() => onNext?.()}
>
Next
</Button>
</div>
);
};

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Button } from "@vector-im/compound-web";
import { atom, useAtom, useSetAtom } from "jotai";
import { atomFamily } from "jotai/utils";
import { atomWithMutation } from "jotai-urql";
@ -20,7 +21,6 @@ import { useRef, useTransition } from "react";
import { FragmentType, graphql, useFragment } from "../gql";
import Block from "./Block";
import Button from "./Button";
import DateTime from "./DateTime";
import Input from "./Input";
import Typography from "./Typography";
@ -237,18 +237,12 @@ const UserEmail: React.FC<{
<Button
disabled={pending}
onClick={onSetPrimaryClick}
compact
className="ml-2"
>
Set primary
</Button>
)}
<Button
disabled={pending}
onClick={onRemoveClick}
compact
className="ml-2"
>
<Button disabled={pending} onClick={onRemoveClick} className="ml-2">
Remove
</Button>
</>

View File

@ -12,14 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Heading, Body } from "@vector-im/compound-web";
import { useAtomValue } from "jotai";
import { atomFamily } from "jotai/utils";
import { atomWithQuery } from "jotai-urql";
import { graphql } from "../gql";
import Typography from "./Typography";
const QUERY = graphql(/* GraphQL */ `
query UserGreeting($userId: ID!) {
user(id: $userId) {
@ -43,9 +42,12 @@ const UserGreeting: React.FC<{ userId: string }> = ({ userId }) => {
if (result.data?.user) {
return (
<Typography variant="headline">
Hello, {result.data.user.username}!
</Typography>
<header className="oidc_Header">
<Heading size="xl" weight="semibold">
John Doe
</Heading>
<Body size="lg">{result.data.user.username}</Body>
</header>
);
}

View File

@ -1,4 +1,3 @@
/* eslint-disable */
import { IntrospectionQuery } from "graphql";
export default {
__schema: {

View File

@ -19,6 +19,60 @@
@tailwind components;
@tailwind utilities;
body, .docs-story {
@apply bg-grey-25 text-black-900 dark:bg-black-800 dark:text-white;
body {
font: var(--cpd-font-body-md-regular);
background: var(--cpd-color-bg-canvas-default);
width: 378px;
margin: var(--cpd-space-10x) auto var(--cpd-space-6x) auto;
}
.oidc_Header {
text-align: center;
}
.oidc_Header > * {
margin: 0;
}
.secondary-text {
color: var(--cpd-color-text-secondary);
}
a {
text-decoration: underline;
}
a:hover {
text-decoration: none;
}
[data-kind="critical"] {
color: var(--cpd-color-text-critical-primary);
}
.nav-bar a {
color: var(--cpd-color-text-action-primary);
}
.nav-bar .active {
font-weight: var(--cpd-font-weight-semibold);
}
.nav-bar li:not(:first-child) {
list-style-type: disc;
color: var(--cpd-color-text-secondary);
}
hr {
border: 0;
height: 1px;
background: var(--cpd-color-gray-400);
}
.session-icon {
color: var(--cpd-color-icon-secondary);
background: var(--cpd-color-bg-subtle-secondary);
padding: var(--cpd-space-1x);
border-radius: 4px;
}

View File

@ -17,6 +17,7 @@ import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import eslint from "vite-plugin-eslint";
import codegen from "vite-plugin-graphql-codegen";
import svgr from "vite-plugin-svgr";
export default defineConfig({
base: "/app/",
@ -39,6 +40,18 @@ export default defineConfig({
// Explicitly set the config file, else storybook gets confused
overrideConfigFile: "./.eslintrc.cjs",
}),
svgr({
exportAsDefault: true,
svgrOptions: {
// Using 1em in order to make SVG size inherits from text size.
icon: "1em",
svgProps: {
// Adding a class in case we want to add global overrides, but one
// should probably stick to using CSS modules most of the time
class: "cpd-icon",
},
},
}),
],
server: {
proxy: {