Merge branch 'beta' of https://github.com/payloadcms/payload into fix-row-field-width
This commit is contained in:
@@ -22,3 +22,9 @@ fb7d1be2f3325d076b7c967b1730afcef37922c2
|
||||
|
||||
# 3.0 prettier & lint everywhere again
|
||||
83fd4c66222d7846eeb5cc332dfa99bf1e830831
|
||||
|
||||
# Upgrade to typescript-eslint v8, then prettier & lint everywhere
|
||||
86fdad0bb8ab27810599c8a32f3d8cba1341e1df
|
||||
|
||||
# Prettier and lint remaining db packages
|
||||
7fd736ea5b2e9fc4ef936e9dc9e5e3d722f6d8bf
|
||||
|
||||
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -17,7 +17,7 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
NODE_VERSION: 18.20.2
|
||||
NODE_VERSION: 22.6.0
|
||||
PNPM_VERSION: 9.7.1
|
||||
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
|
||||
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry
|
||||
|
||||
6
.github/workflows/pr-title.yml
vendored
6
.github/workflows/pr-title.yml
vendored
@@ -39,6 +39,7 @@ jobs:
|
||||
db-mongodb
|
||||
db-postgres
|
||||
db-sqlite
|
||||
drizzle
|
||||
email-nodemailer
|
||||
eslint
|
||||
graphql
|
||||
@@ -106,14 +107,15 @@ jobs:
|
||||
label-pr-on-open:
|
||||
name: label-pr-on-open
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.action == 'opened'
|
||||
steps:
|
||||
- name: Tag with main branch with v2
|
||||
if: github.event.action == 'opened' && github.event.pull_request.base.ref == 'main'
|
||||
if: github.event.pull_request.base.ref == 'main'
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: v2
|
||||
- name: Tag with beta branch with v3
|
||||
if: github.event.action == 'opened' && github.event.pull_request.base.ref == 'beta'
|
||||
if: github.event.pull_request.base.ref == 'beta'
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: v3
|
||||
|
||||
2
.github/workflows/release-canary.yml
vendored
2
.github/workflows/release-canary.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
- beta
|
||||
|
||||
env:
|
||||
NODE_VERSION: 18.20.2
|
||||
NODE_VERSION: 22.6.0
|
||||
PNPM_VERSION: 9.7.1
|
||||
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
|
||||
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry
|
||||
|
||||
@@ -1 +1 @@
|
||||
v18.20.2
|
||||
v22.6.0
|
||||
|
||||
2
.tool-versions
Normal file
2
.tool-versions
Normal file
@@ -0,0 +1,2 @@
|
||||
pnpm 9.7.1
|
||||
nodejs 22.6.0
|
||||
11
.vscode/settings.json
vendored
11
.vscode/settings.json
vendored
@@ -31,8 +31,15 @@
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"editor.formatOnSaveMode": "file",
|
||||
// All ESLint rules to 'warn' to differentate from TypeScript's 'error' level
|
||||
"eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }],
|
||||
"eslint.rules.customizations": [
|
||||
// Defaultt all ESLint errors to 'warn' to differentate from TypeScript's 'error' level
|
||||
{ "rule": "*", "severity": "warn" },
|
||||
|
||||
// Silence some warnings that will get auto-fixed
|
||||
{ "rule": "perfectionist/*", "severity": "off", "fixable": true },
|
||||
{ "rule": "curly", "severity": "off", "fixable": true },
|
||||
{ "rule": "object-shorthand", "severity": "off", "fixable": true }
|
||||
],
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
// Load .git-blame-ignore-revs file
|
||||
"gitlens.advanced.blame.customArguments": ["--ignore-revs-file", ".git-blame-ignore-revs"],
|
||||
|
||||
816
CHANGELOG.md
816
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
<a href="https://payloadcms.com"><img width="100%" src="https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/assets/images/github-banner-alt.jpg?raw=true" alt="Payload headless CMS Admin panel built with React" /></a>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<p align="left">
|
||||
<a href="https://github.com/payloadcms/payload/actions"><img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/payloadcms/payload/main.yml?style=flat-square"></a>
|
||||
|
||||
|
||||
@@ -431,14 +431,14 @@ export const MyClientComponent: React.FC = () => {
|
||||
See [Using Hooks](#using-hooks) for more details.
|
||||
</Banner>
|
||||
|
||||
All [Field Components](./fields) automatically receive their respective Client Field Config through a common [`field`](./fields#the-field-prop) prop:
|
||||
All [Field Components](./fields) automatically receive their respective Field Config through a common [`field`](./fields#the-field-prop) prop:
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import type { TextFieldProps } from 'payload'
|
||||
import type { TextFieldClientComponent } from 'payload'
|
||||
|
||||
export const MyClientFieldComponent: TextFieldProps = ({ field: { name } }) => {
|
||||
export const MyClientFieldComponent: TextFieldClientComponent = ({ field: { name } }) => {
|
||||
return (
|
||||
<p>
|
||||
{`This field's name is ${name}`}
|
||||
|
||||
@@ -136,7 +136,7 @@ All Field Components receive the following props:
|
||||
| Property | Description |
|
||||
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`docPreferences`** | An object that contains the [Preferences](./preferences) for the document.
|
||||
| **`field`** | The sanitized, client-friendly version of the field's config. [More details](#the-field-prop) |
|
||||
| **`field`** | The field's config. [More details](#the-field-prop) |
|
||||
| **`locale`** | The locale of the field. [More details](../configuration/localization). |
|
||||
| **`readOnly`** | A boolean value that represents if the field is read-only or not. |
|
||||
| **`user`** | The currently authenticated user. [More details](../authentication/overview). |
|
||||
@@ -175,46 +175,46 @@ export const CustomTextField: React.FC = () => {
|
||||
|
||||
#### TypeScript
|
||||
|
||||
When building Custom Field Components, you can import the component props to ensure type safety in your component. There is an explicit type for the Field Component, one for every [Field Type](../fields/overview). The convention is to append `Props` to the type of field, i.e. `TextFieldProps`.
|
||||
When building Custom Field Components, you can import the component type to ensure type safety. There is an explicit type for the Field Component, one for every [Field Type](../fields/overview) and for every client/server environment. The convention is to prepend the field type onto the target type, i.e. `TextFieldClientComponent`:
|
||||
|
||||
```tsx
|
||||
import type {
|
||||
ArrayFieldProps,
|
||||
BlocksFieldProps,
|
||||
CheckboxFieldProps,
|
||||
CodeFieldProps,
|
||||
CollapsibleFieldProps,
|
||||
DateFieldProps,
|
||||
EmailFieldProps,
|
||||
GroupFieldProps,
|
||||
HiddenFieldProps,
|
||||
JSONFieldProps,
|
||||
NumberFieldProps,
|
||||
PointFieldProps,
|
||||
RadioFieldProps,
|
||||
RelationshipFieldProps,
|
||||
RichTextFieldProps,
|
||||
RowFieldProps,
|
||||
SelectFieldProps,
|
||||
TabsFieldProps,
|
||||
TextFieldProps,
|
||||
TextareaFieldProps,
|
||||
UploadFieldProps
|
||||
TextFieldClientComponent,
|
||||
TextFieldServerComponent,
|
||||
TextFieldClientProps,
|
||||
TextFieldServerProps,
|
||||
// ...and so on for each Field Type
|
||||
} from 'payload'
|
||||
```
|
||||
|
||||
### The `field` Prop
|
||||
|
||||
All Field Components are passed a client-friendly version of their Field Config through a common `field` prop. Since the raw Field Config is [non-serializable](https://react.dev/reference/rsc/use-client#serializable-types), Payload sanitized it into a [Client Config](./components#accessing-the-payload-config) that is safe to pass into Client Components.
|
||||
All Field Components are passed their own Field Config through a common `field` prop. Within a Server Component, this is the raw Field Config. Within Client Components, however, the raw Field Config is [non-serializable](https://react.dev/reference/rsc/use-client#serializable-types). Instead, Client Components receives a [Client Config](./components#accessing-the-payload-config), which is a sanitizes version of the Field Config that is safe to pass into Client Components.
|
||||
|
||||
The exact shape of this prop is unique to the specific [Field Type](../fields/overview) being rendered, minus all non-serializable properties. Any [Custom Components](../components) are also resolved into a "mapped component" that is safe to pass.
|
||||
|
||||
```tsx
|
||||
import React from 'react'
|
||||
import type { TextFieldServerComponent } from 'payload'
|
||||
|
||||
export const MyServerTextField: TextFieldServerComponent = ({ payload, field: { name } }) => {
|
||||
const result = await payload.find({
|
||||
collection: 'myCollection',
|
||||
depth: 1,
|
||||
})
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Client Component:
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import type { TextFieldProps } from 'payload'
|
||||
import type { TextFieldClientComponent } from 'payload'
|
||||
|
||||
export const MyClientFieldComponent: React.FC<TextFieldProps> = ({ field: { name } }) => {
|
||||
export const MyClientTextField: TextFieldClientComponent = ({ field: { name } }) => {
|
||||
return (
|
||||
<p>
|
||||
{`This field's name is ${name}`}
|
||||
@@ -238,40 +238,18 @@ The following additional properties are also provided to the `field` prop:
|
||||
|
||||
#### TypeScript
|
||||
|
||||
When building Custom Field Components, you can import the client field props to ensure type safety in your component. There is an explicit type for the Field Component, one for every [Field Type](../fields/overview). The convention is to append `Client` to the type of field, i.e. `TextFieldClient`.
|
||||
When building Custom Field Components, you can import the client field props to ensure type safety in your component. There is an explicit type for the Field Component, one for every [Field Type](../fields/overview) and server/client environment. The convention is to prepend the field type onto the target type, i.e. `TextFieldClientComponent`:
|
||||
|
||||
```tsx
|
||||
import type {
|
||||
ArrayFieldClient,
|
||||
BlocksFieldClient,
|
||||
CheckboxFieldClient,
|
||||
CodeFieldClient,
|
||||
CollapsibleFieldClient,
|
||||
DateFieldClient,
|
||||
EmailFieldClient,
|
||||
GroupFieldClient,
|
||||
HiddenFieldClient,
|
||||
JSONFieldClient,
|
||||
NumberFieldClient,
|
||||
PointFieldClient,
|
||||
RadioFieldClient,
|
||||
RelationshipFieldClient,
|
||||
RichTextFieldClient,
|
||||
RowFieldClient,
|
||||
SelectFieldClient,
|
||||
TabsFieldClient,
|
||||
TextFieldClient,
|
||||
TextareaFieldClient,
|
||||
UploadFieldClient
|
||||
TextFieldClientComponent,
|
||||
TextFieldServerComponent,
|
||||
TextFieldClientProps,
|
||||
TextFieldServerProps,
|
||||
// ...and so on for each Field Type
|
||||
} from 'payload'
|
||||
```
|
||||
|
||||
When working on the client, you will never have access to objects of type `Field`. This is reserved for the server-side. Instead, you can use `ClientField` which is a union type of all the client fields:
|
||||
|
||||
```tsx
|
||||
import type { ClientField } from 'payload'
|
||||
```
|
||||
|
||||
### The Cell Component
|
||||
|
||||
The Cell Component is rendered in the table of the List View. It represents the value of the field when displayed in a table cell.
|
||||
@@ -353,7 +331,7 @@ When building Custom Label Components, you can import the component props to ens
|
||||
import type {
|
||||
TextFieldLabelServerComponent,
|
||||
TextFieldLabelClientComponent,
|
||||
// And so on for each Field Type
|
||||
// ...and so on for each Field Type
|
||||
} from 'payload'
|
||||
```
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ The following options are available:
|
||||
| **`lockTime`** | Set the time (in milliseconds) that a user should be locked out if they fail authentication more times than `maxLoginAttempts` allows for. |
|
||||
| **`loginWithUsername`** | Ability to allow users to login with username/password. [More](/docs/authentication/overview#login-with-username) |
|
||||
| **`maxLoginAttempts`** | Only allow a user to attempt logging in X amount of times. Automatically locks out a user from authenticating if this limit is passed. Set to `0` to disable. |
|
||||
| **`removeTokenFromResponses`** | Set to true if you want to remove the token from the returned authentication API responses such as login or refresh. |
|
||||
| **`strategies`** | Advanced - an array of custom authentification strategies to extend this collection's authentication with. [More details](./custom-strategies). |
|
||||
| **`tokenExpiration`** | How long (in seconds) to keep the user logged in. JWTs and HTTP-only cookies will both expire at the same time. |
|
||||
| **`useAPIKey`** | Payload Authentication provides for API keys to be set on each user within an Authentication-enabled Collection. [More details](./api-keys). |
|
||||
|
||||
@@ -71,9 +71,11 @@ The following options are available:
|
||||
| **`db`** \* | The Database Adapter which will be used by Payload. [More details](../database/overview). |
|
||||
| **`serverURL`** | A string used to define the absolute URL of your app. This includes the protocol, for example `https://example.com`. No paths allowed, only protocol, domain and (optionally) port. |
|
||||
| **`collections`** | An array of Collections for Payload to manage. [More details](./collections). |
|
||||
| **`compatibility`** | Compatibility flags for earlier versions of Payload. [More details](#compatibility-flags). |
|
||||
| **`globals`** | An array of Globals for Payload to manage. [More details](./globals). |
|
||||
| **`cors`** | Cross-origin resource sharing (CORS) is a mechanism that accept incoming requests from given domains. You can also customize the `Access-Control-Allow-Headers` header. [More details](#cors). |
|
||||
| **`localization`** | Opt-in to translate your content into multiple locales. [More details](./localization). |
|
||||
| **`logger`** | Logger options, logger options with a destination stream, or an instantiated logger instance. [More details](https://getpino.io/#/docs/api?id=options). |
|
||||
| **`graphQL`** | Manage GraphQL-specific functionality, including custom queries and mutations, query complexity limits, etc. [More details](../graphql/overview#graphql-options). |
|
||||
| **`cookiePrefix`** | A string that will be prefixed to all cookies that Payload sets. |
|
||||
| **`csrf`** | A whitelist array of URLs to allow Payload to accept cookies from. [More details](../authentication/overview#csrf-protection). |
|
||||
@@ -253,3 +255,13 @@ import type { Config, SanitizedConfig } from 'payload'
|
||||
The Payload Config only lives on the server and is not allowed to contain any client-side code. That way, you can load up the Payload Config in any server environment or standalone script, without having to use Bundlers or Node.js loaders to handle importing client-only modules (e.g. scss files or React Components) without any errors.
|
||||
|
||||
Behind the curtains, the Next.js-based Admin Panel generates a ClientConfig, which strips away any server-only code and enriches the config with React Components.
|
||||
|
||||
## Compatibility flags
|
||||
|
||||
The Payload Config can accept compatibility flags for running the newest versions but with older databases. You should only use these flags if you need to, and should confirm that you need to prior to enabling these flags.
|
||||
|
||||
`allowLocalizedWithinLocalized`
|
||||
|
||||
Payload localization works on a field-by-field basis. As you can nest fields within other fields, you could potentially nest a localized field within a localized field—but this would be redundant and unnecessary. There would be no reason to define a localized field within a localized parent field, given that the entire data structure from the parent field onward would be localized.
|
||||
|
||||
By default, Payload will remove the `localized: true` property from sub-fields if a parent field is localized. Set this compatibility flag to `true` only if you have an existing Payload MongoDB database from pre-3.0, and you have nested localized fields that you would like to maintain without migrating.
|
||||
|
||||
@@ -57,7 +57,6 @@ export default buildConfig({
|
||||
| `pool` \* | [Pool connection options](https://orm.drizzle.team/docs/quick-postgresql/node-postgres) that will be passed to Drizzle and `node-postgres` or to `@vercel/postgres` |
|
||||
| `push` | Disable Drizzle's [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push) in development mode. By default, `push` is enabled for development mode only. |
|
||||
| `migrationDir` | Customize the directory that migrations are stored. |
|
||||
| `logger` | The instance of the logger to be passed to drizzle. By default Payload's will be used. |
|
||||
| `schemaName` (experimental) | A string for the postgres schema to use, defaults to 'public'. |
|
||||
| `idType` | A string of 'serial', or 'uuid' that is used for the data type given to id columns. |
|
||||
| `transactionOptions` | A PgTransactionConfig object for transactions, or set to `false` to disable using transactions. [More details](https://orm.drizzle.team/docs/transactions) |
|
||||
|
||||
@@ -25,13 +25,12 @@ export const defaultESLintIgnores = [
|
||||
let FlatConfig
|
||||
|
||||
export const rootParserOptions = {
|
||||
EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true,
|
||||
EXPERIMENTAL_useProjectService: {
|
||||
allowDefaultProjectForFiles: ['./src/*.ts', './src/*.tsx'],
|
||||
maximumDefaultProjectFileMatchCount_THIS_WILL_SLOW_DOWN_LINTING: 100,
|
||||
},
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 'latest',
|
||||
projectService: {
|
||||
maximumDefaultProjectFileMatchCount_THIS_WILL_SLOW_DOWN_LINTING: 40,
|
||||
allowDefaultProject: ['scripts/*.ts', '*.js', '*.mjs', '*.spec.ts', '*.d.ts'],
|
||||
},
|
||||
}
|
||||
|
||||
/** @type {FlatConfig[]} */
|
||||
@@ -40,20 +39,12 @@ export const rootEslintConfig = [
|
||||
{
|
||||
ignores: [
|
||||
...defaultESLintIgnores,
|
||||
'packages/eslint-*/**',
|
||||
'test/live-preview/next-app',
|
||||
'packages/**/*.spec.ts',
|
||||
'templates/**',
|
||||
],
|
||||
},
|
||||
{
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
tsconfigDirName: import.meta.dirname,
|
||||
...rootParserOptions,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
plugins: {
|
||||
payload: payloadPlugin,
|
||||
@@ -78,6 +69,15 @@ export const rootEslintConfig = [
|
||||
|
||||
export default [
|
||||
...rootEslintConfig,
|
||||
{
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
...rootParserOptions,
|
||||
projectService: true,
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['packages/eslint-config/**/*.ts'],
|
||||
rules: {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
:global([data-theme="light"]) {
|
||||
:global([data-theme='light']) {
|
||||
.logo {
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
@@ -10,12 +10,12 @@ export const RenderParams: React.FC<{
|
||||
className?: string
|
||||
}> = ({ params = ['error', 'message', 'success'], message, className }) => {
|
||||
const searchParams = useSearchParams()
|
||||
const paramValues = params.map(param => searchParams.get(param)).filter(Boolean)
|
||||
const paramValues = params.map((param) => searchParams.get(param)).filter(Boolean)
|
||||
|
||||
if (paramValues.length) {
|
||||
return (
|
||||
<div className={className}>
|
||||
{paramValues.map(paramValue => (
|
||||
{paramValues.map((paramValue) => (
|
||||
<Message
|
||||
key={paramValue}
|
||||
message={(message || 'PARAM')?.replace('PARAM', paramValue || '')}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@use './queries.scss' as *;
|
||||
@use './colors.scss' as *;
|
||||
@use './type.scss' as *;
|
||||
@import "./theme.scss";
|
||||
@import './theme.scss';
|
||||
|
||||
:root {
|
||||
--base: 24px;
|
||||
@@ -88,7 +88,7 @@ p {
|
||||
margin: var(--base) 0;
|
||||
|
||||
@include mid-break {
|
||||
margin: calc(var(--base) * .75) 0;
|
||||
margin: calc(var(--base) * 0.75) 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,12 +102,12 @@ a {
|
||||
color: currentColor;
|
||||
|
||||
&:focus {
|
||||
opacity: .8;
|
||||
opacity: 0.8;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: .7;
|
||||
opacity: 0.7;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode; api?: 'rest' |
|
||||
const [user, setUser] = useState<User | null>()
|
||||
|
||||
const create = useCallback<Create>(
|
||||
async args => {
|
||||
async (args) => {
|
||||
if (api === 'rest') {
|
||||
const user = await rest(`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users`, args)
|
||||
setUser(user)
|
||||
@@ -38,7 +38,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode; api?: 'rest' |
|
||||
)
|
||||
|
||||
const login = useCallback<Login>(
|
||||
async args => {
|
||||
async (args) => {
|
||||
if (api === 'rest') {
|
||||
const user = await rest(`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users/login`, args)
|
||||
setUser(user)
|
||||
@@ -110,7 +110,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode; api?: 'rest' |
|
||||
}, [api])
|
||||
|
||||
const forgotPassword = useCallback<ForgotPassword>(
|
||||
async args => {
|
||||
async (args) => {
|
||||
if (api === 'rest') {
|
||||
const user = await rest(
|
||||
`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users/forgot-password`,
|
||||
@@ -132,7 +132,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode; api?: 'rest' |
|
||||
)
|
||||
|
||||
const resetPassword = useCallback<ResetPassword>(
|
||||
async args => {
|
||||
async (args) => {
|
||||
if (api === 'rest') {
|
||||
const user = await rest(
|
||||
`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users/reset-password`,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../_css/common";
|
||||
@import '../../_css/common';
|
||||
|
||||
.form {
|
||||
margin-bottom: var(--base);
|
||||
|
||||
@@ -139,7 +139,7 @@ export const AccountForm: React.FC = () => {
|
||||
label="Confirm Password"
|
||||
required
|
||||
register={register}
|
||||
validate={value => value === password.current || 'The passwords do not match'}
|
||||
validate={(value) => value === password.current || 'The passwords do not match'}
|
||||
error={errors.passwordConfirm}
|
||||
/>
|
||||
</Fragment>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../_css/common";
|
||||
@import '../../_css/common';
|
||||
|
||||
.form {
|
||||
margin-bottom: var(--base);
|
||||
|
||||
@@ -103,7 +103,7 @@ export const CreateAccountForm: React.FC = () => {
|
||||
label="Confirm Password"
|
||||
required
|
||||
register={register}
|
||||
validate={value => value === password.current || 'The passwords do not match'}
|
||||
validate={(value) => value === password.current || 'The passwords do not match'}
|
||||
error={errors.passwordConfirm}
|
||||
/>
|
||||
<Button
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../_css/common";
|
||||
@import '../_css/common';
|
||||
|
||||
.createAccount {
|
||||
margin-bottom: var(--block-padding);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../_css/common";
|
||||
@import '../../_css/common';
|
||||
|
||||
.form {
|
||||
margin-bottom: var(--base);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../_css/common";
|
||||
@import '../_css/common';
|
||||
|
||||
.login {
|
||||
margin-bottom: var(--block-padding);
|
||||
|
||||
@@ -5,7 +5,7 @@ import Link from 'next/link'
|
||||
|
||||
import { useAuth } from '../../_providers/Auth'
|
||||
|
||||
export const LogoutPage: React.FC = props => {
|
||||
export const LogoutPage: React.FC = (props) => {
|
||||
const { logout } = useAuth()
|
||||
const [success, setSuccess] = useState('')
|
||||
const [error, setError] = useState('')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../_css/common";
|
||||
@import '../../_css/common';
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
@@ -20,4 +20,3 @@
|
||||
.message {
|
||||
margin-bottom: var(--base);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../_css/common";
|
||||
@import '../_css/common';
|
||||
|
||||
.recoverPassword {
|
||||
margin-bottom: var(--block-padding);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../_css/common";
|
||||
@import '../../_css/common';
|
||||
|
||||
.form {
|
||||
width: 66.66%;
|
||||
|
||||
2097
examples/auth/next-app/pnpm-lock.yaml
generated
2097
examples/auth/next-app/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
1799
examples/auth/next-pages/pnpm-lock.yaml
generated
1799
examples/auth/next-pages/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
:global([data-theme="light"]) {
|
||||
:global([data-theme='light']) {
|
||||
.logo {
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@ export const RenderParams: React.FC<{
|
||||
}> = ({ params = ['error', 'message', 'success'], message, className }) => {
|
||||
const router = useRouter()
|
||||
const searchParams = new URLSearchParams(router.query as any)
|
||||
const paramValues = params.map(param => searchParams.get(param)).filter(Boolean)
|
||||
const paramValues = params.map((param) => searchParams.get(param)).filter(Boolean)
|
||||
|
||||
if (paramValues.length) {
|
||||
return (
|
||||
<div className={className}>
|
||||
{paramValues.map(paramValue => (
|
||||
{paramValues.map((paramValue) => (
|
||||
<Message key={paramValue} message={(message || 'PARAM')?.replace('PARAM', paramValue)} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@use './queries.scss' as *;
|
||||
@use './colors.scss' as *;
|
||||
@use './type.scss' as *;
|
||||
@import "./theme.scss";
|
||||
@import './theme.scss';
|
||||
|
||||
:root {
|
||||
--base: 24px;
|
||||
@@ -88,7 +88,7 @@ p {
|
||||
margin: var(--base) 0;
|
||||
|
||||
@include mid-break {
|
||||
margin: calc(var(--base) * .75) 0;
|
||||
margin: calc(var(--base) * 0.75) 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,12 +102,12 @@ a {
|
||||
color: currentColor;
|
||||
|
||||
&:focus {
|
||||
opacity: .8;
|
||||
opacity: 0.8;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: .7;
|
||||
opacity: 0.7;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../css/common";
|
||||
@import '../../css/common';
|
||||
|
||||
.account {
|
||||
margin-bottom: var(--block-padding);
|
||||
|
||||
@@ -150,7 +150,7 @@ const Account: React.FC = () => {
|
||||
label="Confirm Password"
|
||||
required
|
||||
register={register}
|
||||
validate={value => value === password.current || 'The passwords do not match'}
|
||||
validate={(value) => value === password.current || 'The passwords do not match'}
|
||||
error={errors.passwordConfirm}
|
||||
/>
|
||||
</Fragment>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../css/common";
|
||||
@import '../../css/common';
|
||||
|
||||
.createAccount {
|
||||
margin-bottom: var(--block-padding);
|
||||
|
||||
@@ -106,7 +106,7 @@ const CreateAccount: React.FC = () => {
|
||||
label="Confirm Password"
|
||||
required
|
||||
register={register}
|
||||
validate={value => value === password.current || 'The passwords do not match'}
|
||||
validate={(value) => value === password.current || 'The passwords do not match'}
|
||||
error={errors.passwordConfirm}
|
||||
/>
|
||||
<Button
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../css/common";
|
||||
@import '../../css/common';
|
||||
|
||||
.login {
|
||||
margin-bottom: var(--block-padding);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../css/common";
|
||||
@import '../../css/common';
|
||||
|
||||
.recoverPassword {
|
||||
margin-bottom: var(--block-padding);
|
||||
@@ -24,4 +24,3 @@
|
||||
.message {
|
||||
margin-bottom: var(--base);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../css/common";
|
||||
@import '../../css/common';
|
||||
|
||||
.resetPassword {
|
||||
margin-bottom: var(--block-padding);
|
||||
|
||||
@@ -14,7 +14,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode; api?: 'rest' |
|
||||
const [user, setUser] = useState<User | null>()
|
||||
|
||||
const create = useCallback<Create>(
|
||||
async args => {
|
||||
async (args) => {
|
||||
if (api === 'rest') {
|
||||
const user = await rest(`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users`, args)
|
||||
setUser(user)
|
||||
@@ -36,7 +36,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode; api?: 'rest' |
|
||||
)
|
||||
|
||||
const login = useCallback<Login>(
|
||||
async args => {
|
||||
async (args) => {
|
||||
if (api === 'rest') {
|
||||
const user = await rest(`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users/login`, args)
|
||||
setUser(user)
|
||||
@@ -108,7 +108,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode; api?: 'rest' |
|
||||
}, [api])
|
||||
|
||||
const forgotPassword = useCallback<ForgotPassword>(
|
||||
async args => {
|
||||
async (args) => {
|
||||
if (api === 'rest') {
|
||||
const user = await rest(
|
||||
`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users/forgot-password`,
|
||||
@@ -130,7 +130,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode; api?: 'rest' |
|
||||
)
|
||||
|
||||
const resetPassword = useCallback<ResetPassword>(
|
||||
async args => {
|
||||
async (args) => {
|
||||
if (api === 'rest') {
|
||||
const user = await rest(
|
||||
`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users/reset-password`,
|
||||
|
||||
4636
examples/auth/payload/pnpm-lock.yaml
generated
4636
examples/auth/payload/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import type { ElementType } from 'react';
|
||||
import type { ElementType } from 'react'
|
||||
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Ref } from 'react';
|
||||
import type { Ref } from 'react'
|
||||
|
||||
import React, { forwardRef } from 'react'
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
:global([data-theme="light"]) {
|
||||
:global([data-theme='light']) {
|
||||
.logo {
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
@@ -10,12 +10,12 @@ export const RenderParams: React.FC<{
|
||||
params?: string[]
|
||||
}> = ({ className, message, params = ['error', 'message', 'success'] }) => {
|
||||
const searchParams = useSearchParams()
|
||||
const paramValues = params.map(param => searchParams.get(param)).filter(Boolean)
|
||||
const paramValues = params.map((param) => searchParams.get(param)).filter(Boolean)
|
||||
|
||||
if (paramValues.length) {
|
||||
return (
|
||||
<div className={className}>
|
||||
{paramValues.map(paramValue => (
|
||||
{paramValues.map((paramValue) => (
|
||||
<Message
|
||||
key={paramValue}
|
||||
message={(message || 'PARAM')?.replace('PARAM', paramValue || '')}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../_css/common";
|
||||
@import '../../_css/common';
|
||||
|
||||
.form {
|
||||
margin-bottom: var(--base);
|
||||
|
||||
@@ -136,7 +136,7 @@ export const AccountForm: React.FC = () => {
|
||||
register={register}
|
||||
required
|
||||
type="password"
|
||||
validate={value => value === password.current || 'The passwords do not match'}
|
||||
validate={(value) => value === password.current || 'The passwords do not match'}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../_css/common";
|
||||
@import '../../_css/common';
|
||||
|
||||
.form {
|
||||
margin-bottom: var(--base);
|
||||
|
||||
@@ -103,7 +103,7 @@ export const CreateAccountForm: React.FC = () => {
|
||||
register={register}
|
||||
required
|
||||
type="password"
|
||||
validate={value => value === password.current || 'The passwords do not match'}
|
||||
validate={(value) => value === password.current || 'The passwords do not match'}
|
||||
/>
|
||||
<Button
|
||||
appearance="primary"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../_css/common";
|
||||
@import '../_css/common';
|
||||
|
||||
.createAccount {
|
||||
margin-bottom: var(--block-padding);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../_css/common";
|
||||
@import '../../_css/common';
|
||||
|
||||
.form {
|
||||
margin-bottom: var(--base);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../_css/common";
|
||||
@import '../_css/common';
|
||||
|
||||
.login {
|
||||
margin-bottom: var(--block-padding);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../_css/common";
|
||||
@import '../../_css/common';
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
@@ -20,4 +20,3 @@
|
||||
.message {
|
||||
margin-bottom: var(--base);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../_css/common";
|
||||
@import '../_css/common';
|
||||
|
||||
.recoverPassword {
|
||||
margin-bottom: var(--block-padding);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "../../_css/common";
|
||||
@import '../../_css/common';
|
||||
|
||||
.form {
|
||||
width: 66.66%;
|
||||
|
||||
@@ -3,8 +3,8 @@ import type { User } from '../../payload-types'
|
||||
export const checkRole = (allRoles: User['roles'] = [], user: User = undefined): boolean => {
|
||||
if (user) {
|
||||
if (
|
||||
allRoles.some(role => {
|
||||
return user?.roles?.some(individualRole => {
|
||||
allRoles.some((role) => {
|
||||
return user?.roles?.some((individualRole) => {
|
||||
return individualRole === role
|
||||
})
|
||||
})
|
||||
|
||||
@@ -100,10 +100,10 @@ On boot, a seed script is included to scaffold a basic database for you to use a
|
||||
|
||||
### Conflicting routes
|
||||
|
||||
>In a monorepo when routes are bootstrapped to the same host, they can conflict with Payload's own routes if they have the same name. In our template we've named the Nextjs API routes to `next` to avoid this conflict.
|
||||
> In a monorepo when routes are bootstrapped to the same host, they can conflict with Payload's own routes if they have the same name. In our template we've named the Nextjs API routes to `next` to avoid this conflict.
|
||||
>
|
||||
>This can happen with any other routes conflicting with Payload such as `admin` and we recommend using different names for custom routes.
|
||||
>Alternatively you can also rename Payload's own routes via the [configuration](https://payloadcms.com/docs/configuration/overview).
|
||||
> This can happen with any other routes conflicting with Payload such as `admin` and we recommend using different names for custom routes.
|
||||
> Alternatively you can also rename Payload's own routes via the [configuration](https://payloadcms.com/docs/configuration/overview).
|
||||
|
||||
## Production
|
||||
|
||||
|
||||
@@ -10,12 +10,12 @@ const files = ['./next.config.js', './next-env.d.ts']
|
||||
const directories = ['./src/app']
|
||||
|
||||
const eject = async (): Promise<void> => {
|
||||
files.forEach(file => {
|
||||
files.forEach((file) => {
|
||||
fs.unlinkSync(path.join(__dirname, file))
|
||||
})
|
||||
|
||||
directories.forEach(directory => {
|
||||
fs.rm(path.join(__dirname, directory), { recursive: true }, err => {
|
||||
directories.forEach((directory) => {
|
||||
fs.rm(path.join(__dirname, directory), { recursive: true }, (err) => {
|
||||
if (err) throw err
|
||||
})
|
||||
})
|
||||
|
||||
@@ -29,7 +29,18 @@ $breakpoint: 1000px;
|
||||
html {
|
||||
font-size: 20px;
|
||||
line-height: 1.5;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Open Sans',
|
||||
'Helvetica Neue',
|
||||
sans-serif;
|
||||
|
||||
@media (max-width: $breakpoint) {
|
||||
font-size: 16px;
|
||||
|
||||
@@ -20,7 +20,7 @@ const start = async (): Promise<void> => {
|
||||
const payload = await getPayloadClient({
|
||||
initOptions: {
|
||||
express: app,
|
||||
onInit: async newPayload => {
|
||||
onInit: async (newPayload) => {
|
||||
newPayload.logger.info(`Payload Admin URL: ${newPayload.getAdminURL()}`)
|
||||
},
|
||||
},
|
||||
|
||||
@@ -18,7 +18,7 @@ const start = async (): Promise<void> => {
|
||||
const payload = await getPayloadClient({
|
||||
initOptions: {
|
||||
express: app,
|
||||
onInit: async newPayload => {
|
||||
onInit: async (newPayload) => {
|
||||
newPayload.logger.info(`Payload Admin URL: ${newPayload.getAdminURL()}`)
|
||||
},
|
||||
},
|
||||
|
||||
@@ -4,10 +4,7 @@
|
||||
"module": "commonjs",
|
||||
"outDir": "dist",
|
||||
"noEmit": false,
|
||||
"jsx": "react",
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": [
|
||||
"src/server.ts",
|
||||
"src/payload.config.ts",
|
||||
]
|
||||
"include": ["src/server.ts", "src/payload.config.ts"]
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export const fetchPage = async (
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
).then(res => res.json())
|
||||
).then((res) => res.json())
|
||||
|
||||
return pageRes?.docs?.[0] ?? null
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ import type { Page } from '../../payload-types'
|
||||
export const fetchPages = async (): Promise<Page[]> => {
|
||||
const pageRes: {
|
||||
docs: Page[]
|
||||
} = await fetch(`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/pages?depth=0&limit=100`).then(res =>
|
||||
res.json(),
|
||||
} = await fetch(`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/pages?depth=0&limit=100`).then(
|
||||
(res) => res.json(),
|
||||
) // eslint-disable-line function-paren-newline
|
||||
|
||||
return pageRes?.docs ?? []
|
||||
|
||||
@@ -9,7 +9,7 @@ import classes from './index.module.scss'
|
||||
|
||||
const Title: React.FC = () => <span>Dashboard</span>
|
||||
|
||||
export const AdminBarClient: React.FC<PayloadAdminBarProps> = props => {
|
||||
export const AdminBarClient: React.FC<PayloadAdminBarProps> = (props) => {
|
||||
const [user, setUser] = useState<PayloadMeUser>()
|
||||
|
||||
return (
|
||||
|
||||
@@ -11,7 +11,7 @@ import classes from './index.module.scss'
|
||||
export async function Header() {
|
||||
const mainMenu: MainMenu = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/globals/main-menu`,
|
||||
).then(res => res.json())
|
||||
).then((res) => res.json())
|
||||
|
||||
const { navItems } = mainMenu
|
||||
|
||||
|
||||
@@ -29,7 +29,18 @@ $breakpoint: 1000px;
|
||||
html {
|
||||
font-size: 20px;
|
||||
line-height: 1.5;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Open Sans',
|
||||
'Helvetica Neue',
|
||||
sans-serif;
|
||||
|
||||
@media (max-width: $breakpoint) {
|
||||
font-size: 16px;
|
||||
|
||||
@@ -11,7 +11,7 @@ export const AdminBar: React.FC<{
|
||||
adminBarProps?: PayloadAdminBarProps
|
||||
user?: PayloadMeUser
|
||||
setUser?: (user: PayloadMeUser) => void // eslint-disable-line no-unused-vars
|
||||
}> = props => {
|
||||
}> = (props) => {
|
||||
const { adminBarProps, user, setUser } = props
|
||||
|
||||
return (
|
||||
|
||||
@@ -43,7 +43,7 @@ export const Header: React.FC<{
|
||||
mainMenu: MainMenu
|
||||
}
|
||||
adminBarProps: PayloadAdminBarProps
|
||||
}> = props => {
|
||||
}> = (props) => {
|
||||
const { globals, adminBarProps } = props
|
||||
|
||||
const [user, setUser] = useState<PayloadMeUser>()
|
||||
|
||||
@@ -14,7 +14,7 @@ const Page: React.FC<
|
||||
mainMenu: MainMenu
|
||||
preview?: boolean
|
||||
}
|
||||
> = props => {
|
||||
> = (props) => {
|
||||
const { title, richText } = props
|
||||
|
||||
return (
|
||||
@@ -119,7 +119,7 @@ export const getStaticPaths: GetStaticPaths = async () => {
|
||||
const { docs: pages } = pagesData
|
||||
|
||||
if (pages && Array.isArray(pages) && pages.length > 0) {
|
||||
paths = pages.map(page => ({ params: { slug: page.slug } }))
|
||||
paths = pages.map((page) => ({ params: { slug: page.slug } }))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,18 @@ $breakpoint: 1000px;
|
||||
html {
|
||||
font-size: 20px;
|
||||
line-height: 1.5;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Open Sans',
|
||||
'Helvetica Neue',
|
||||
sans-serif;
|
||||
|
||||
@media (max-width: $breakpoint) {
|
||||
font-size: 16px;
|
||||
@@ -136,7 +147,18 @@ $breakpoint: 1000px;
|
||||
html {
|
||||
font-size: 20px;
|
||||
line-height: 1.5;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Open Sans',
|
||||
'Helvetica Neue',
|
||||
sans-serif;
|
||||
|
||||
@media (max-width: $breakpoint) {
|
||||
font-size: 16px;
|
||||
|
||||
@@ -4,7 +4,7 @@ import Page, { getStaticProps as sharedGetStaticProps } from './[slug]'
|
||||
|
||||
export default Page
|
||||
|
||||
export const getStaticProps: GetStaticProps = async ctx => {
|
||||
export const getStaticProps: GetStaticProps = async (ctx) => {
|
||||
const func = sharedGetStaticProps.bind(this)
|
||||
return func(ctx)
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
"preLaunchTask": "npm: build:server",
|
||||
"env": {
|
||||
"PAYLOAD_CONFIG_PATH": "${workspaceFolder}/src/payload.config.ts"
|
||||
},
|
||||
}
|
||||
// "outFiles": [
|
||||
// "${workspaceFolder}/dist/**/*.js"
|
||||
// ]
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export const Pages: CollectionConfig = {
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
defaultColumns: ['title', 'slug', 'updatedAt'],
|
||||
preview: doc => {
|
||||
preview: (doc) => {
|
||||
return `${process.env.PAYLOAD_PUBLIC_SITE_URL}/api/preview?url=${encodeURIComponent(
|
||||
formatAppURL({
|
||||
doc,
|
||||
|
||||
@@ -125,7 +125,7 @@ const link: LinkType = ({ appearances, disableLabel = false, overrides = {} } =
|
||||
]
|
||||
|
||||
if (appearances) {
|
||||
appearanceOptionsToUse = appearances.map(appearance => appearanceOptions[appearance])
|
||||
appearanceOptionsToUse = appearances.map((appearance) => appearanceOptions[appearance])
|
||||
}
|
||||
|
||||
linkResult.fields.push({
|
||||
|
||||
@@ -17,7 +17,7 @@ export function isObject(item: unknown): boolean {
|
||||
export default function deepMerge<T, R>(target: T, source: R): T {
|
||||
const output = { ...target }
|
||||
if (isObject(target) && isObject(source)) {
|
||||
Object.keys(source).forEach(key => {
|
||||
Object.keys(source).forEach((key) => {
|
||||
if (isObject(source[key])) {
|
||||
if (!(key in target)) {
|
||||
Object.assign(output, { [key]: source[key] })
|
||||
|
||||
@@ -32,7 +32,7 @@ const Newsletter: CollectionConfig = {
|
||||
name: 'email',
|
||||
type: 'text',
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@ import generateEmailHTML from './generateEmailHTML'
|
||||
const generateForgotPasswordEmail = async ({ token }): Promise<string> =>
|
||||
generateEmailHTML({
|
||||
headline: 'Locked out?',
|
||||
content:
|
||||
'<p>Let's get you back in.</p>',
|
||||
content: '<p>Let's get you back in.</p>',
|
||||
cta: {
|
||||
buttonLabel: 'Reset your password',
|
||||
url: `${process.env.PAYLOAD_PUBLIC_SERVER_URL}/reset-password?token=${token}`,
|
||||
|
||||
@@ -1,317 +1,345 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<style type="text/css">
|
||||
body,
|
||||
html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style type="text/css">
|
||||
body,
|
||||
html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body,
|
||||
html,
|
||||
.bg {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body,
|
||||
html,
|
||||
.bg {
|
||||
height: 100%;
|
||||
}
|
||||
body,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
p,
|
||||
em,
|
||||
strong {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
body,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
p,
|
||||
em,
|
||||
strong {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
body {
|
||||
font-size: 15px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 15px;
|
||||
color: #333333;
|
||||
}
|
||||
a {
|
||||
color: #333333;
|
||||
outline: 0;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #333333;
|
||||
outline: 0;
|
||||
text-decoration: underline;
|
||||
}
|
||||
a img {
|
||||
border: 0;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
a img {
|
||||
border: 0;
|
||||
outline: 0;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5 {
|
||||
font-weight: 900;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 40px;
|
||||
color: #333333;
|
||||
margin: 0 0 25px 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #333333;
|
||||
margin: 0 0 25px 0;
|
||||
font-size: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 25px;
|
||||
color: #333333;
|
||||
margin: 0 0 25px 0;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 20px;
|
||||
color: #333333;
|
||||
margin: 0 0 15px 0;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
color: #333333;
|
||||
font-size: 17px;
|
||||
font-weight: 900;
|
||||
margin: 0 0 15px;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
p,
|
||||
td {
|
||||
font-size: 14px;
|
||||
line-height: 25px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 25px;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 15px;
|
||||
margin-left: 15px;
|
||||
font-size: 14px;
|
||||
line-height: 25px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
li {
|
||||
font-size: 14px;
|
||||
line-height: 25px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
table.hr td {
|
||||
font-size: 0;
|
||||
line-height: 2px;
|
||||
}
|
||||
|
||||
.white {
|
||||
color: white;
|
||||
}
|
||||
|
||||
/********************************
|
||||
MAIN
|
||||
********************************/
|
||||
|
||||
.main {
|
||||
background: white;
|
||||
}
|
||||
|
||||
/********************************
|
||||
MAX WIDTHS
|
||||
********************************/
|
||||
|
||||
.max-width {
|
||||
max-width: 800px;
|
||||
width: 94%;
|
||||
margin: 0 3%;
|
||||
}
|
||||
|
||||
/********************************
|
||||
REUSABLES
|
||||
********************************/
|
||||
|
||||
.padding {
|
||||
padding: 60px;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.no-border {
|
||||
border: 0;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.no-margin {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
line-height: 45px;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
/********************************
|
||||
PANELS
|
||||
********************************/
|
||||
|
||||
.panel {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media screen and (max-width : 800px) {
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5 {
|
||||
font-weight: 900;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px !important;
|
||||
margin: 0 0 20px 0 !important;
|
||||
font-size: 40px;
|
||||
color: #333333;
|
||||
margin: 0 0 25px 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 20px !important;
|
||||
margin: 0 0 20px 0 !important;
|
||||
color: #333333;
|
||||
margin: 0 0 25px 0;
|
||||
font-size: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 20px !important;
|
||||
margin: 0 0 20px 0 !important;
|
||||
font-size: 25px;
|
||||
color: #333333;
|
||||
margin: 0 0 25px 0;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 18px !important;
|
||||
margin: 0 0 15px 0 !important;
|
||||
font-size: 20px;
|
||||
color: #333333;
|
||||
margin: 0 0 15px 0;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 15px !important;
|
||||
margin: 0 0 10px !important;
|
||||
color: #333333;
|
||||
font-size: 17px;
|
||||
font-weight: 900;
|
||||
margin: 0 0 15px;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
p,
|
||||
td {
|
||||
font-size: 14px;
|
||||
line-height: 25px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 25px;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 15px;
|
||||
margin-left: 15px;
|
||||
font-size: 14px;
|
||||
line-height: 25px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
li {
|
||||
font-size: 14px;
|
||||
line-height: 25px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
table.hr td {
|
||||
font-size: 0;
|
||||
line-height: 2px;
|
||||
}
|
||||
|
||||
.white {
|
||||
color: white;
|
||||
}
|
||||
|
||||
/********************************
|
||||
MAIN
|
||||
********************************/
|
||||
|
||||
.main {
|
||||
background: white;
|
||||
}
|
||||
|
||||
/********************************
|
||||
MAX WIDTHS
|
||||
********************************/
|
||||
|
||||
.max-width {
|
||||
width: 90% !important;
|
||||
margin: 0 5% !important;
|
||||
max-width: 800px;
|
||||
width: 94%;
|
||||
margin: 0 3%;
|
||||
}
|
||||
|
||||
td.padding {
|
||||
padding: 30px !important;
|
||||
/********************************
|
||||
REUSABLES
|
||||
********************************/
|
||||
|
||||
.padding {
|
||||
padding: 60px;
|
||||
}
|
||||
|
||||
td.padding-vert {
|
||||
padding-top: 20px !important;
|
||||
padding-bottom: 20px !important;
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
td.padding-horiz {
|
||||
padding-left: 20px !important;
|
||||
padding-right: 20px !important;
|
||||
.no-border {
|
||||
border: 0;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.no-margin {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
line-height: 20px !important;
|
||||
height: 20px !important;
|
||||
line-height: 45px;
|
||||
height: 45px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="background-color:#F3F3F3; height: 100%;">
|
||||
<table height="100%" width="100%" cellpadding="0" cellspacing="0" border="0" bgcolor="#f3f3f3"
|
||||
style="background-color: #f3f3f3;">
|
||||
<tr>
|
||||
<td valign="top" align="left">
|
||||
<table cellpadding="0" cellspacing="0" border="0" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="top">
|
||||
<table cellpadding="0" cellspacing="0" border="0" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table class="max-width" cellpadding="0" cellspacing="0" border="0" width="100%"
|
||||
style="width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="spacer"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="padding main">
|
||||
<table cellpadding="0" cellspacing="0" border="0" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<!-- LOGO -->
|
||||
<a href="https://payloadcms.com/" target="_blank">
|
||||
<img src="https://payloadcms.com/images/logo-dark.png" width="150"
|
||||
height="auto" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="spacer"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<!-- HEADLINE -->
|
||||
<h1 style="margin: 0 0 30px;">{{headline}}</h1>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<!-- CONTENT -->
|
||||
{{{content}}}
|
||||
/********************************
|
||||
PANELS
|
||||
********************************/
|
||||
|
||||
<!-- CTA -->
|
||||
{{#if cta}}
|
||||
<div>
|
||||
<a href="{{cta.url}}"
|
||||
style="background-color:#222222;border-radius:4px;color:#ffffff;display:inline-block;font-family:sans-serif;font-size:13px;font-weight:bold;line-height:60px;text-align:center;text-decoration:none;width:200px;-webkit-text-size-adjust:none;">
|
||||
{{cta.buttonLabel}}
|
||||
.panel {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
h1 {
|
||||
font-size: 24px !important;
|
||||
margin: 0 0 20px 0 !important;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 20px !important;
|
||||
margin: 0 0 20px 0 !important;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 20px !important;
|
||||
margin: 0 0 20px 0 !important;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 18px !important;
|
||||
margin: 0 0 15px 0 !important;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 15px !important;
|
||||
margin: 0 0 10px !important;
|
||||
}
|
||||
|
||||
.max-width {
|
||||
width: 90% !important;
|
||||
margin: 0 5% !important;
|
||||
}
|
||||
|
||||
td.padding {
|
||||
padding: 30px !important;
|
||||
}
|
||||
|
||||
td.padding-vert {
|
||||
padding-top: 20px !important;
|
||||
padding-bottom: 20px !important;
|
||||
}
|
||||
|
||||
td.padding-horiz {
|
||||
padding-left: 20px !important;
|
||||
padding-right: 20px !important;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
line-height: 20px !important;
|
||||
height: 20px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="background-color: #f3f3f3; height: 100%">
|
||||
<table
|
||||
height="100%"
|
||||
width="100%"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
border="0"
|
||||
bgcolor="#f3f3f3"
|
||||
style="background-color: #f3f3f3"
|
||||
>
|
||||
<tr>
|
||||
<td valign="top" align="left">
|
||||
<table cellpadding="0" cellspacing="0" border="0" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="top">
|
||||
<table cellpadding="0" cellspacing="0" border="0" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table
|
||||
class="max-width"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
border="0"
|
||||
width="100%"
|
||||
style="width: 100%"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="spacer"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="padding main">
|
||||
<table cellpadding="0" cellspacing="0" border="0" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<!-- LOGO -->
|
||||
<a href="https://payloadcms.com/" target="_blank">
|
||||
<img
|
||||
src="https://payloadcms.com/images/logo-dark.png"
|
||||
width="150"
|
||||
height="auto"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="spacer"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<!-- HEADLINE -->
|
||||
<h1 style="margin: 0 0 30px">{{headline}}</h1>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<!-- CONTENT -->
|
||||
{{{content}}}
|
||||
|
||||
<!-- CTA -->
|
||||
{{#if cta}}
|
||||
<div>
|
||||
<a
|
||||
href="{{cta.url}}"
|
||||
style="
|
||||
background-color: #222222;
|
||||
border-radius: 4px;
|
||||
color: #ffffff;
|
||||
display: inline-block;
|
||||
font-family: sans-serif;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
line-height: 60px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
width: 200px;
|
||||
-webkit-text-size-adjust: none;
|
||||
"
|
||||
>
|
||||
{{cta.buttonLabel}}
|
||||
</a>
|
||||
</div>
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -8,7 +8,6 @@ if (process.env.NODE_ENV === 'production') {
|
||||
// Configure a custom transport here
|
||||
},
|
||||
}
|
||||
|
||||
} else {
|
||||
email = {
|
||||
fromName: 'Ethereal Email',
|
||||
|
||||
@@ -1 +1 @@
|
||||
export default {};
|
||||
export default {}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import dotenv from 'dotenv'
|
||||
import path from 'path'
|
||||
import { buildConfig } from 'payload/config'
|
||||
@@ -14,7 +13,7 @@ const mockModulePath = path.resolve(__dirname, './emptyModule.js')
|
||||
|
||||
export default buildConfig({
|
||||
admin: {
|
||||
webpack: config => ({
|
||||
webpack: (config) => ({
|
||||
...config,
|
||||
resolve: {
|
||||
...config?.resolve,
|
||||
@@ -36,10 +35,7 @@ export default buildConfig({
|
||||
},
|
||||
}),
|
||||
},
|
||||
collections: [
|
||||
Newsletter,
|
||||
Users,
|
||||
],
|
||||
collections: [Newsletter, Users],
|
||||
typescript: {
|
||||
outputFile: path.resolve(__dirname, 'payload-types.ts'),
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@use "../shared.scss";
|
||||
@use '../shared.scss';
|
||||
|
||||
.checkbox {
|
||||
position: relative;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@use "../shared.scss";
|
||||
@use '../shared.scss';
|
||||
|
||||
.select {
|
||||
position: relative;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@use "../shared.scss";
|
||||
@use '../shared.scss';
|
||||
|
||||
.wrap {
|
||||
position: relative;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@use "../shared.scss";
|
||||
@use '../shared.scss';
|
||||
|
||||
.wrap {
|
||||
position: relative;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@use "../shared.scss";
|
||||
@use '../shared.scss';
|
||||
|
||||
.select {
|
||||
position: relative;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@use "../shared.scss";
|
||||
@use '../shared.scss';
|
||||
|
||||
.select {
|
||||
position: relative;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@use "../shared.scss";
|
||||
@use '../shared.scss';
|
||||
|
||||
.wrap {
|
||||
position: relative;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@use "../shared.scss";
|
||||
@use '../shared.scss';
|
||||
|
||||
.wrap {
|
||||
position: relative;
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
margin-right: calc(var(--base) * -0.5);
|
||||
width: calc(100% + #{var(--base)});
|
||||
|
||||
>* {
|
||||
> * {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@use "../../../css/common.scss" as *;
|
||||
@use '../../../css/common.scss' as *;
|
||||
|
||||
@mixin formInput() {
|
||||
all: unset;
|
||||
|
||||
@@ -35,7 +35,8 @@
|
||||
background-color: var(--color-black);
|
||||
color: var(--color-white);
|
||||
|
||||
&:hover, &:focus {
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--color-white);
|
||||
color: var(--color-black);
|
||||
box-shadow: inset 0 0 0 1px var(--color-black);
|
||||
@@ -46,7 +47,8 @@
|
||||
background-color: var(--color-white);
|
||||
box-shadow: inset 0 0 0 1px var(--color-black);
|
||||
|
||||
&:hover, &:focus {
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--color-black);
|
||||
color: var(--color-white);
|
||||
box-shadow: inset 0 0 0 1px var(--color-black);
|
||||
|
||||
@@ -26,11 +26,11 @@
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
|
||||
&[aria-expanded="true"] {
|
||||
&[aria-expanded='true'] {
|
||||
transform: rotate(-25deg);
|
||||
}
|
||||
|
||||
@include mid-break {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,4 +28,4 @@
|
||||
.menuItem {
|
||||
@extend %h4;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
@use './type.scss' as *;
|
||||
|
||||
:root {
|
||||
--breakpoint-xs-width : #{$breakpoint-xs-width};
|
||||
--breakpoint-s-width : #{$breakpoint-s-width};
|
||||
--breakpoint-m-width : #{$breakpoint-m-width};
|
||||
--breakpoint-l-width : #{$breakpoint-l-width};
|
||||
--breakpoint-xs-width: #{$breakpoint-xs-width};
|
||||
--breakpoint-s-width: #{$breakpoint-s-width};
|
||||
--breakpoint-m-width: #{$breakpoint-m-width};
|
||||
--breakpoint-l-width: #{$breakpoint-l-width};
|
||||
--scrollbar-width: 17px;
|
||||
|
||||
--base: 24px;
|
||||
@@ -100,7 +100,7 @@ p {
|
||||
margin: var(--base) 0;
|
||||
|
||||
@include mid-break {
|
||||
margin: calc(var(--base) * .75) 0;
|
||||
margin: calc(var(--base) * 0.75) 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,12 +114,12 @@ a {
|
||||
color: currentColor;
|
||||
|
||||
&:focus {
|
||||
opacity: .8;
|
||||
opacity: 0.8;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: .7;
|
||||
opacity: 0.7;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user