Compare commits

...

240 Commits

Author SHA1 Message Date
Elliot DeNolf
d2f45343da chore(release): db-postgres/0.1.10 [skip ci] 2023-10-19 16:13:55 -04:00
Elliot DeNolf
5ba95df674 chore(release): db-mongodb/1.0.4 [skip ci] 2023-10-19 16:13:46 -04:00
Elliot DeNolf
40f98e4a0d chore(release): bundler-webpack/1.0.4 [skip ci] 2023-10-19 16:13:39 -04:00
Elliot DeNolf
584ead9fe2 chore(release): bundler-vite/0.1.3 [skip ci] 2023-10-19 16:13:29 -04:00
Elliot DeNolf
b6bf354f6a chore(release): payload/2.0.11 [skip ci] 2023-10-19 16:10:46 -04:00
Elliot DeNolf
9918c2499a chore(deps): bump sass (#3768)
* chore(deps): bump sass and sass-loader

* chore: handle sass slash div deprecation
2023-10-19 15:52:39 -04:00
Jarrod Flesch
8c48c8beb5 fix(webpack-bundler): corrects payload alias (#3769) 2023-10-19 15:21:39 -04:00
Jacob Fletcher
2697753715 chore(live-preview): significantly improves test coverage (#3763) 2023-10-19 14:56:16 -04:00
Jarrod Flesch
4b13686f61 fix: corrects versions collection casing (#3739) 2023-10-19 13:08:24 -04:00
Jessica Chowdhury
ab7999d3c1 fix: updates req after file resize (#3754) 2023-10-19 12:56:24 -04:00
Jessica Chowdhury
a592188c1d fix: correctly renders focal point when crop is set to false (#3759) 2023-10-19 12:51:13 -04:00
Elliot DeNolf
5ff0846b6c feat: add ability to opt out of type gen declare statement (#3765)
* feat: add ability to opt out of type gen declare statement

* chore: docs wording
2023-10-19 12:44:28 -04:00
xHomu
13cabf129e fix: account for many slug types in generate types (#3698)
* Fix generate:types bug #3697

generateEntityDeclarations function creates mismatched type names. We'll simply use the existing Config type instead.

* code cleanup
2023-10-19 11:36:26 -04:00
Elliot DeNolf
c173e55b89 fix(bundler-webpack): better node_modules resolution (#3744)
* fix(bundler-webpack): better node_modules resolution

* chore: see if retries are affecting new webpack changes

* chore: reinstate retries

This reverts commit 96989295ba.

* chore: default to process.cwd() if cannot find node_modules path
2023-10-19 11:28:31 -04:00
Take Weiland
bcdd2d626f fix: handle graphQL: false on globals when building policy type (#3729) 2023-10-19 09:13:51 -04:00
Elliot DeNolf
67682248c8 chore: more master -> main readme renames 2023-10-19 09:08:40 -04:00
Jacob Fletcher
7c52d6ee28 Merge pull request #3745 from payloadcms/fix/misc-admin
Fix/misc admin
2023-10-19 09:06:20 -04:00
Elliot DeNolf
bc65b53ce5 chore(release): eslint-config-payload/1.0.0 2023-10-18 21:37:35 -04:00
Elliot DeNolf
c8cc6ea1cc chore(script): more prompts during publish 2023-10-18 21:29:26 -04:00
Jacob Fletcher
4e05e6fd85 fix(a11y): tab indices 2023-10-18 17:55:55 -04:00
Jacob Fletcher
6988a68eaf fix: renders id as fallback title in DeleteDocument 2023-10-18 17:55:55 -04:00
Jacob Fletcher
a272692726 chore: refines drawer and blur styles 2023-10-18 17:55:39 -04:00
Dan Ribbens
229e4459cb fix(db-postgres): block and array inserts error (#3714)
Co-authored-by: James <james@trbl.design>
2023-10-18 16:53:26 -04:00
Take Weiland
056585ed31 fix: properly handles hideAPIURL (#3721) 2023-10-18 16:36:57 -04:00
Elliot DeNolf
b545433ee6 chore(templates): add payload helper npm script 2023-10-18 16:11:58 -04:00
Elliot DeNolf
4c938b5f9e chore(plugin-nested-docs): lint fix (#3740) 2023-10-18 14:40:48 -04:00
Elliot DeNolf
f1d8fa9999 chore: add 2.0 announcement banner 2023-10-18 14:38:58 -04:00
Jarrod Flesch
1670a603f6 chore: adjust where sharp types are imported from (#3645) 2023-10-18 11:44:49 -04:00
Elliot DeNolf
22f1fa8fc9 chore: update issue template and repro guide 2023-10-18 11:43:11 -04:00
Jacob Fletcher
370e8d1938 chore: replaces bg blur in document controls (#3736) 2023-10-18 11:40:25 -04:00
Elliot DeNolf
3a3eab761e fix: filesRequiredOnCreate typing, tests, linting (#3737) 2023-10-18 11:27:55 -04:00
Jacob Fletcher
238f7e1b94 chore(examples/live-preview): pins @payloadcms/live-preview-react to latest 2023-10-18 10:18:01 -04:00
Jacob Fletcher
58e2083882 Merge pull request #3719 from payloadcms/fix/live-preview/uploads
Fix/live preview/uploads
2023-10-17 17:05:30 -04:00
Jacob Fletcher
20cde242fb fix(live-preview): properly handles uploads and hasOne monomorphic relationships 2023-10-17 17:00:59 -04:00
Elliot DeNolf
f50a392d59 chore(script): update release script [skip ci] 2023-10-17 17:00:14 -04:00
Elliot DeNolf
fa1740d906 chore: update changelog 2023-10-17 16:51:24 -04:00
Elliot DeNolf
e847061c74 chore(release): payload/2.0.10 [skip ci] 2023-10-17 16:45:10 -04:00
Jacob Fletcher
ebd5e6ae8f chore: types fieldSchemaToJSON 2023-10-17 16:39:36 -04:00
TomDo1234
48de89794b feat: filesRequired is optional for uploads (#3668)
* filesRequired is optional for uploads

* More accurate name

* Updated docs

* ensure that by default you throw on missing file
2023-10-17 15:21:16 -04:00
Elliot DeNolf
ef4b5d8bfd chore: add payload as peer dep to all adapters (#3716) 2023-10-17 15:20:09 -04:00
Elliot DeNolf
5711d42eca chore: update pnpm-lock.yaml 2023-10-17 15:03:58 -04:00
Elliot DeNolf
a446a788a9 chore(release): payload/2.0.9 2023-10-17 15:00:55 -04:00
Elliot DeNolf
df57196d19 chore(live-preview-react): adjust live-preview semver dep 2023-10-17 15:00:55 -04:00
Alessio Gravili
86c563e4e5 Merge remote-tracking branch 'origin/main' 2023-10-17 20:57:17 +02:00
Alessio Gravili
734b8c08ed chore(richtext-lexical): add additional safety checks for incorrect data passed into the editor 2023-10-17 20:56:22 +02:00
geminigeek
68c5a57515 [fix] Register first user verify update missing transaction id / req (#3665)
Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
2023-10-17 14:50:56 -04:00
Elliot DeNolf
d9f0b7bd30 chore(release): live-preview-react/0.1.4 2023-10-17 13:23:10 -04:00
Elliot DeNolf
5ecfe3da28 chore(release): richtext-lexical/0.1.13 2023-10-17 13:18:30 -04:00
Elliot DeNolf
3e3163e875 chore(release): live-preview/0.1.4 2023-10-17 13:17:33 -04:00
Elliot DeNolf
cfe1698dfd chore: update changelog 2023-10-17 13:15:36 -04:00
Elliot DeNolf
5722634660 chore(release): v2.0.8 2023-10-17 13:06:25 -04:00
Jacob Fletcher
cdbfc9132a chore: optimizes live preview (#3713) 2023-10-17 12:42:31 -04:00
Jacob Fletcher
dd0ac066ce feat(live-preview): caches field schema (#3711) 2023-10-17 12:11:55 -04:00
PatrikKozak
8b8ceabbdd fix(templates): user access control (#3712) 2023-10-17 12:07:37 -04:00
Alessio Gravili
8da18d3496 Merge pull request #3707 from payloadcms/fix/3683
fix(richtext-lexical): Blocks Field: Sub-forms being re-rendered unnecessarily
2023-10-17 17:33:05 +02:00
Elliot DeNolf
6cd4df3dc4 chore(script): properly version and publish non-latest packages 2023-10-17 11:30:09 -04:00
Elliot DeNolf
e74dc8633b ci: add retries to e2e tests (#3708)
* ci: add retries to e2e tests

* ci: add timeout_minutes for retry
2023-10-17 11:26:34 -04:00
Alessio Gravili
2f945919a3 chore: disable props for useIntersect hook 2023-10-17 17:24:50 +02:00
Alessio Gravili
52dc9177d7 chore: enable forceRender for richtext-lexical's Blocks feature form, and all their sub-forms 2023-10-17 16:44:34 +02:00
Alessio Gravili
cec757d098 chore: make sure intersectionObserver does not cause re-renders if forceRender is enabled 2023-10-17 16:34:44 +02:00
Jacob Fletcher
80e57150a0 chore: refines live preview shadow and bg (#3706) 2023-10-17 10:11:21 -04:00
Elliot DeNolf
2969da7402 chore(release): create-payload-app/1.0.0 2023-10-16 23:32:32 -04:00
Elliot DeNolf
35da1db99f chore(create-payload-app): use swc, cleanup scripts 2023-10-16 23:17:45 -04:00
Elliot DeNolf
a578226d34 ci: rework release-it script 2023-10-16 22:47:27 -04:00
James
8c75f32620 chore: website seed script compatibility with postgres + mongo 2023-10-16 18:39:28 -04:00
James
c3ab8b9115 Merge branch 'main' of github.com:payloadcms/payload 2023-10-16 18:34:18 -04:00
James
993568a195 fix: bug with seeding ecommerce 2023-10-16 18:34:10 -04:00
Jacob Fletcher
c3cec64220 chore: refines main nav (#3703) 2023-10-16 17:36:05 -04:00
Elliot DeNolf
084e9f0ff8 chore(release): db-postgres/0.1.9 2023-10-16 16:32:03 -04:00
James Mikrut
9f0ef9b7da chore: template compatibility with postgres (#3701)
* fix: blocks within groups in postgres

* chore: template compatibility
2023-10-16 16:26:21 -04:00
James Mikrut
c384f490c8 Merge pull request #3700 from payloadcms/fix/postgres-blocks-within-groups
fix: blocks within groups in postgres
2023-10-16 16:25:46 -04:00
James
45a62ba949 fix: blocks within groups in postgres 2023-10-16 16:12:21 -04:00
Elliot DeNolf
1eae5f9c99 test(create-payload-app): test create project for all templates 2023-10-16 15:12:59 -04:00
James
dfa861557d chore: website template postgres compatibility 2023-10-16 14:47:18 -04:00
James
228fd58020 chore: ecommerce template postgres compatibility 2023-10-16 14:40:37 -04:00
Take Weiland
150799e10e fix: some local operations missing req.transactionID (#3651)
Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
2023-10-16 14:17:58 -04:00
Elliot DeNolf
823436a883 chore(release): richtext-slate/1.0.6 2023-10-16 14:16:20 -04:00
Elliot DeNolf
cbb0ba1a2c chore(release): richtext-lexical/0.1.12 2023-10-16 14:15:55 -04:00
Alessio Gravili
cb39354a9d fix(richtext-*): link drawer form receiving incorrect field schema (#3696) 2023-10-16 19:43:23 +02:00
Jacob Fletcher
1625ff244e fix: renders mobile document controls (#3695) 2023-10-16 13:37:40 -04:00
Alessio Gravili
24918fe1d2 fix(richtext-lexical): #3682 isolated editor container causing z-index issues 2023-10-16 18:56:25 +02:00
Alessio Gravili
b8a58666e7 fix(richtext-*): extra fields not being iterated correctly (#3693) 2023-10-16 18:47:52 +02:00
Elliot DeNolf
2e6a2c8355 ci: add plugin-nested-docs build 2023-10-16 12:16:14 -04:00
Elliot DeNolf
1fdff92525 chore(script): use semver to validate and show next version 2023-10-16 12:10:30 -04:00
Elliot DeNolf
7da5f6e92a chore(release): plugin-nested-docs/1.0.8 2023-10-16 12:07:17 -04:00
James Mikrut
67c7572e5f Merge pull request #3689 from payloadcms/chore/nested-docs
Chore/nested docs
2023-10-16 11:44:11 -04:00
craigrdaniels
e311e8fff9 fix: autosave time shown minutes only (#3492) 2023-10-16 11:32:31 -04:00
James
086e50b9b3 chore: renames nested-docs test suite 2023-10-16 11:30:45 -04:00
James
66ab6c587d chore: moves nested docs to monorepo 2023-10-16 11:28:40 -04:00
Elliot DeNolf
786fb926c2 chore: update changelog 2023-10-16 11:16:56 -04:00
James
7d954b11a3 Merge branch 'feat/allow-filteroptions-null' of github.com:payloadcms/payload into chore/nested-docs 2023-10-16 11:09:23 -04:00
Jarrod Flesch
3c5044368d fix: corrects add block index (#3681) 2023-10-16 10:58:54 -04:00
Jacob Fletcher
f129d6c607 Merge pull request #3687 from payloadcms/dependabot/npm_and_yarn/examples/testing/babel/traverse-7.23.2
chore(deps): bump @babel/traverse from 7.22.17 to 7.23.2 in /examples/testing
2023-10-16 10:57:29 -04:00
dependabot[bot]
de2d985405 chore(deps): bump @babel/traverse in /examples/testing
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.22.17 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-16 14:44:39 +00:00
Jacob Fletcher
060f3c73fa Merge pull request #3674 from payloadcms/chore/plugin-nested-docs
chore: imports nested docs plugin
2023-10-16 10:43:52 -04:00
Jacob Fletcher
62ae7be113 chore(plugin-nested-docs): migrates demo to payload 2.x 2023-10-16 10:40:23 -04:00
Jacob Fletcher
771df061b4 chore(plugin-nested-docs): migrates to monorepo setup 2023-10-16 10:29:04 -04:00
Alessio Gravili
09e64c3be8 chore: _community test: add missing "extends" to tsconfig.json 2023-10-16 16:28:17 +02:00
PatrikKozak
1a006fef19 chore(templates/website): uses correct logo color in dark mode (#3667) 2023-10-16 10:14:09 -04:00
James
c4cac99875 feat: allows filterOptions to return null 2023-10-16 10:01:19 -04:00
Jessica Chowdhury
d616772740 fix: misc upload crop/focal point updates (#3580) 2023-10-16 08:42:35 -04:00
Elliot DeNolf
4b9e87bb4d chore(release): db-postgres/0.1.8 2023-10-15 21:04:46 -04:00
Elliot DeNolf
ff5e174497 chore(script): multiselect publishing prompt 2023-10-15 20:53:16 -04:00
Elliot DeNolf
34017e1758 chore(release): payload/2.0.7 2023-10-15 20:49:18 -04:00
James Mikrut
23d95526ab Merge pull request #3666 from payloadcms/fix/empty-in-array
fix: empty inArray no longer crashes postgres
2023-10-15 18:05:15 -04:00
James
59b87fdb21 chore: typo 2023-10-15 17:55:30 -04:00
James
f2ac1f7d48 chore: merge main 2023-10-15 17:31:24 -04:00
James
8d4f39af5e Merge branch 'main' of github.com:payloadcms/payload into fix/empty-in-array 2023-10-15 17:29:51 -04:00
James
c4ac341d75 fix: empty inArray no longer crashes postgres 2023-10-15 17:28:13 -04:00
Elliot DeNolf
27589482dd chore(release): @payloadcms/db-postgres/0.1.7 2023-10-15 14:12:02 -04:00
James Mikrut
d7ab4b7062 Merge pull request #3642 from payloadcms/fix/#3568-postgres-relationships-in-array
fix(db-postgres): query relationship in array alias
2023-10-15 11:31:57 -04:00
James
2c8fbf1be3 chore: adds specificity to tests 2023-10-15 11:08:16 -04:00
James
eec88f8f1b Merge branch 'fix/#3568-postgres-relationships-in-array' of github.com:payloadcms/payload into fix/#3568-postgres-relationships-in-array 2023-10-15 10:43:32 -04:00
James
1481ef97b5 Merge branch 'main' of github.com:payloadcms/payload 2023-10-15 09:44:19 -04:00
James
a89e89fb80 chore: documents pagination: false 2023-10-15 09:44:00 -04:00
Alessio Gravili
7e7eeb059d chore(richtext-lexical): add 'use client' to field and cell 2023-10-15 14:25:36 +02:00
Alessio Gravili
dc2a502dcc perf(richtext-lexical): remove unnecessary prop drilling and load hooks being run for initialState 2023-10-15 14:07:24 +02:00
Jacob Fletcher
3eefe8cb21 Merge remote-tracking branch 'plugin-nested-docs/main' into chore/plugin-nested-docs 2023-10-15 02:28:29 -04:00
Elliot DeNolf
a9a5ba82d8 chore: update pnpm-lock.yaml 2023-10-14 21:09:15 -04:00
James
e6e8fae1c5 Merge branch 'main' of github.com:payloadcms/payload 2023-10-14 19:24:50 -04:00
James
a05868a7f3 chore: remove unnecessary peer dep 2023-10-14 19:24:32 -04:00
Elliot DeNolf
f27cd26575 chore(release): richtext-lexical/0.1.11 2023-10-14 17:35:44 -04:00
Elliot DeNolf
db835ea5c8 chore(release): db-postgres/0.1.6 2023-10-14 17:35:26 -04:00
Jacob Fletcher
d8f265fb94 chore(examples): bumps deps to latest (#3655) 2023-10-14 16:49:47 -04:00
Alessio Gravili
d81d4eb075 feat(richtext-lexical): LexicalPluginToLexical migration feature 2023-10-14 22:36:16 +02:00
James
52f89c0136 fix(db-postgres): ensures columns are nullable if within field with condition 2023-10-14 15:34:27 -04:00
Dan Ribbens
b0083b7c07 test: fix missing variable 2023-10-14 15:32:06 -04:00
Dan Ribbens
21649537a6 fix(db-postgres): query relationship path inside arrays 2023-10-14 15:17:42 -04:00
Elliot DeNolf
9be34c9599 chore(release): richtext-lexical/0.1.10 2023-10-14 14:38:58 -04:00
Elliot DeNolf
8ca632e541 chore(release): richtext-slate/1.0.5 2023-10-14 14:38:48 -04:00
Elliot DeNolf
2ef79145a4 chore(release): payload/2.0.6 2023-10-14 14:37:18 -04:00
James
a0641a445d Merge branch 'main' of github.com:payloadcms/payload 2023-10-14 14:34:46 -04:00
James
3a2e78f7f3 chore: add peer deps for richtext packages 2023-10-14 14:34:40 -04:00
James Mikrut
976d69d154 Merge pull request #3657 from payloadcms/chore/export-pattern
chore: properly separates server / client exports
2023-10-14 14:32:11 -04:00
James
66018362fe chore: properly separates server / client exports 2023-10-14 14:08:08 -04:00
Elliot DeNolf
647fe23d1c chore(release): richtext-lexical/0.1.9 2023-10-14 12:26:28 -04:00
Elliot DeNolf
d7c61861f6 chore(release): richtext-slate/1.0.4 2023-10-14 12:26:19 -04:00
Elliot DeNolf
7caa098023 chore(release): db-postgres/0.1.5 2023-10-14 12:25:54 -04:00
James Mikrut
fd54c40400 Merge pull request #3654 from payloadcms/chore/dynamic-drizzle-kit-import
chore: only imports drizzle-kit if it will be used
2023-10-14 12:21:14 -04:00
James
e180131314 chore: only imports drizzle-kit if it will be used 2023-10-14 12:13:13 -04:00
James
5902d4542b Merge branch 'main' of github.com:payloadcms/payload 2023-10-14 11:51:13 -04:00
James
6bc282444e chore: slate compatibility with next-payload 2023-10-14 11:49:38 -04:00
Alessio Gravili
4dc6c09347 feat(richtext-lexical): SlateToLexical migration feature 2023-10-14 13:36:32 +02:00
Elliot DeNolf
03b9ab0054 chore: cleanup scripts 2023-10-13 16:34:37 -04:00
Elliot DeNolf
3c3c93f483 chore(release): richtext-lexical/0.1.8 2023-10-13 16:05:59 -04:00
Alessio Gravili
5dbfb1a335 fix(richtext-lexical): Blocks: working population for crazy amounts of nesting 2023-10-13 21:04:56 +02:00
Alessio Gravili
d411874589 chore(richtext-lexical): Blocks: clean up population 2023-10-13 20:02:18 +02:00
Jacob Fletcher
8358e2f2d2 chore: properly scopes selector in bulk update e2e test (#3640) 2023-10-13 13:51:52 -04:00
Dan Ribbens
2c67eff059 fix(db-postgres): query relationship in array alias 2023-10-13 13:32:44 -04:00
Elliot DeNolf
012b8e6f90 chore: remove pnpm from engines, shows warning when not using pnpm 2023-10-13 13:05:25 -04:00
Jacob Fletcher
fcd4c8d830 fix: document sidebar vertical overflow (#3639) 2023-10-13 13:00:02 -04:00
Elliot DeNolf
81ec435363 chore(release): richtext-lexical/0.1.7 2023-10-13 12:49:08 -04:00
Jacob Fletcher
e116fcfbf5 docs: updates references of master to main 2023-10-13 12:44:45 -04:00
Alessio Gravili
c47632dc1d fix(richtext-lexical): Blocks: Nested Blocks having incorrect initial data (e.g. missing rows property) (#3638)
* fix(richtext-lexical): Blocks: Sub-Blocks having incorrect initial data (e.g. missing rows property)

* chore: remove unnecessary comment
2023-10-13 18:39:34 +02:00
Jacob Fletcher
0dab68b336 chore: prevents group fields from overflowing into the sidebar (#3637) 2023-10-13 12:04:39 -04:00
Jacob Fletcher
483f93bfcf chore: cleans up admin e2e tests (#3636) 2023-10-13 12:04:05 -04:00
Jessica Chowdhury
4bd01df411 fix: login form clearing out and field spacing (#3633) 2023-10-13 11:15:07 -04:00
Elliot DeNolf
8938f2b7e9 chore(release): plugin-nested-docs/1.0.7 2023-10-13 10:52:51 -04:00
Elliot DeNolf
ed8a9ffa09 chore(deps): add payload 2.0 to peer deps 2023-10-13 10:51:14 -04:00
Jessica Chowdhury
c956a85252 fix: sidebar field permissions (#3629)
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
2023-10-13 10:29:22 -04:00
Jacob Fletcher
beed83b231 fix: preview button conditions (#3613) 2023-10-13 10:23:26 -04:00
James Mikrut
3b1bdcbe41 chore: de-duplicates array / block data from form state (#3607)
* chore: consolidates array manipulation tests

---------

Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
2023-10-13 09:45:00 -04:00
James Mikrut
d3d0971275 Merge pull request #3630 from payloadcms/fix/duplicating-drafts
fix: allows drafts to be duplicated
2023-10-13 08:50:36 -04:00
Jessica Boezwinkle
1a99d66cd0 fix: allows drafts to be duplicated 2023-10-13 11:59:23 +01:00
Elliot DeNolf
52c4a63bf1 chore(release): richtext-slate/1.0.3 2023-10-12 23:38:38 -04:00
Elliot DeNolf
3446d28602 chore(release): richtext-lexical/0.1.6 2023-10-12 23:38:29 -04:00
Elliot DeNolf
2eb18771a1 chore(release): live-preview-react/0.1.3 2023-10-12 23:38:25 -04:00
Elliot DeNolf
f6fd5d6742 chore(release): live-preview/0.1.3 2023-10-12 23:38:20 -04:00
Jacob Fletcher
b5b487ab90 1.0.6 2023-07-10 16:49:14 -04:00
Jacob Fletcher
940bfe4f18 fix: threads locale through resaveChildren (#24) 2023-07-10 16:45:21 -04:00
Jacob Fletcher
41dcdd4e01 chore: adds localization section to docs (#25) 2023-07-10 16:43:28 -04:00
Jacob Fletcher
3dbb70a9e6 1.0.5 2023-06-19 17:02:48 -04:00
Jacob Fletcher
adef360275 Merge pull request #21 from payloadcms/fix/parent-types
fix: createParentField types
2023-06-19 17:01:02 -04:00
Jacob Fletcher
abebd7f440 Merge pull request #11 from payloadcms/chore/export-fields
chore: exports fields top-level
2023-06-19 16:56:50 -04:00
Jacob Fletcher
e9ed969ad9 fix: createParentField types 2023-06-19 16:56:34 -04:00
Jacob Fletcher
6b1a7f0843 Merge pull request #16 from payloadcms/chore/eslint
chore: eslint and prettier
2023-05-18 14:43:26 -04:00
Jacob Fletcher
e76cd58425 chore: bumps demo to payload v1.8.2 2023-05-18 13:57:32 -04:00
Jacob Fletcher
888f937e3c chore: eslint and prettier 2023-05-18 13:57:32 -04:00
Jacob Fletcher
d61ced9cbd chore: exports fields top-level 2023-02-01 17:57:16 -05:00
Jacob Fletcher
ada165a21e Merge pull request #10 from payloadcms/chore/export-types
chore: exports types
2023-02-01 17:42:12 -05:00
Jacob Fletcher
8d4fd14ff2 chore: exports types 2023-02-01 17:36:31 -05:00
Jacob Fletcher
0363c85dbd chore: renames Options to PluginConfig 2022-11-17 17:13:27 -05:00
Jacob Fletcher
fa660cd4ef chore: ignores package-lock 2022-11-16 14:06:03 -05:00
Jacob Fletcher
4f6eb1e307 chore: adds .env.example to demo 2022-11-16 12:50:53 -05:00
Jacob Fletcher
50860bc8c1 1.0.4 2022-11-16 12:27:39 -05:00
Jacob Fletcher
52f7890989 docs: adds development documentation 2022-11-16 12:22:57 -05:00
Jacob Fletcher
6200a119f9 fix: prevents child-parents 2022-11-16 11:31:15 -05:00
Jacob Fletcher
46eb61d18a chore: payload peerDep 2022-11-16 10:45:14 -05:00
Jacob Fletcher
2766489476 chore: bumps payload deps 2022-11-15 17:44:59 -05:00
Jacob Fletcher
0c1102a138 chore: seeds grandchild page 2022-11-15 17:10:53 -05:00
Jacob Fletcher
e765b96a4e Merge pull request #8 from payloadcms/fix/localization
fix: conditionally localizes breadcrumbs
2022-11-15 16:33:39 -05:00
Jacob Fletcher
01c42d9630 fix: conditionally localizes breadcrumbs 2022-11-15 16:19:18 -05:00
Jacob Fletcher
f421a2715d chore: seeds demo 2022-11-15 16:01:27 -05:00
Jacob Fletcher
edd7a8086c Merge pull request #7 from bencun/feat/disable-self-as-parent
Disable referencing self as a parent by default to prevent crashes
2022-11-15 13:24:09 -05:00
Jacob Fletcher
010db46e08 docs: updates README 2022-11-15 13:18:11 -05:00
Jacob Fletcher
fc8a6e107e chore: ignores .DS_Store 2022-11-15 12:58:09 -05:00
Aleksandar Bencun
2cb79f1752 Improved the docs a bit. 2022-10-26 23:22:53 +02:00
Aleksandar Bencun
5f740a60cc Prevent selecting self as a parent unless this behavior is overridden explicitly. 2022-10-26 23:15:04 +02:00
Jacob Fletcher
27313995cc Merge pull request #5 from mikemoooo/patch-1
feat: localized breadcrumbs
2022-08-15 10:03:02 -04:00
Mike
93afe1d000 Support localised breadcrumbs 2022-08-15 15:51:28 +02:00
James
e7ffa2638a 1.0.3 2022-07-11 15:19:02 -07:00
James
666765f3fb fix: further ensures resaving children works with drafts 2022-07-11 15:18:52 -07:00
James
fc14622555 1.0.2 2022-07-11 15:14:14 -07:00
James
0d83d83d3c feat: ensures drafts work 2022-07-11 15:14:09 -07:00
Jacob Fletcher
aab2f5f7d2 chore: updates README 2022-07-05 15:11:25 -04:00
Jacob Fletcher
fbdc74ea71 chore: adds homepage and repository to package.json 2022-07-05 14:54:39 -04:00
Jacob Fletcher
141e40ffb9 Merge branch 'main' of github.com:payloadcms/payload-plugin-nested-pages 2022-07-05 14:46:41 -04:00
Jacob Fletcher
bd2c1c6bf2 1.0.1 2022-07-05 14:46:27 -04:00
Jacob Fletcher
ba90fdbdfd feat: updates payload 2022-07-05 14:45:48 -04:00
James Mikrut
9991fdb8c8 Update README.md 2022-06-13 09:15:14 -04:00
Jacob Fletcher
15c9ce56c2 chore: updates package name 2022-05-27 10:25:15 -04:00
Jacob Fletcher
eecdd0e118 Merge branch 'main' of github.com:payloadcms/payload-plugin-nested-pages 2022-05-04 11:13:21 -04:00
Jacob Fletcher
f749732a0a 0.0.3 2022-05-04 11:13:07 -04:00
Jacob Fletcher
78e2b518cf chore: updates react peerDep 2022-05-04 11:12:09 -04:00
James
91362587f0 chore: renames plugin 2022-05-04 10:48:36 -04:00
Jacob Fletcher
a4c4fc7060 Merge pull request #1 from payloadcms/fix/disable-errors-breadcrumbs
fix: disables errors while populating breadcrumbs
2022-04-18 08:43:31 -04:00
James
db09f4839f fix: disables errors while populating breadcrumbs 2022-04-15 15:28:18 -04:00
Jacob Fletcher
c71e079fae 0.0.2 2022-04-10 12:23:09 -04:00
Jacob Fletcher
b90785fa8c chore: bumps payload to v0.15.6 2022-04-10 12:22:54 -04:00
Jacob Fletcher
6e340f008f feat: initial working draft 2022-03-07 16:19:40 -05:00
Jacob Fletcher
e08e681eda chore: initializes standalone repository 2022-02-22 15:22:13 -05:00
James
238db1750c fix: potential bug in breadcrumbs plugin 2022-01-31 16:18:46 -05:00
James
0962cd6fcb fix: allows breadcrumbs to populate without being passed full copy of page data 2022-01-07 18:34:40 -05:00
James
6b436d38e1 fix: bug with breadcrumbs 2021-10-18 11:42:29 -04:00
James
ae93006446 chore: revises logs 2021-10-07 19:59:17 -04:00
James
fc722573bf chore: logging 2021-10-07 19:18:49 -04:00
James
b72ba7fe86 feat: resaves page after create to properly generate breadcrumb 2021-09-14 11:47:17 -04:00
James
3dd777343b fix: ensures id is passed through to populateBreadcrumbs 2021-09-14 11:42:17 -04:00
James
f1f7592eb2 fix: big rip 2021-09-08 20:30:21 -04:00
James
3816589e8b fix: restricts depth to 0 in search sync ops 2021-09-08 20:15:11 -04:00
James
ba70b8065e feat: improves breadcrumbs performance 2021-09-08 19:01:05 -04:00
James
9b88a47a47 feat: attempts to optimize breadcrumbs 2021-09-01 18:14:18 -04:00
James
9a747bc1eb feat: adds alerts 2021-09-01 13:42:28 -04:00
James
f82723ef33 fix: reverts plugin back to beforeRead 2021-08-31 12:49:19 -04:00
James
897f7be0f7 feat: adds author and improves breadcrumbs 2021-08-31 12:43:09 -04:00
James
80f6c7ebe1 fix: cleans breadcrumb types 2021-08-31 12:33:53 -04:00
James
9da4151642 fix: breadcrumbs plugin beforeChange 2021-08-31 12:31:38 -04:00
James
b89cbe5715 fix: breadcrumbs, fullTitle, subsite 2021-08-30 17:08:56 -04:00
James
725a1d35ef feat: revises breadcrumbs and subsites to rely on depth properly 2021-08-02 22:01:58 -04:00
James
2fbfb5d305 feat: builds breadcrumbs plugin 2021-08-02 21:08:01 -04:00
384 changed files with 13772 additions and 6294 deletions

View File

@@ -10,7 +10,12 @@ body:
id: reproduction-link id: reproduction-link
attributes: attributes:
label: Link to reproduction label: Link to reproduction
description: Please add a link to a reproduction. See the fork [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information. description: Want us to look into your issue faster? Follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
validations:
required: false
- type: textarea
attributes:
label: Describe the Bug
validations: validations:
required: true required: true
- type: textarea - type: textarea
@@ -19,11 +24,6 @@ body:
description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken. description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken.
validations: validations:
required: true required: true
- type: textarea
attributes:
label: Describe the Bug
validations:
required: true
- type: input - type: input
id: version id: version
attributes: attributes:

View File

@@ -1,10 +1,11 @@
# Reproduction Guide # Reproduction Guide
1. [fork](https://github.com/payloadcms/payload/fork) this repo 1. [Fork](https://github.com/payloadcms/payload/fork) this repo
2. run `yarn` to install dependencies 2. Optionally, create a new branch for your reproduction
3. open up the `test/_community` directory 3. Run `pnpm install` to install dependencies
4. add any necessary `collections/globals/fields` in this directory to recreate the issue you are experiencing 4. Open up the `test/_community` directory
5. run `yarn dev _community` to start the admin panel 5. Add any necessary `collections/globals/fields` in this directory to recreate the issue you are experiencing
6. Run `pnpm dev _community` to start the admin panel
**NOTE:** The goal is to isolate the problem by reducing the number of `collections/globals/fields` you add to the `test/_community` folder. This folder is _not_ meant for you to copy your project into, but rather recreate the issue you are experiencing with minimal config. **NOTE:** The goal is to isolate the problem by reducing the number of `collections/globals/fields` you add to the `test/_community` folder. This folder is _not_ meant for you to copy your project into, but rather recreate the issue you are experiencing with minimal config.
@@ -21,7 +22,7 @@
- `config.ts` - This is the _granular_ Payload config for testing. It should be as lightweight as possible. Reference existing configs for an example - `config.ts` - This is the _granular_ Payload config for testing. It should be as lightweight as possible. Reference existing configs for an example
- `int.spec.ts` [Optional] - This is the test file run by jest. Any test file must have a `*int.spec.ts` suffix. - `int.spec.ts` [Optional] - This is the test file run by jest. Any test file must have a `*int.spec.ts` suffix.
- `e2e.spec.ts` [Optional] - This is the end-to-end test file that will load up the admin UI using the above config and run Playwright tests. - `e2e.spec.ts` [Optional] - This is the end-to-end test file that will load up the admin UI using the above config and run Playwright tests.
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `yarn dev:generate-types _community`. - `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `pnpm dev:generate-types _community`.
The directory split up in this way specifically to reduce friction when creating tests and to add the ability to boot up Payload with that specific config. You should modify the files in `test/_community` to get started. The directory split up in this way specifically to reduce friction when creating tests and to add the ability to boot up Payload with that specific config. You should modify the files in `test/_community` to get started.
@@ -44,7 +45,7 @@ There are a couple ways run integration tests:
- **Manually** - you can run all int tests in the `/test/_community/int.spec.ts` file by running the following command: - **Manually** - you can run all int tests in the `/test/_community/int.spec.ts` file by running the following command:
```bash ```bash
yarn test:int _community pnpm test:int _community
``` ```
### Running E2E tests (Admin Panel UI tests) ### Running E2E tests (Admin Panel UI tests)

View File

@@ -132,7 +132,12 @@ jobs:
key: ${{ github.sha }}-${{ github.run_number }} key: ${{ github.sha }}-${{ github.run_number }}
- name: E2E Tests - name: E2E Tests
run: pnpm test:e2e --part ${{ matrix.part }} --bail uses: nick-fields/retry@v2
with:
retry_on: error
max_attempts: 2
timeout_minutes: 15
command: pnpm test:e2e --part ${{ matrix.part }} --bail
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
if: always() if: always()
@@ -213,8 +218,9 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
pkg: pkg:
- plugin-cloud
- create-payload-app - create-payload-app
- plugin-cloud
- plugin-nested-docs
steps: steps:
- name: Use Node.js 18 - name: Use Node.js 18

View File

@@ -1,22 +0,0 @@
{
"verbose": true,
"git": {
"commitMessage": "chore(release): v${version}",
"requireCleanWorkingDir": true
},
"github": {
"release": true
},
"npm": {
"skipChecks": true
},
"hooks": {
"before:init": ["pnpm i", "pnpm clean", "pnpm test"]
},
"plugins": {
"@release-it/conventional-changelog": {
"preset": "angular",
"infile": "CHANGELOG.md"
}
}
}

16
.release-it.pre.js Normal file
View File

@@ -0,0 +1,16 @@
module.exports = {
verbose: true,
git: {
requireCleanWorkingDir: false,
commit: false,
push: false,
tag: false,
},
npm: {
skipChecks: true,
tag: 'beta',
},
hooks: {
'before:init': ['pnpm install', 'pnpm clean', 'pnpm build'],
},
}

View File

@@ -1,25 +0,0 @@
{
"verbose": true,
"git": {
"requireCleanWorkingDir": false,
"commit": false,
"push": false,
"tag": false
},
"github": {
"release": true
},
"npm": {
"skipChecks": true,
"tag": "canary"
},
"hooks": {
"before:init": ["pnpm i", "pnpm clean", "pnpm test"]
},
"plugins": {
"@release-it/conventional-changelog": {
"preset": "angular",
"infile": "CHANGELOG.md"
}
}
}

View File

@@ -1,3 +1,119 @@
## [2.0.10](https://github.com/payloadcms/payload/compare/v2.0.9...v2.0.10) (2023-10-17)
### Features
* filesRequired is optional for uploads ([#3668](https://github.com/payloadcms/payload/pull/3668)) ([48de897](https://github.com/payloadcms/payload/commit/48de89794b2c5d94183090b0830fd355d8d6c6f3))
### Bug Fixes
* Register first user verify update missing transaction id / req ([#3665](https://github.com/payloadcms/payload/pull/3665)) ([68c5a5751](https://github.com/payloadcms/payload/commit/68c5a57515ffbba37c9194a75d0f672bdb10d96b))
## [2.0.8](https://github.com/payloadcms/payload/compare/v2.0.7...v2.0.8) (2023-10-17)
### Features
* allows filterOptions to return null ([c4cac99](https://github.com/payloadcms/payload/commit/c4cac998752730e7084598c92c77789da8c82e0d))
* **live-preview:** caches field schema ([#3711](https://github.com/payloadcms/payload/issues/3711)) ([dd0ac06](https://github.com/payloadcms/payload/commit/dd0ac066ce2ed88b85025309303610a95b6089e1))
### Bug Fixes
* autosave time shown minutes only ([#3492](https://github.com/payloadcms/payload/issues/3492)) ([e311e8f](https://github.com/payloadcms/payload/commit/e311e8fff9cd4264d7a71903f63c4fa825a3564d))
* blocks within groups in postgres ([45a62ba](https://github.com/payloadcms/payload/commit/45a62ba949aca33b25e0808773a5c2f1cf4adf82))
* bug with seeding ecommerce ([993568a](https://github.com/payloadcms/payload/commit/993568a1959ea10f960e35e4ed7a8e06af672a72))
* corrects add block index ([#3681](https://github.com/payloadcms/payload/issues/3681)) ([3c50443](https://github.com/payloadcms/payload/commit/3c5044368d5b30c76a2ff20c25b9234ef89dc205))
* misc upload crop/focal point updates ([#3580](https://github.com/payloadcms/payload/issues/3580)) ([d616772](https://github.com/payloadcms/payload/commit/d6167727401a01282345e63636560e029ae8e0f3))
* renders mobile document controls ([#3695](https://github.com/payloadcms/payload/issues/3695)) ([1625ff2](https://github.com/payloadcms/payload/commit/1625ff244e6e81e6edc0357037c3abc1a3bf8ba7))
* some local operations missing req.transactionID ([#3651](https://github.com/payloadcms/payload/issues/3651)) ([150799e](https://github.com/payloadcms/payload/commit/150799e10e580281d1af49388eb142ee9639a002))
* **richtext-*:** extra fields not being iterated correctly ([#3693](https://github.com/payloadcms/payload/issues/3693)) ([b8a5866](https://github.com/payloadcms/payload/commit/b8a58666e70f604af1e1cf349bcb4f9add0147e7))
* **richtext-*:** link drawer form receiving incorrect field schema ([#3696](https://github.com/payloadcms/payload/issues/3696)) ([cb39354](https://github.com/payloadcms/payload/commit/cb39354a9de3d20960110e453f62c4aa166d8448))
* **richtext-lexical:** [#3682](https://github.com/payloadcms/payload/issues/3682) isolated editor container causing z-index issues ([24918fe](https://github.com/payloadcms/payload/commit/24918fe1d2ca251e211632765d370c214cef2a38))
* **templates:** user access control ([#3712](https://github.com/payloadcms/payload/issues/3712)) ([8b8ceab](https://github.com/payloadcms/payload/commit/8b8ceabbdd6354761e7d744cacb1192cac3a2427))
## [2.0.6](https://github.com/payloadcms/payload/compare/v2.0.5...v2.0.6) (2023-10-14)
### Bug Fixes
* document sidebar vertical overflow ([#3639](https://github.com/payloadcms/payload/issues/3639)) ([fcd4c8d](https://github.com/payloadcms/payload/commit/fcd4c8d83040f00d10142ca12ba92616618b966e))
* login form clearing out and field spacing ([#3633](https://github.com/payloadcms/payload/issues/3633)) ([4bd01df](https://github.com/payloadcms/payload/commit/4bd01df411e4ad2ccacdcd6de0fb21a8145c3964))
* sidebar field permissions ([#3629](https://github.com/payloadcms/payload/issues/3629)) ([c956a85](https://github.com/payloadcms/payload/commit/c956a85252bc7de1686925cc783694383c0ac9be))
* preview button conditions ([#3613](https://github.com/payloadcms/payload/issues/3613)) ([beed83b](https://github.com/payloadcms/payload/commit/beed83b231b19090902dd502ff5eab054a67a1a6))
* allows drafts to be duplicated ([1a99d66](https://github.com/payloadcms/payload/commit/1a99d66cd0675cf2cb2c4317a121721f35682ff3))
## [2.0.5](https://github.com/payloadcms/payload/compare/v2.0.4...v2.0.5) (2023-10-12)
### Bug Fixes
* properly renders custom buttons for globals ([#3616](https://github.com/payloadcms/payload/issues/3616)) ([05cc287](https://github.com/payloadcms/payload/commit/05cc2873b4a19e2bd46be778f3610643d3e15d09))
* minor type issue in richText validate function ([06a51b3](https://github.com/payloadcms/payload/commit/06a51b3c9b9045b23051807aa03b222b542b46f5))
* live preview device size ([#3606](https://github.com/payloadcms/payload/issues/3606)) ([8bbac60](https://github.com/payloadcms/payload/commit/8bbac60e60a6dbe4dc0c7b05edbca7f6f2d1c569))
* properly handles nested routes for live preview ([#3586](https://github.com/payloadcms/payload/issues/3586)) ([6486468](https://github.com/payloadcms/payload/commit/64864686c418f9822bf61c45ece078a39e81b4cb))
* various stepnav related issues ([#3599](https://github.com/payloadcms/payload/issues/3599)) ([aaf8839](https://github.com/payloadcms/payload/commit/aaf883909c588bae1145ddddc5291a98740c2c03))
* database adapter types ([cc56da1](https://github.com/payloadcms/payload/commit/cc56da11d635d11ebbee67e6d0919c7275ede36e))
* postgres select fields within groups ([#3570](https://github.com/payloadcms/payload/issues/3570)) ([06e2fa9](https://github.com/payloadcms/payload/commit/06e2fa9d111c18fad3422953082266db9329fc91))
* [#3511](https://github.com/payloadcms/payload/issues/3511), documents don't delete their versions ([#3520](https://github.com/payloadcms/payload/issues/3520)) ([e3c7765](https://github.com/payloadcms/payload/commit/e3c776523a64da03a4756ee5ae8a84175090979f))
### Documentation
- updates building your own live preview hook ([#3604](https://github.com/payloadcms/payload/issues/3604)) ([15c7f0dbf](https://github.com/payloadcms/payload/commit/15c7f0dbf3ebf5c6a2bb011970dda515a15acb56))
- update config overview ([cfd923140](https://github.com/payloadcms/payload/commit/1a006fef19a215d7ef74c71a210fd511727f95bd))
## [2.0.4](https://github.com/payloadcms/payload/compare/v2.0.3...v2.0.4) (2023-10-12)
### Bug Fixes
- API tab breadcrumbs and results indentation ([#3564](https://github.com/payloadcms/payload/issues/3564)) ([e0afeec](https://github.com/payloadcms/payload/commit/e0afeeca974d0a0cac7aca8e40f9436449c902b5))
- sticky sidebar ([#3563](https://github.com/payloadcms/payload/issues/3563)) ([76e306d](https://github.com/payloadcms/payload/commit/76e306ddd8aae45f03fd4715415d6a44501a9400))
- sidebar width when fields have long descriptions ([#3562](https://github.com/payloadcms/payload/issues/3562)) ([cfc78ed](https://github.com/payloadcms/payload/commit/cfc78ed4f58647769f651da5a952fed20cfb217f))
- row field margins ([#3558](https://github.com/payloadcms/payload/issues/3558)) ([6d9353b](https://github.com/payloadcms/payload/commit/6d9353b53f4197bae2b15ca95298a87d784c7e76))
- removes nested array field configs from array value ([#3549](https://github.com/payloadcms/payload/issues/3549)) ([af892ec](https://github.com/payloadcms/payload/commit/af892ecb0e67777a97206bb5fccf489387ce68fc))
- [#3521](https://github.com/payloadcms/payload/issues/3521) ([eb97acd](https://github.com/payloadcms/payload/commit/eb97acd408f128438c2122ab6a6e2930f5b4a1ca))
- [#3540](https://github.com/payloadcms/payload/issues/3540) ([2567ac5](https://github.com/payloadcms/payload/commit/2567ac58bac851d0a15ee40db0f5f4737b199a75))
- row field width ([#3550](https://github.com/payloadcms/payload/issues/3550)) ([9ff014b](https://github.com/payloadcms/payload/commit/9ff014bbfe08d7b114c11824294f0d59f5f6c2c3))
- [#3541](https://github.com/payloadcms/payload/issues/3541) ([e6f0d35](https://github.com/payloadcms/payload/commit/e6f0d3598549a921e36f470adfcbacbaebaea53f))
- renders global label as page title ([#3532](https://github.com/payloadcms/payload/issues/3532)) ([ace3e57](https://github.com/payloadcms/payload/commit/ace3e577f6b1cbeb12860dc936c578c2a1f68570))
- increases document controls popup list button hitbox ([#3529](https://github.com/payloadcms/payload/issues/3529)) ([f009593](https://github.com/payloadcms/payload/commit/f0095937bafdd85c53c99bcc1d29d3361aa07238))
### Documentation
- removes MONGODB_URI ([8bfae6b93](https://github.com/payloadcms/payload/commit/8bfae6b932d6c9bd0c628a203ebf8d24121d66f8))
- adds build your own plugin page ([#3184](https://github.com/payloadcms/payload/pull/3184)) ([15f650afd](https://github.com/payloadcms/payload/commit/15f650afdef717d62c162846fec77aa0f326bb43))
## [2.0.3](https://github.com/payloadcms/payload/compare/v2.0.2...v2.0.3) (2023-10-09)
### Bug Fixes
- webpack export default was not found [#3494](https://github.com/payloadcms/payload/issues/3494) ([be049ce](https://github.com/payloadcms/payload/commit/be049cea0029cf497822e337777b8a86b7d38bed))
- postgres generated type id [#3504](https://github.com/payloadcms/payload/issues/3504) ([c90d1fa](https://github.com/payloadcms/payload/commit/c90d1faa7fedd5d902949089fd457c56eed4643d))
- hasMany relationships unable to be cleared [#3513](https://github.com/payloadcms/payload/issues/3513) ([5d9384f](https://github.com/payloadcms/payload/commit/5d9384f53052c96403d8c07ae9d05edf3676c4ef))
### Documentation
- move payload script mention to top of migrations ([26967fb92](https://github.com/payloadcms/payload/commit/26967fb92))
- payload script in package.json ([2f86c196e](https://github.com/payloadcms/payload/commit/2f86c196e))
- updates required node version ([70e068b18](https://github.com/payloadcms/payload/commit/70e068b18))
- improves custom views (#3499) ([6c17222a6](https://github.com/payloadcms/payload/commit/6c17222a6))
## [2.0.2](https://github.com/payloadcms/payload/compare/v2.0.1...v2.0.2) (2023-10-09)
### Bug Fixes
- beforeOperation hooks now correctly only run once ([e5d6a75](https://github.com/payloadcms/payload/commit/e5d6a75449acce2e53820a65386f1af78ff1317b))
## [2.0.1](https://github.com/payloadcms/payload/compare/v2.0.0...v2.0.1) (2023-10-09)
### Bug Fixes
- fix: richtext adapter types (#3497) ([7679e3f0a](https://github.com/payloadcms/payload/commit/7679e3f0aa351832ca933a3978d5931c47375e8b))
### Documentation
- remove --save flag for install command ([f7c35df6d](https://github.com/payloadcms/payload/commit/f7c35df6de6817ef33837f60951cd2812431fec7))
- updates live preview docs ([ca97f692c](https://github.com/payloadcms/payload/commit/ca97f692c3d470e658e417daf29213b2b2b49e11))
- fixes label for rich text overview ([7df1256bf](https://github.com/payloadcms/payload/commit/7df1256bf61daa911089d308cf7c0532d524c9c6))
## [2.0.0](https://github.com/payloadcms/payload/releases/tag/v2.0.0) (2023-10-09) ## [2.0.0](https://github.com/payloadcms/payload/releases/tag/v2.0.0) (2023-10-09)
### Features ### Features
@@ -123,7 +239,7 @@ To avoid any issues, you can pass the `req.transactionID` through to your Local
### ⚠️ Locales now have more functionality, and in some places, you might need to update custom code ### ⚠️ Locales now have more functionality, and in some places, you might need to update custom code
Payload's locales have become more powerful and now allow you to customize more aspects per locale such as a human-friendly label and if the locale is RTL or not. Payload's locales have become more powerful and now allow you to customize more aspects per locale such as a human-friendly label and if the locale is RTL or not.
This means that certain functions now return a different shape, such as `useLocale`. This hook used to return a string of the locale code you are currently editing, but it now returns an object with type of `Locale`. This means that certain functions now return a different shape, such as `useLocale`. This hook used to return a string of the locale code you are currently editing, but it now returns an object with type of `Locale`.
@@ -218,7 +334,7 @@ To pass connection options for MongoDB, you now need to pass them to `db: mongoo
### ⚠️ Some types have changed locations ### ⚠️ Some types have changed locations
If you are importing types from Payload, some of their locations may have changed. An example would be Slate-specific types being no longer exported from Payload itself—they are now exported from the `@payloadcms/richtext-slate` package. If you are importing types from Payload, some of their locations may have changed. An example would be Slate-specific types being no longer exported from Payload itself—they are now exported from the `@payloadcms/richtext-slate` package.
### Recap ### Recap

View File

@@ -129,7 +129,7 @@ To add a _new_ view to the Admin Panel, simply add another key to the `views` ob
} }
``` ```
_For more examples regarding how to customize components, look at the following [examples](https://github.com/payloadcms/payload/tree/master/test/admin/components)._ _For more examples regarding how to customize components, look at the following [examples](https://github.com/payloadcms/payload/tree/main/test/admin/components)._
For help on how to build your own custom view components, see [building a custom view component](#building-a-custom-view-component). For help on how to build your own custom view components, see [building a custom view component](#building-a-custom-view-component).
@@ -399,12 +399,12 @@ Your custom view components will be given all the props that a React Router `<Ro
#### Example #### Example
You can find examples of custom views in the [Payload source code `/test/admin/components/views` folder](https://github.com/payloadcms/payload/tree/master/test/admin/components/views). There, you'll find two custom views: You can find examples of custom views in the [Payload source code `/test/admin/components/views` folder](https://github.com/payloadcms/payload/tree/main/test/admin/components/views). There, you'll find two custom views:
1. A custom view that uses the `DefaultTemplate`, which is the built-in Payload template that displays the sidebar and "eyebrow nav" 1. A custom view that uses the `DefaultTemplate`, which is the built-in Payload template that displays the sidebar and "eyebrow nav"
1. A custom view that uses the `MinimalTemplate` - which is just a centered template used for things like logging in or out 1. A custom view that uses the `MinimalTemplate` - which is just a centered template used for things like logging in or out
To see how to pass in your custom views to create custom views of your own, take a look at the `admin.components.views` property of the [Payload test admin config](https://github.com/payloadcms/payload/blob/master/test/admin/config.ts). To see how to pass in your custom views to create custom views of your own, take a look at the `admin.components.views` property of the [Payload test admin config](https://github.com/payloadcms/payload/blob/main/test/admin/config.ts).
### Fields ### Fields

View File

@@ -11,19 +11,23 @@ keywords: documentation, getting started, guide, Content Management System, cms,
title="Payload Introduction - Closing the Gap Between Headless CMS and Application Frameworks" title="Payload Introduction - Closing the Gap Between Headless CMS and Application Frameworks"
/> />
Payload is a headless CMS and application framework. It's meant to provide a massive boost to your
development process, but importantly, stay out of your way as your apps get more complex.
<Banner type="success"> <Banner type="success">
Payload is a headless CMS and application framework. Its meant to provide a massive boost to your <strong>Payload 2.0 has been released!</strong>
development process, but importantly, stay out of your way as your apps get more complex. <br />
Includes Postgres support, Live Preview, Lexical Editor, and more. <a href="/blog/payload-2-0">Read the announcement</a>.
</Banner> </Banner>
Out of the box, Payload gives you a lot of the things that you often need when developing a new website, web app, or native app: Out of the box, Payload gives you a lot of the things that you often need when developing a new website, web app, or native app:
- A MongoDB database to store your data - A database to store your data (Postgres and MongoDB supported)
- A way to store, retrieve, and manipulate data of any shape via full REST and GraphQL APIs - A way to store, retrieve, and manipulate data of any shape via full REST and GraphQL APIs
- Authentication—complete with commonly required functionality like registration, email verification, login, & password reset - Authentication—complete with commonly required functionality like registration, email verification, login, & password reset
- Deep access control to your data, based on document or field-level functions - Deep access control to your data, based on document or field-level functions
- File storage and access control - File storage and access control
- A beautiful admin UI thats generated specifically to suit your data - A beautiful admin UI that's generated specifically to suit your data
## What does "headless" mean? ## What does "headless" mean?

View File

@@ -131,6 +131,7 @@ const result = await payload.find({
depth: 2, depth: 2,
page: 1, page: 1,
limit: 10, limit: 10,
pagination: false, // If you want to disable pagination count, etc.
where: {}, // pass a `where` query here where: {}, // pass a `where` query here
sort: '-title', sort: '-title',
locale: 'en', locale: 'en',

View File

@@ -59,3 +59,7 @@ All Payload APIs support the pagination controls below. With them, you can creat
| ------- | --------------------------------------- | | ------- | --------------------------------------- |
| `limit` | Limits the number of documents returned | | `limit` | Limits the number of documents returned |
| `page` | Get a specific page number | | `page` | Get a specific page number |
### Disabling pagination within Local API
For `find` operations within the Local API, you can disable pagination to retrieve all documents from a collection by passing `pagination: false` to the `find` local operation. This is not supported in REST or GraphQL, however, because it could potentially lead to malicious activity.

View File

@@ -18,6 +18,32 @@ payload generate:types
You can run this command whenever you need to regenerate your types, and then you can use these types in your Payload code directly. You can run this command whenever you need to regenerate your types, and then you can use these types in your Payload code directly.
### Disable declare statement
By default, `generate:types` will add a `declare` statement to your types file, which automatically enables type inference within Payload.
If you are using your `payload-types.ts` file in other repos, though, it might be better to disable this `declare` statement, so that you don't get any TS errors in projects that use your Payload types, but do not have Payload installed.
```ts
// payload.config.ts
{
// ...
typescript: {
declare: false, // defaults to true if not set
},
}
```
If you do disable the `declare` pattern, you'll need to manually add a `declare` statement to your code in order for Payload types to be recognized. Here's an example showing how to declare your types in your `payload.config.ts` file:
```ts
import { Config } from './payload-types'
declare module 'payload' {
export interface GeneratedTypes extends Config {}
}
```
### Custom output file path ### Custom output file path
You can specify where you want your types to be generated by adding a property to your Payload config: You can specify where you want your types to be generated by adding a property to your Payload config:

View File

@@ -40,20 +40,21 @@ Every Payload Collection can opt-in to supporting Uploads by specifying the `upl
#### Collection Upload Options #### Collection Upload Options
| Option | Description | | Option | Description |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`staticURL`** \* | The URL path to use to access your uploads. Relative path like `/media` will be served by payload. Full path like `https://example.com/media` needs to be served by another web server. | | **`staticURL`** \* | The URL path to use to access your uploads. Relative path like `/media` will be served by payload. Full path like `https://example.com/media` needs to be served by another web server. |
| **`staticDir`** \* | The folder directory to use to store media in. Can be either an absolute path or relative to the directory that contains your config. | | **`staticDir`** \* | The folder directory to use to store media in. Can be either an absolute path or relative to the directory that contains your config. |
| **`adminThumbnail`** | Set the way that the Admin panel will display thumbnails for this Collection. [More](#admin-thumbnails) | | **`adminThumbnail`** | Set the way that the Admin panel will display thumbnails for this Collection. [More](#admin-thumbnails) |
| **`crop`** | Set to `false` to disable the cropping tool in the Admin panel. Crop is enabled by default. [More](#crop-and-focal-point-selector) | | **`crop`** | Set to `false` to disable the cropping tool in the Admin panel. Crop is enabled by default. [More](#crop-and-focal-point-selector) |
| **`disableLocalStorage`** | Completely disable uploading files to disk locally. [More](#disabling-local-upload-storage) | | **`disableLocalStorage`** | Completely disable uploading files to disk locally. [More](#disabling-local-upload-storage) |
| **`focalPoint`** | Set to `false` to disable the focal point selection tool in the Admin panel. The focal point selector is only available when `imageSizes` or `resizeOptions` are defined. [More](#crop-and-focal-point-selector) | | **`focalPoint`** | Set to `false` to disable the focal point selection tool in the Admin panel. The focal point selector is only available when `imageSizes` or `resizeOptions` are defined. [More](#crop-and-focal-point-selector) |
| **`formatOptions`** | An object with `format` and `options` that are used with the Sharp image library to format the upload file. [More](https://sharp.pixelplumbing.com/api-output#toformat) | | **`formatOptions`** | An object with `format` and `options` that are used with the Sharp image library to format the upload file. [More](https://sharp.pixelplumbing.com/api-output#toformat) |
| **`handlers`** | Array of Express request handlers to execute before the built-in Payload static middleware executes. | | **`handlers`** | Array of Express request handlers to execute before the built-in Payload static middleware executes. |
| **`imageSizes`** | If specified, image uploads will be automatically resized in accordance to these image sizes. [More](#image-sizes) | | **`imageSizes`** | If specified, image uploads will be automatically resized in accordance to these image sizes. [More](#image-sizes) |
| **`mimeTypes`** | Restrict mimeTypes in the file picker. Array of valid mimetypes or mimetype wildcards [More](#mimetypes) | | **`mimeTypes`** | Restrict mimeTypes in the file picker. Array of valid mimetypes or mimetype wildcards [More](#mimetypes) |
| **`staticOptions`** | Set options for `express.static` to use while serving your static files. [More](http://expressjs.com/en/resources/middleware/serve-static.html) format) | | **`staticOptions`** | Set options for `express.static` to use while serving your static files. [More](http://expressjs.com/en/resources/middleware/serve-static.html) format) |
| **`resizeOptions`** | An object passed to the the Sharp image library to resize the uploaded file. [More](https://sharp.pixelplumbing.com/api-resize) | | **`resizeOptions`** | An object passed to the the Sharp image library to resize the uploaded file. [More](https://sharp.pixelplumbing.com/api-resize) |
| **`filesRequiredOnCreate`** | Mandate file data on creation, default is true. |
_An asterisk denotes that a property above is required._ _An asterisk denotes that a property above is required._

View File

@@ -17,9 +17,9 @@
"lint:fix": "eslint --fix --ext .ts,.tsx src" "lint:fix": "eslint --fix --ext .ts,.tsx src"
}, },
"dependencies": { "dependencies": {
"@payloadcms/bundler-webpack": "^1.0.0-beta.5", "@payloadcms/bundler-webpack": "latest",
"@payloadcms/db-mongodb": "^1.0.0-beta.8", "@payloadcms/db-mongodb": "latest",
"@payloadcms/richtext-slate": "^1.0.0-beta.4", "@payloadcms/richtext-slate": "latest",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"express": "^4.17.1", "express": "^4.17.1",
"payload": "latest" "payload": "latest"

File diff suppressed because it is too large Load Diff

View File

@@ -20,9 +20,9 @@
"lint:fix": "eslint --fix --ext .ts,.tsx src" "lint:fix": "eslint --fix --ext .ts,.tsx src"
}, },
"dependencies": { "dependencies": {
"@payloadcms/bundler-webpack": "^1.0.0-beta.5", "@payloadcms/bundler-webpack": "latest",
"@payloadcms/db-mongodb": "^1.0.0-beta.8", "@payloadcms/db-mongodb": "latest",
"@payloadcms/richtext-slate": "^1.0.0-beta.4", "@payloadcms/richtext-slate": "latest",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"express": "^4.17.1", "express": "^4.17.1",

File diff suppressed because it is too large Load Diff

View File

@@ -18,9 +18,9 @@
"lint:fix": "eslint --fix --ext .ts,.tsx src" "lint:fix": "eslint --fix --ext .ts,.tsx src"
}, },
"dependencies": { "dependencies": {
"@payloadcms/bundler-webpack": "^1.0.0-beta.5", "@payloadcms/bundler-webpack": "latest",
"@payloadcms/db-mongodb": "^1.0.0-beta.8", "@payloadcms/db-mongodb": "latest",
"@payloadcms/richtext-slate": "^1.0.0-beta.4", "@payloadcms/richtext-slate": "latest",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"express": "^4.17.1", "express": "^4.17.1",
"payload": "latest" "payload": "latest"

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
# Form Builder Example Front-End # Form Builder Example Front-End
This is a [Next.js](https://nextjs.org) app using the [Pages Router](https://nextjs.org/docs/pages). It was made explicitly for Payload's [Form Builder Example](https://github.com/payloadcms/payload/tree/master/examples/form-builder/payload). This is a [Next.js](https://nextjs.org) app using the [Pages Router](https://nextjs.org/docs/pages). It was made explicitly for Payload's [Form Builder Example](https://github.com/payloadcms/payload/tree/main/examples/form-builder/payload).
> This example uses the Pages Router, the legacy API of Next.js. If your app is using the latest [App Router](https://nextjs.org/docs/app), we will add an example for that soon. > This example uses the Pages Router, the legacy API of Next.js. If your app is using the latest [App Router](https://nextjs.org/docs/app), we will add an example for that soon.
@@ -8,7 +8,7 @@ This is a [Next.js](https://nextjs.org) app using the [Pages Router](https://nex
### Payload ### Payload
First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/master/examples/form-builder/payload). If you have not done so already, clone it down and follow the setup instructions there. First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/main/examples/form-builder/payload). If you have not done so already, clone it down and follow the setup instructions there.
### Next.js App ### Next.js App
@@ -18,7 +18,7 @@ First you'll need a running Payload app. There is one made explicitly for this e
4. `yarn dev` or `npm run dev` to start the server 4. `yarn dev` or `npm run dev` to start the server
5. `open http://localhost:3001` to see the result 5. `open http://localhost:3001` to see the result
Once running you will find a couple seeded pages on your local environment with some basic instructions. You can also start editing the pages by modifying the documents within Payload. See the [Form Builder Example](https://github.com/payloadcms/payload/tree/master/examples/form-builder/payload) for full details. Once running you will find a couple seeded pages on your local environment with some basic instructions. You can also start editing the pages by modifying the documents within Payload. See the [Form Builder Example](https://github.com/payloadcms/payload/tree/main/examples/form-builder/payload) for full details.
## Learn More ## Learn More

View File

@@ -15,9 +15,9 @@
"generate:graphQLSchema": "PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:graphQLSchema" "generate:graphQLSchema": "PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:graphQLSchema"
}, },
"dependencies": { "dependencies": {
"@payloadcms/bundler-webpack": "^1.0.0-beta.5", "@payloadcms/bundler-webpack": "latest",
"@payloadcms/db-mongodb": "^1.0.0-beta.8", "@payloadcms/db-mongodb": "latest",
"@payloadcms/richtext-slate": "^1.0.0-beta.4", "@payloadcms/richtext-slate": "latest",
"@faceless-ui/modal": "^2.0.1", "@faceless-ui/modal": "^2.0.1",
"@payloadcms/plugin-form-builder": "^1.0.12", "@payloadcms/plugin-form-builder": "^1.0.12",
"@payloadcms/plugin-seo": "^1.0.8", "@payloadcms/plugin-seo": "^1.0.8",

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,14 @@
# Payload Live Preview Example Front-End # Payload Live Preview Example Front-End
This is a [Next.js](https://nextjs.org) app using the [App Router](https://nextjs.org/docs/app). It was made explicitly for Payload's [Live Preview Example](https://github.com/payloadcms/payload/tree/master/examples/live-preview/payload). This is a [Next.js](https://nextjs.org) app using the [App Router](https://nextjs.org/docs/app). It was made explicitly for Payload's [Live Preview Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload).
> This example uses the App Router, the latest API of Next.js. If your app is using the legacy [Pages Router](https://nextjs.org/docs/pages), check out the official [Pages Router Example](https://github.com/payloadcms/payload/tree/master/examples/live-preview/next-pages). > This example uses the App Router, the latest API of Next.js. If your app is using the legacy [Pages Router](https://nextjs.org/docs/pages), check out the official [Pages Router Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/next-pages).
## Getting Started ## Getting Started
### Payload ### Payload
First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/master/examples/live-preview/payload). If you have not done so already, clone it down and follow the setup instructions there. This will provide all the necessary APIs that your Next.js app requires for authentication. First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload). If you have not done so already, clone it down and follow the setup instructions there. This will provide all the necessary APIs that your Next.js app requires for authentication.
### Next.js ### Next.js
@@ -18,7 +18,7 @@ First you'll need a running Payload app. There is one made explicitly for this e
4. `yarn dev` or `npm run dev` to start the server 4. `yarn dev` or `npm run dev` to start the server
5. `open http://localhost:3001` to see the result 5. `open http://localhost:3001` to see the result
Once running you will find a couple seeded pages on your local environment with some basic instructions. You can also start editing the pages by modifying the documents within Payload. See the [Live Preview Example](https://github.com/payloadcms/payload/tree/master/examples/live-preview/payload) for full details. Once running you will find a couple seeded pages on your local environment with some basic instructions. You can also start editing the pages by modifying the documents within Payload. See the [Live Preview Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload) for full details.
## Learn More ## Learn More
@@ -32,6 +32,6 @@ You can check out [the Payload GitHub repository](https://github.com/payloadcms/
## Deployment ## Deployment
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new) from the creators of Next.js. You could also combine this app into a [single Express server](https://github.com/payloadcms/payload/tree/master/examples/custom-server) and deploy in to [Payload Cloud](https://payloadcms.com/new/import). The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new) from the creators of Next.js. You could also combine this app into a [single Express server](https://github.com/payloadcms/payload/tree/main/examples/custom-server) and deploy in to [Payload Cloud](https://payloadcms.com/new/import).
Check out our [Payload deployment documentation](https://payloadcms.com/docs/production/deployment) or the [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. Check out our [Payload deployment documentation](https://payloadcms.com/docs/production/deployment) or the [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

View File

@@ -9,7 +9,7 @@
"lint": "next lint" "lint": "next lint"
}, },
"dependencies": { "dependencies": {
"@payloadcms/live-preview-react": "^1.0.0-beta.3", "@payloadcms/live-preview-react": "latest",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"next": "^13.4.8", "next": "^13.4.8",
"payload-admin-bar": "^1.0.6", "payload-admin-bar": "^1.0.6",

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,14 @@
# Payload Live Preview Example Front-End # Payload Live Preview Example Front-End
This is a [Next.js](https://nextjs.org) app using the [Pages Router](https://nextjs.org/docs/pages). It was made explicitly for Payload's [Live Preview Example](https://github.com/payloadcms/payload/tree/master/examples/live-preview/payload). This is a [Next.js](https://nextjs.org) app using the [Pages Router](https://nextjs.org/docs/pages). It was made explicitly for Payload's [Live Preview Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload).
> This example uses the Pages Router, the legacy API of Next.js. If your app is using the latest [App Router](https://nextjs.org/docs/app), check out the official [App Router Example](https://github.com/payloadcms/payload/tree/master/examples/live-preview/next-app). > This example uses the Pages Router, the legacy API of Next.js. If your app is using the latest [App Router](https://nextjs.org/docs/app), check out the official [App Router Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/next-app).
## Getting Started ## Getting Started
### Payload ### Payload
First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/master/examples/live-preview/payload). If you have not done so already, clone it down and follow the setup instructions there. This will provide all the necessary APIs that your Next.js app requires for authentication. First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload). If you have not done so already, clone it down and follow the setup instructions there. This will provide all the necessary APIs that your Next.js app requires for authentication.
### Next.js ### Next.js
@@ -18,7 +18,7 @@ First you'll need a running Payload app. There is one made explicitly for this e
4. `yarn dev` or `npm run dev` to start the server 4. `yarn dev` or `npm run dev` to start the server
5. `open http://localhost:3001` to see the result 5. `open http://localhost:3001` to see the result
Once running you will find a couple seeded pages on your local environment with some basic instructions. You can also start editing the pages by modifying the documents within Payload. See the [Live Preview Example](https://github.com/payloadcms/payload/tree/master/examples/live-preview/payload) for full details. Once running you will find a couple seeded pages on your local environment with some basic instructions. You can also start editing the pages by modifying the documents within Payload. See the [Live Preview Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload) for full details.
## Learn More ## Learn More
@@ -32,6 +32,6 @@ You can check out [the Payload GitHub repository](https://github.com/payloadcms/
## Deployment ## Deployment
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new) from the creators of Next.js. You could also combine this app into a [single Express server](https://github.com/payloadcms/payload/tree/master/examples/custom-server) and deploy in to [Payload Cloud](https://payloadcms.com/new/import). The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new) from the creators of Next.js. You could also combine this app into a [single Express server](https://github.com/payloadcms/payload/tree/main/examples/custom-server) and deploy in to [Payload Cloud](https://payloadcms.com/new/import).
Check out our [Payload deployment documentation](https://payloadcms.com/docs/production/deployment) or the [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. Check out our [Payload deployment documentation](https://payloadcms.com/docs/production/deployment) or the [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

View File

@@ -9,7 +9,7 @@
"lint": "next lint" "lint": "next lint"
}, },
"dependencies": { "dependencies": {
"@payloadcms/live-preview-react": "^1.0.0-beta.3", "@payloadcms/live-preview-react": "latest",
"@types/escape-html": "^1.0.2", "@types/escape-html": "^1.0.2",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"next": "^13.4.8", "next": "^13.4.8",

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
# Payload Live Preview Example # Payload Live Preview Example
The [Payload Live Preview Example](https://github.com/payloadcms/payload/tree/master/examples/live-preview/payload) demonstrates how to implement [Live Preview](https://payloadcms.com/docs/live-preview) in [Payload](https://github.com/payloadcms/payload). With Live Preview you can render your front-end application directly within the Admin panel. As you type, your changes take effect in real-time. No need to save a draft or publish your changes. The [Payload Live Preview Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload) demonstrates how to implement [Live Preview](https://payloadcms.com/docs/live-preview) in [Payload](https://github.com/payloadcms/payload). With Live Preview you can render your front-end application directly within the Admin panel. As you type, your changes take effect in real-time. No need to save a draft or publish your changes.
There are various fully working front-ends made explicitly for this example, including: There are various fully working front-ends made explicitly for this example, including:
@@ -40,7 +40,7 @@ See the [Collections](https://payloadcms.com/docs/configuration/collections) doc
} }
``` ```
For additional help with authentication, see the [Authentication](https://payloadcms.com/docs/authentication/overview#authentication-overview) docs or the official [Auth Example](https://github.com/payloadcms/payload/tree/master/examples/auth). For additional help with authentication, see the [Authentication](https://payloadcms.com/docs/authentication/overview#authentication-overview) docs or the official [Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth).
- #### Pages - #### Pages

View File

@@ -18,9 +18,9 @@
"lint:fix": "eslint --fix --ext .ts,.tsx src" "lint:fix": "eslint --fix --ext .ts,.tsx src"
}, },
"dependencies": { "dependencies": {
"@payloadcms/bundler-webpack": "^1.0.0-beta.5", "@payloadcms/bundler-webpack": "latest",
"@payloadcms/db-mongodb": "^1.0.0-beta.8", "@payloadcms/db-mongodb": "latest",
"@payloadcms/richtext-slate": "^1.0.0-beta.4", "@payloadcms/richtext-slate": "latest",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"express": "^4.17.1", "express": "^4.17.1",
"payload": "latest" "payload": "latest"

File diff suppressed because it is too large Load Diff

View File

@@ -18,9 +18,9 @@
"lint:fix": "eslint --fix --ext .ts,.tsx src" "lint:fix": "eslint --fix --ext .ts,.tsx src"
}, },
"dependencies": { "dependencies": {
"@payloadcms/bundler-webpack": "^1.0.0-beta.5", "@payloadcms/bundler-webpack": "latest",
"@payloadcms/db-mongodb": "^1.0.0-beta.8", "@payloadcms/db-mongodb": "latest",
"@payloadcms/richtext-slate": "^1.0.0-beta.4", "@payloadcms/richtext-slate": "latest",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"express": "^4.17.1", "express": "^4.17.1",
"payload": "latest" "payload": "latest"

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
# Redirects Example Front-End # Redirects Example Front-End
This is a [Next.js](https://nextjs.org) app using the [Pages Router](https://nextjs.org/docs/pages). It was made explicitly for Payload's [Redirects Example](https://github.com/payloadcms/payload/tree/master/examples/redireects/payload). This is a [Next.js](https://nextjs.org) app using the [Pages Router](https://nextjs.org/docs/pages). It was made explicitly for Payload's [Redirects Example](https://github.com/payloadcms/payload/tree/main/examples/redireects/payload).
> This example uses the Pages Router, the legacy API of Next.js. If your app is using the latest [App Router](https://nextjs.org/docs/app), we will soon add a new example for you to use soon. > This example uses the Pages Router, the legacy API of Next.js. If your app is using the latest [App Router](https://nextjs.org/docs/app), we will soon add a new example for you to use soon.
@@ -8,7 +8,7 @@ This is a [Next.js](https://nextjs.org) app using the [Pages Router](https://nex
### Payload ### Payload
First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/master/examples/redirects/payload). If you have not done so already, clone it down and follow the setup instructions there. First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/main/examples/redirects/payload). If you have not done so already, clone it down and follow the setup instructions there.
### Next.js App ### Next.js App

View File

@@ -17,9 +17,9 @@
"lint:fix": "eslint --fix --ext .ts,.tsx src" "lint:fix": "eslint --fix --ext .ts,.tsx src"
}, },
"dependencies": { "dependencies": {
"@payloadcms/bundler-webpack": "^1.0.0-beta.5", "@payloadcms/bundler-webpack": "latest",
"@payloadcms/db-mongodb": "^1.0.0-beta.8", "@payloadcms/db-mongodb": "latest",
"@payloadcms/richtext-slate": "^1.0.0-beta.4", "@payloadcms/richtext-slate": "latest",
"@payloadcms/plugin-redirects": "^1.0.0", "@payloadcms/plugin-redirects": "^1.0.0",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"express": "^4.17.1", "express": "^4.17.1",

File diff suppressed because it is too large Load Diff

View File

@@ -496,6 +496,16 @@
"@jridgewell/trace-mapping" "^0.3.17" "@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1" jsesc "^2.5.1"
"@babel/generator@^7.23.0":
version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420"
integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==
dependencies:
"@babel/types" "^7.23.0"
"@jridgewell/gen-mapping" "^0.3.2"
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1"
"@babel/helper-compilation-targets@^7.22.15": "@babel/helper-compilation-targets@^7.22.15":
version "7.22.15" version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52"
@@ -507,18 +517,23 @@
lru-cache "^5.1.1" lru-cache "^5.1.1"
semver "^6.3.1" semver "^6.3.1"
"@babel/helper-environment-visitor@^7.22.20":
version "7.22.20"
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167"
integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
"@babel/helper-environment-visitor@^7.22.5": "@babel/helper-environment-visitor@^7.22.5":
version "7.22.5" version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98"
integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==
"@babel/helper-function-name@^7.22.5": "@babel/helper-function-name@^7.23.0":
version "7.22.5" version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759"
integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
dependencies: dependencies:
"@babel/template" "^7.22.5" "@babel/template" "^7.22.15"
"@babel/types" "^7.22.5" "@babel/types" "^7.23.0"
"@babel/helper-hoist-variables@^7.22.5": "@babel/helper-hoist-variables@^7.22.5":
version "7.22.5" version "7.22.5"
@@ -574,6 +589,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz#601fa28e4cc06786c18912dca138cec73b882044" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz#601fa28e4cc06786c18912dca138cec73b882044"
integrity sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ== integrity sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==
"@babel/helper-validator-identifier@^7.22.20":
version "7.22.20"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
"@babel/helper-validator-option@^7.22.15": "@babel/helper-validator-option@^7.22.15":
version "7.22.15" version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040"
@@ -602,6 +622,11 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.16.tgz#180aead7f247305cce6551bea2720934e2fa2c95" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.16.tgz#180aead7f247305cce6551bea2720934e2fa2c95"
integrity sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA== integrity sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==
"@babel/parser@^7.23.0":
version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
"@babel/plugin-syntax-async-generators@^7.8.4": "@babel/plugin-syntax-async-generators@^7.8.4":
version "7.8.4" version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
@@ -707,7 +732,7 @@
dependencies: dependencies:
regenerator-runtime "^0.14.0" regenerator-runtime "^0.14.0"
"@babel/template@^7.22.15", "@babel/template@^7.22.5", "@babel/template@^7.3.3": "@babel/template@^7.22.15", "@babel/template@^7.3.3":
version "7.22.15" version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
@@ -717,18 +742,18 @@
"@babel/types" "^7.22.15" "@babel/types" "^7.22.15"
"@babel/traverse@^7.22.15", "@babel/traverse@^7.22.17": "@babel/traverse@^7.22.15", "@babel/traverse@^7.22.17":
version "7.22.17" version "7.23.2"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.17.tgz#b23c203ab3707e3be816043081b4a994fcacec44" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8"
integrity sha512-xK4Uwm0JnAMvxYZxOVecss85WxTEIbTa7bnGyf/+EgCL5Zt3U7htUpEOWv9detPlamGKuRzCqw74xVglDWpPdg== integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==
dependencies: dependencies:
"@babel/code-frame" "^7.22.13" "@babel/code-frame" "^7.22.13"
"@babel/generator" "^7.22.15" "@babel/generator" "^7.23.0"
"@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-environment-visitor" "^7.22.20"
"@babel/helper-function-name" "^7.22.5" "@babel/helper-function-name" "^7.23.0"
"@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-hoist-variables" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.22.6" "@babel/helper-split-export-declaration" "^7.22.6"
"@babel/parser" "^7.22.16" "@babel/parser" "^7.23.0"
"@babel/types" "^7.22.17" "@babel/types" "^7.23.0"
debug "^4.1.0" debug "^4.1.0"
globals "^11.1.0" globals "^11.1.0"
@@ -741,6 +766,15 @@
"@babel/helper-validator-identifier" "^7.22.15" "@babel/helper-validator-identifier" "^7.22.15"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@babel/types@^7.23.0":
version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb"
integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==
dependencies:
"@babel/helper-string-parser" "^7.22.5"
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"
"@bcherny/json-schema-ref-parser@9.0.9": "@bcherny/json-schema-ref-parser@9.0.9":
version "9.0.9" version "9.0.9"
resolved "https://registry.yarnpkg.com/@bcherny/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz#09899d405bc708c0acac0066ae8db5b94d465ca4" resolved "https://registry.yarnpkg.com/@bcherny/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz#09899d405bc708c0acac0066ae8db5b94d465ca4"

View File

@@ -20,8 +20,8 @@
"lint-staged": "lint-staged", "lint-staged": "lint-staged",
"pretest": "pnpm build", "pretest": "pnpm build",
"reinstall": "pnpm clean:unix && pnpm install", "reinstall": "pnpm clean:unix && pnpm install",
"list:packages": "./scripts/list_published_packages.sh beta", "script:list-packages": "tsx ./scripts/list-packages.ts",
"script:release:beta": "./scripts/release_beta.sh", "script:release": "tsx ./scripts/release.ts",
"test": "pnpm test:int && pnpm test:components && pnpm test:e2e", "test": "pnpm test:int && pnpm test:components && pnpm test:e2e",
"test:components": "cross-env jest --config=jest.components.config.js", "test:components": "cross-env jest --config=jest.components.config.js",
"test:e2e": "npx playwright install --with-deps && ts-node -T ./test/runE2E.ts", "test:e2e": "npx playwright install --with-deps && ts-node -T ./test/runE2E.ts",
@@ -47,6 +47,7 @@
"@types/prompts": "^2.4.5", "@types/prompts": "^2.4.5",
"@types/qs": "6.9.7", "@types/qs": "6.9.7",
"@types/react": "18.2.15", "@types/react": "18.2.15",
"@types/semver": "^7.5.3",
"@types/shelljs": "0.8.12", "@types/shelljs": "0.8.12",
"@types/testing-library__jest-dom": "5.14.8", "@types/testing-library__jest-dom": "5.14.8",
"chalk": "^5.3.0", "chalk": "^5.3.0",
@@ -59,6 +60,7 @@
"fs-extra": "10.1.0", "fs-extra": "10.1.0",
"get-port": "5.1.1", "get-port": "5.1.1",
"glob": "8.1.0", "glob": "8.1.0",
"graphql-request": "6.1.0",
"husky": "^8.0.3", "husky": "^8.0.3",
"isomorphic-fetch": "3.0.0", "isomorphic-fetch": "3.0.0",
"jest": "29.6.4", "jest": "29.6.4",
@@ -73,6 +75,7 @@
"prompts": "2.4.2", "prompts": "2.4.2",
"qs": "6.11.2", "qs": "6.11.2",
"rimraf": "3.0.2", "rimraf": "3.0.2",
"semver": "^7.5.4",
"shelljs": "0.8.5", "shelljs": "0.8.5",
"simple-git": "^3.20.0", "simple-git": "^3.20.0",
"slash": "3.0.0", "slash": "3.0.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@payloadcms/bundler-vite", "name": "@payloadcms/bundler-vite",
"version": "0.1.2", "version": "0.1.3",
"description": "The officially supported Vite bundler adapter for Payload", "description": "The officially supported Vite bundler adapter for Payload",
"repository": "https://github.com/payloadcms/payload", "repository": "https://github.com/payloadcms/payload",
"license": "MIT", "license": "MIT",
@@ -36,6 +36,7 @@
"payload": "workspace:*" "payload": "workspace:*"
}, },
"peerDependencies": { "peerDependencies": {
"payload": "^2.0.0",
"react-dom": "18.2.0" "react-dom": "18.2.0"
}, },
"publishConfig": { "publishConfig": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@payloadcms/bundler-webpack", "name": "@payloadcms/bundler-webpack",
"version": "1.0.3", "version": "1.0.4",
"description": "The officially supported Webpack bundler adapter for Payload", "description": "The officially supported Webpack bundler adapter for Payload",
"repository": "https://github.com/payloadcms/payload", "repository": "https://github.com/payloadcms/payload",
"license": "MIT", "license": "MIT",
@@ -58,6 +58,9 @@
"@types/webpack-hot-middleware": "2.25.6", "@types/webpack-hot-middleware": "2.25.6",
"payload": "workspace:*" "payload": "workspace:*"
}, },
"peerDependencies": {
"payload": "^2.0.0"
},
"publishConfig": { "publishConfig": {
"main": "./dist/index.js", "main": "./dist/index.js",
"registry": "https://registry.npmjs.org/", "registry": "https://registry.npmjs.org/",

View File

@@ -2,6 +2,7 @@ import type { SanitizedConfig } from 'payload/config'
import type { Configuration } from 'webpack' import type { Configuration } from 'webpack'
import findNodeModules from 'find-node-modules' import findNodeModules from 'find-node-modules'
import fs from 'fs'
import HtmlWebpackPlugin from 'html-webpack-plugin' import HtmlWebpackPlugin from 'html-webpack-plugin'
import path from 'path' import path from 'path'
import webpack from 'webpack' import webpack from 'webpack'
@@ -9,78 +10,94 @@ import webpack from 'webpack'
const mockModulePath = path.resolve(__dirname, '../mocks/emptyModule.js') const mockModulePath = path.resolve(__dirname, '../mocks/emptyModule.js')
const mockDotENVPath = path.resolve(__dirname, '../mocks/dotENV.js') const mockDotENVPath = path.resolve(__dirname, '../mocks/dotENV.js')
const nodeModulesPaths = findNodeModules({ cwd: process.cwd() }) const nodeModulesPaths = findNodeModules({ cwd: process.cwd(), relative: false })
const nodeModulesPath = path.resolve(nodeModulesPaths[0])
const adminFolderPath = path.resolve(nodeModulesPath, 'payload/dist/admin')
export const getBaseConfig = (payloadConfig: SanitizedConfig): Configuration => ({ export const getBaseConfig = (payloadConfig: SanitizedConfig): Configuration => {
entry: { let nodeModulesPath = nodeModulesPaths.find((p) => {
main: [adminFolderPath], const guess = path.resolve(p, 'payload/dist/admin')
}, if (fs.existsSync(guess)) {
module: { return true
rules: [ }
{ return false
exclude: /\/node_modules\/(?!.+\.tsx?$).*$/, })
test: /\.(t|j)sx?$/,
use: [ if (!nodeModulesPath) {
{ nodeModulesPath = process.cwd()
loader: require.resolve('swc-loader'), }
options: {
jsc: { const adminFolderPath = path.resolve(nodeModulesPath, 'payload/dist/admin')
parser: {
syntax: 'typescript', const config: Configuration = {
tsx: true, entry: {
main: [adminFolderPath],
},
module: {
rules: [
{
exclude: /\/node_modules\/(?!.+\.tsx?$).*$/,
test: /\.(t|j)sx?$/,
use: [
{
loader: require.resolve('swc-loader'),
options: {
jsc: {
parser: {
syntax: 'typescript',
tsx: true,
},
}, },
}, },
}, },
}, ],
], },
}, {
{ oneOf: [
oneOf: [ {
{ test: /\.(?:ico|gif|png|jpg|jpeg|woff(2)?|eot|ttf|otf|svg)$/i,
test: /\.(?:ico|gif|png|jpg|jpeg|woff(2)?|eot|ttf|otf|svg)$/i, type: 'asset/resource',
type: 'asset/resource', },
}, ],
], },
}, ],
], },
}, plugins: [
plugins: [ new webpack.ProvidePlugin({ process: require.resolve('process/browser') }),
new webpack.ProvidePlugin({ process: require.resolve('process/browser') }), new webpack.DefinePlugin(
new webpack.DefinePlugin( Object.entries(process.env).reduce((values, [key, val]) => {
Object.entries(process.env).reduce((values, [key, val]) => { if (key.indexOf('PAYLOAD_PUBLIC_') === 0) {
if (key.indexOf('PAYLOAD_PUBLIC_') === 0) { return {
return { ...values,
...values, [`process.env.${key}`]: `'${val}'`,
[`process.env.${key}`]: `'${val}'`, }
} }
}
return values return values
}, {}), }, {}),
), ),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
filename: path.normalize('./index.html'), filename: path.normalize('./index.html'),
template: payloadConfig.admin.indexHTML, template: payloadConfig.admin.indexHTML,
}), }),
new webpack.HotModuleReplacementPlugin(), new webpack.HotModuleReplacementPlugin(),
], ],
resolve: { resolve: {
alias: { alias: {
'@payloadcms/bundler-webpack': mockModulePath, '@payloadcms/bundler-webpack': mockModulePath,
dotenv: mockDotENVPath, dotenv: mockDotENVPath,
path: require.resolve('path-browserify'), path: require.resolve('path-browserify'),
payload$: mockModulePath, payload$: mockModulePath,
'payload-config': payloadConfig.paths.rawConfig, 'payload-config': payloadConfig.paths.rawConfig,
'payload-user-css': payloadConfig.admin.css, 'payload-user-css': payloadConfig.admin.css,
},
extensions: ['.ts', '.tsx', '.js', '.json'],
fallback: {
crypto: false,
http: false,
https: false,
},
modules: ['node_modules', nodeModulesPath, path.resolve(__dirname, '../../node_modules')],
}, },
extensions: ['.ts', '.tsx', '.js', '.json'], }
fallback: {
crypto: false, return config
http: false, }
https: false,
},
modules: ['node_modules', nodeModulesPath, path.resolve(__dirname, '../../node_modules')],
},
})

View File

@@ -1 +1,3 @@
export const webpackBundler = () => {} export const webpackBundler = () => {}
export default () => {}

View File

@@ -0,0 +1,15 @@
{
"$schema": "https://json.schemastore.org/swcrc",
"sourceMaps": "inline",
"jsc": {
"target": "esnext",
"parser": {
"syntax": "typescript",
"tsx": true,
"dts": true
}
},
"module": {
"type": "commonjs"
}
}

View File

@@ -1,18 +1,14 @@
{ {
"name": "create-payload-app", "name": "create-payload-app",
"version": "0.5.2", "version": "1.0.0",
"license": "MIT", "license": "MIT",
"bin": { "bin": {
"create-payload-app": "bin/cli.js" "create-payload-app": "bin/cli.js"
}, },
"scripts": { "scripts": {
"build": "tsc && pnpm copyfiles", "build": "pnpm build:swc",
"copyfiles": "copyfiles -u 1 \"src/templates/**\" \"src/lib/common-files/**\" dist", "build:swc": "swc ./src -d ./dist --config-file .swcrc",
"clean": "rimraf dist", "clean": "rimraf dist",
"typecheck": "tsc --noEmit",
"lint": "eslint \"src/**/*.ts\"",
"lint:fix": "eslint \"src/**/*.ts\" --fix",
"lint-staged": "lint-staged --quiet",
"test": "jest", "test": "jest",
"prepublishOnly": "pnpm test && pnpm clean && pnpm build" "prepublishOnly": "pnpm test && pnpm clean && pnpm build"
}, },

View File

@@ -4,6 +4,7 @@ import type { BundlerType, CliArgs, DbType, ProjectTemplate } from '../types'
import { createProject } from './create-project' import { createProject } from './create-project'
import { bundlerPackages, dbPackages, editorPackages } from './packages' import { bundlerPackages, dbPackages, editorPackages } from './packages'
import exp from 'constants' import exp from 'constants'
import { getValidTemplates } from './templates'
const projectDir = path.resolve(__dirname, './tmp') const projectDir = path.resolve(__dirname, './tmp')
describe('createProject', () => { describe('createProject', () => {
@@ -78,17 +79,20 @@ describe('createProject', () => {
}) })
describe('db adapters and bundlers', () => { describe('db adapters and bundlers', () => {
const templates = getValidTemplates()
it.each([ it.each([
['mongodb', 'webpack'], ['blank', 'mongodb', 'webpack'],
['postgres', 'webpack'], ['blank', 'postgres', 'webpack'],
])('update config and deps: %s, %s', async (db, bundler) => { ['website', 'mongodb', 'webpack'],
['website', 'postgres', 'webpack'],
['ecommerce', 'mongodb', 'webpack'],
['ecommerce', 'postgres', 'webpack'],
])('update config and deps: %s, %s, %s', async (templateName, db, bundler) => {
const projectName = 'starter-project' const projectName = 'starter-project'
const template: ProjectTemplate = {
name: 'blank', const template = templates.find((t) => t.name === templateName)
type: 'starter',
url: 'https://github.com/payloadcms/payload/templates/blank',
description: 'Blank Template',
}
await createProject({ await createProject({
cliArgs: args, cliArgs: args,
projectName, projectName,
@@ -124,7 +128,12 @@ describe('createProject', () => {
editorReplacement.version, editorReplacement.version,
) )
const payloadConfigPath = path.resolve(projectDir, 'src/payload.config.ts') let payloadConfigPath = path.resolve(projectDir, 'src/payload.config.ts')
// Website and ecommerce templates have payload.config.ts in src/payload
if (!fse.existsSync(payloadConfigPath)) {
payloadConfigPath = path.resolve(projectDir, 'src/payload/payload.config.ts')
}
const content = fse.readFileSync(payloadConfigPath, 'utf-8') const content = fse.readFileSync(payloadConfigPath, 'utf-8')
// Check payload.config.ts // Check payload.config.ts

View File

@@ -19,6 +19,5 @@
"src/**/*.spec.ts", "src/**/*.spec.ts",
"src/**/*.spec.tsx" "src/**/*.spec.tsx"
], ],
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.json"], "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.json"]
"references": [{ "path": "../payload" }]
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@payloadcms/db-mongodb", "name": "@payloadcms/db-mongodb",
"version": "1.0.3", "version": "1.0.4",
"description": "The officially supported MongoDB database adapter for Payload", "description": "The officially supported MongoDB database adapter for Payload",
"repository": "https://github.com/payloadcms/payload", "repository": "https://github.com/payloadcms/payload",
"license": "MIT", "license": "MIT",
@@ -35,6 +35,9 @@
"mongodb-memory-server": "8.13.0", "mongodb-memory-server": "8.13.0",
"payload": "workspace:*" "payload": "workspace:*"
}, },
"peerDependencies": {
"payload": "^2.0.0"
},
"publishConfig": { "publishConfig": {
"main": "./dist/index.js", "main": "./dist/index.js",
"registry": "https://registry.npmjs.org/", "registry": "https://registry.npmjs.org/",

View File

@@ -52,7 +52,7 @@ export const init: Init = async function init(this: MongooseAdapter) {
const model = mongoose.model( const model = mongoose.model(
versionModelName, versionModelName,
versionSchema, versionSchema,
versionModelName, this.autoPluralization === true ? undefined : versionModelName,
) as CollectionModel ) as CollectionModel
// this.payload.versions[collection.slug] = model; // this.payload.versions[collection.slug] = model;
this.versions[collection.slug] = model this.versions[collection.slug] = model

View File

@@ -1,6 +1,6 @@
{ {
"name": "@payloadcms/db-postgres", "name": "@payloadcms/db-postgres",
"version": "0.1.4", "version": "0.1.10",
"description": "The officially supported Postgres database adapter for Payload", "description": "The officially supported Postgres database adapter for Payload",
"repository": "https://github.com/payloadcms/payload", "repository": "https://github.com/payloadcms/payload",
"license": "MIT", "license": "MIT",
@@ -35,7 +35,7 @@
"payload": "workspace:*" "payload": "workspace:*"
}, },
"peerDependencies": { "peerDependencies": {
"better-sqlite3": "^8.5.0" "payload": "^2.0.0"
}, },
"publishConfig": { "publishConfig": {
"main": "./dist/index.js", "main": "./dist/index.js",

View File

@@ -1,6 +1,5 @@
import type { Connect } from 'payload/database' import type { Connect } from 'payload/database'
import { pushSchema } from 'drizzle-kit/utils'
import { eq, sql } from 'drizzle-orm' import { eq, sql } from 'drizzle-orm'
import { drizzle } from 'drizzle-orm/node-postgres' import { drizzle } from 'drizzle-orm/node-postgres'
import { numeric, pgTable, timestamp, varchar } from 'drizzle-orm/pg-core' import { numeric, pgTable, timestamp, varchar } from 'drizzle-orm/pg-core'
@@ -40,6 +39,8 @@ export const connect: Connect = async function connect(this: PostgresAdapter, pa
) )
return return
const { pushSchema } = require('drizzle-kit/utils')
// This will prompt if clarifications are needed for Drizzle to push new schema // This will prompt if clarifications are needed for Drizzle to push new schema
const { apply, hasDataLoss, statementsToExecute, warnings } = await pushSchema( const { apply, hasDataLoss, statementsToExecute, warnings } = await pushSchema(
this.schema, this.schema,

View File

@@ -2,7 +2,6 @@
import type { DrizzleSnapshotJSON } from 'drizzle-kit/utils' import type { DrizzleSnapshotJSON } from 'drizzle-kit/utils'
import type { CreateMigration } from 'payload/database' import type { CreateMigration } from 'payload/database'
import { generateDrizzleJson, generateMigration } from 'drizzle-kit/utils'
import fs from 'fs' import fs from 'fs'
import prompts from 'prompts' import prompts from 'prompts'
@@ -61,6 +60,8 @@ export const createMigration: CreateMigration = async function createMigration(
fs.mkdirSync(dir) fs.mkdirSync(dir)
} }
const { generateDrizzleJson, generateMigration } = require('drizzle-kit/utils')
const [yyymmdd, hhmmss] = new Date().toISOString().split('T') const [yyymmdd, hhmmss] = new Date().toISOString().split('T')
const formattedDate = yyymmdd.replace(/\D/g, '') const formattedDate = yyymmdd.replace(/\D/g, '')
const formattedTime = hhmmss.split('.')[0].replace(/\D/g, '') const formattedTime = hhmmss.split('.')[0].replace(/\D/g, '')

View File

@@ -33,7 +33,7 @@ export const findMany = async function find({
const db = adapter.sessions[req.transactionID]?.db || adapter.drizzle const db = adapter.sessions[req.transactionID]?.db || adapter.drizzle
const table = adapter.tables[tableName] const table = adapter.tables[tableName]
let limit = limitArg let limit = limitArg ?? 10
let totalDocs: number let totalDocs: number
let totalPages: number let totalPages: number
let hasPrevPage: boolean let hasPrevPage: boolean
@@ -119,7 +119,11 @@ export const findMany = async function find({
findManyArgs.where = inArray(adapter.tables[tableName].id, Object.keys(orderedIDMap)) findManyArgs.where = inArray(adapter.tables[tableName].id, Object.keys(orderedIDMap))
} else { } else {
findManyArgs.limit = limitArg === 0 ? undefined : limitArg findManyArgs.limit = limitArg === 0 ? undefined : limitArg
findManyArgs.offset = skip || (page - 1) * limitArg
const offset = skip || (page - 1) * limitArg
if (!Number.isNaN(offset)) findManyArgs.offset = offset
if (where) { if (where) {
findManyArgs.where = where findManyArgs.where = where
} }

View File

@@ -128,7 +128,7 @@ export const traverseFields = ({
with: {}, with: {},
} }
if (adapter.tables[`${topLevelTableName}_${toSnakeCase(block.slug)}_locales`]) if (adapter.tables[`${topLevelTableName}_blocks_${toSnakeCase(block.slug)}_locales`])
withBlock.with._locales = _locales withBlock.with._locales = _locales
topLevelArgs.with[blockKey] = withBlock topLevelArgs.with[blockKey] = withBlock
@@ -139,7 +139,7 @@ export const traverseFields = ({
currentTableName, currentTableName,
depth, depth,
fields: block.fields, fields: block.fields,
path, path: '',
topLevelArgs, topLevelArgs,
topLevelTableName, topLevelTableName,
}) })

View File

@@ -2,9 +2,7 @@
import type { Payload } from 'payload' import type { Payload } from 'payload'
import type { Migration } from 'payload/database' import type { Migration } from 'payload/database'
import { generateDrizzleJson } from 'drizzle-kit/utils'
import { readMigrationFiles } from 'payload/database' import { readMigrationFiles } from 'payload/database'
import { DatabaseError } from 'pg'
import prompts from 'prompts' import prompts from 'prompts'
import type { PostgresAdapter } from './types' import type { PostgresAdapter } from './types'
@@ -78,6 +76,8 @@ export async function migrate(this: PostgresAdapter): Promise<void> {
} }
async function runMigrationFile(payload: Payload, migration: Migration, batch: number) { async function runMigrationFile(payload: Payload, migration: Migration, batch: number) {
const { generateDrizzleJson } = require('drizzle-kit/utils')
const start = Date.now() const start = Date.now()
payload.logger.info({ msg: `Migrating: ${migration.name}` }) payload.logger.info({ msg: `Migrating: ${migration.name}` })

View File

@@ -34,12 +34,14 @@ type Args = {
aliasTable?: GenericTable aliasTable?: GenericTable
collectionPath: string collectionPath: string
columnPrefix?: string columnPrefix?: string
constraintPath?: string
constraints?: Constraint[] constraints?: Constraint[]
fields: (Field | TabAsField)[] fields: (Field | TabAsField)[]
joinAliases: BuildQueryJoinAliases joinAliases: BuildQueryJoinAliases
joins: BuildQueryJoins joins: BuildQueryJoins
locale?: string locale?: string
pathSegments: string[] pathSegments: string[]
rootTableName?: string
selectFields: Record<string, GenericColumn> selectFields: Record<string, GenericColumn>
tableName: string tableName: string
} }
@@ -53,17 +55,22 @@ export const getTableColumnFromPath = ({
aliasTable, aliasTable,
collectionPath, collectionPath,
columnPrefix = '', columnPrefix = '',
constraintPath: incomingConstraintPath,
constraints = [], constraints = [],
fields, fields,
joinAliases, joinAliases,
joins, joins,
locale: incomingLocale, locale: incomingLocale,
pathSegments: incomingSegments, pathSegments: incomingSegments,
rootTableName: incomingRootTableName,
selectFields, selectFields,
tableName, tableName,
}: Args): TableColumn => { }: Args): TableColumn => {
const fieldPath = incomingSegments[0] const fieldPath = incomingSegments[0]
let locale = incomingLocale let locale = incomingLocale
const rootTableName = incomingRootTableName || tableName
let constraintPath = incomingConstraintPath || ''
const field = flattenTopLevelFields(fields as Field[]).find( const field = flattenTopLevelFields(fields as Field[]).find(
(fieldToFind) => fieldAffectsData(fieldToFind) && fieldToFind.name === fieldPath, (fieldToFind) => fieldAffectsData(fieldToFind) && fieldToFind.name === fieldPath,
) as Field | TabAsField ) as Field | TabAsField
@@ -105,6 +112,7 @@ export const getTableColumnFromPath = ({
aliasTable, aliasTable,
collectionPath, collectionPath,
columnPrefix, columnPrefix,
constraintPath,
constraints, constraints,
fields: field.tabs.map((tab) => ({ fields: field.tabs.map((tab) => ({
...tab, ...tab,
@@ -114,6 +122,7 @@ export const getTableColumnFromPath = ({
joins, joins,
locale, locale,
pathSegments: pathSegments.slice(1), pathSegments: pathSegments.slice(1),
rootTableName,
selectFields, selectFields,
tableName: newTableName, tableName: newTableName,
}) })
@@ -125,12 +134,14 @@ export const getTableColumnFromPath = ({
aliasTable, aliasTable,
collectionPath, collectionPath,
columnPrefix: `${columnPrefix}${field.name}_`, columnPrefix: `${columnPrefix}${field.name}_`,
constraintPath,
constraints, constraints,
fields: field.fields, fields: field.fields,
joinAliases, joinAliases,
joins, joins,
locale, locale,
pathSegments: pathSegments.slice(1), pathSegments: pathSegments.slice(1),
rootTableName,
selectFields, selectFields,
tableName: newTableName, tableName: newTableName,
}) })
@@ -140,12 +151,14 @@ export const getTableColumnFromPath = ({
aliasTable, aliasTable,
collectionPath, collectionPath,
columnPrefix, columnPrefix,
constraintPath,
constraints, constraints,
fields: field.fields, fields: field.fields,
joinAliases, joinAliases,
joins, joins,
locale, locale,
pathSegments: pathSegments.slice(1), pathSegments: pathSegments.slice(1),
rootTableName,
selectFields, selectFields,
tableName: newTableName, tableName: newTableName,
}) })
@@ -172,12 +185,14 @@ export const getTableColumnFromPath = ({
aliasTable, aliasTable,
collectionPath, collectionPath,
columnPrefix: `${columnPrefix}${field.name}_`, columnPrefix: `${columnPrefix}${field.name}_`,
constraintPath,
constraints, constraints,
fields: field.fields, fields: field.fields,
joinAliases, joinAliases,
joins, joins,
locale, locale,
pathSegments: pathSegments.slice(1), pathSegments: pathSegments.slice(1),
rootTableName,
selectFields, selectFields,
tableName: newTableName, tableName: newTableName,
}) })
@@ -185,6 +200,7 @@ export const getTableColumnFromPath = ({
case 'array': { case 'array': {
newTableName = `${tableName}_${toSnakeCase(field.name)}` newTableName = `${tableName}_${toSnakeCase(field.name)}`
constraintPath = `${constraintPath}${field.name}.%.`
if (locale && field.localized && adapter.payload.config.localization) { if (locale && field.localized && adapter.payload.config.localization) {
joins[newTableName] = and( joins[newTableName] = and(
eq(adapter.tables[tableName].id, adapter.tables[newTableName]._parentID), eq(adapter.tables[tableName].id, adapter.tables[newTableName]._parentID),
@@ -206,12 +222,14 @@ export const getTableColumnFromPath = ({
return getTableColumnFromPath({ return getTableColumnFromPath({
adapter, adapter,
collectionPath, collectionPath,
constraintPath,
constraints, constraints,
fields: field.fields, fields: field.fields,
joinAliases, joinAliases,
joins, joins,
locale, locale,
pathSegments: pathSegments.slice(1), pathSegments: pathSegments.slice(1),
rootTableName,
selectFields, selectFields,
tableName: newTableName, tableName: newTableName,
}) })
@@ -229,12 +247,14 @@ export const getTableColumnFromPath = ({
result = getTableColumnFromPath({ result = getTableColumnFromPath({
adapter, adapter,
collectionPath, collectionPath,
constraintPath: '',
constraints: blockConstraints, constraints: blockConstraints,
fields: block.fields, fields: block.fields,
joinAliases, joinAliases,
joins, joins,
locale, locale,
pathSegments: pathSegments.slice(1), pathSegments: pathSegments.slice(1),
rootTableName,
selectFields: blockSelectFields, selectFields: blockSelectFields,
tableName: newTableName, tableName: newTableName,
}) })
@@ -283,9 +303,8 @@ export const getTableColumnFromPath = ({
case 'relationship': case 'relationship':
case 'upload': { case 'upload': {
let relationshipFields let relationshipFields
const relationTableName = `${tableName}_rels` const relationTableName = `${rootTableName}_rels`
const newCollectionPath = pathSegments.slice(1).join('.') const newCollectionPath = pathSegments.slice(1).join('.')
const aliasRelationshipTableName = uuid() const aliasRelationshipTableName = uuid()
const aliasRelationshipTable = alias( const aliasRelationshipTable = alias(
adapter.tables[relationTableName], adapter.tables[relationTableName],
@@ -295,7 +314,7 @@ export const getTableColumnFromPath = ({
// Join in the relationships table // Join in the relationships table
joinAliases.push({ joinAliases.push({
condition: eq( condition: eq(
(aliasTable || adapter.tables[tableName]).id, (aliasTable || adapter.tables[rootTableName]).id,
aliasRelationshipTable.parent, aliasRelationshipTable.parent,
), ),
table: aliasRelationshipTable, table: aliasRelationshipTable,
@@ -306,7 +325,7 @@ export const getTableColumnFromPath = ({
constraints.push({ constraints.push({
columnName: 'path', columnName: 'path',
table: aliasRelationshipTable, table: aliasRelationshipTable,
value: field.name, value: `${constraintPath}${field.name}`,
}) })
let newAliasTable let newAliasTable
@@ -368,6 +387,7 @@ export const getTableColumnFromPath = ({
joins, joins,
locale, locale,
pathSegments: pathSegments.slice(1), pathSegments: pathSegments.slice(1),
rootTableName: newTableName,
selectFields, selectFields,
tableName: newTableName, tableName: newTableName,
}) })

View File

@@ -100,7 +100,11 @@ export async function parseParams({
const val = where[relationOrPath][operator] const val = where[relationOrPath][operator]
queryConstraints.forEach(({ columnName: col, table: constraintTable, value }) => { queryConstraints.forEach(({ columnName: col, table: constraintTable, value }) => {
constraints.push(operatorMap.equals(constraintTable[col], value)) if (typeof value === 'string' && value.indexOf('%') > -1) {
constraints.push(operatorMap.like(constraintTable[col], value))
} else {
constraints.push(operatorMap.equals(constraintTable[col], value))
}
}) })
if (['json', 'richText'].includes(field.type) && Array.isArray(pathSegments)) { if (['json', 'richText'].includes(field.type) && Array.isArray(pathSegments)) {
@@ -144,13 +148,19 @@ export async function parseParams({
break break
} }
const { operator: queryOperator, value: queryValue } = sanitizeQueryValue({ const sanitizedQueryValue = sanitizeQueryValue({
field, field,
operator, operator,
relationOrPath, relationOrPath,
val, val,
}) })
if (sanitizedQueryValue === null) {
break
}
const { operator: queryOperator, value: queryValue } = sanitizedQueryValue
if (queryOperator === 'not_equals' && queryValue !== null) { if (queryOperator === 'not_equals' && queryValue !== null) {
constraints.push( constraints.push(
or( or(
@@ -159,7 +169,10 @@ export async function parseParams({
ne<any>(rawColumn || table[columnName], queryValue), ne<any>(rawColumn || table[columnName], queryValue),
), ),
) )
} else if ( break
}
if (
(field.type === 'relationship' || field.type === 'upload') && (field.type === 'relationship' || field.type === 'upload') &&
Array.isArray(queryValue) && Array.isArray(queryValue) &&
operator === 'not_in' operator === 'not_in'
@@ -170,11 +183,13 @@ export async function parseParams({
IS IS
NULL`, NULL`,
) )
} else {
constraints.push( break
operatorMap[queryOperator](rawColumn || table[columnName], queryValue),
)
} }
constraints.push(
operatorMap[queryOperator](rawColumn || table[columnName], queryValue),
)
} }
} }
} }

View File

@@ -42,11 +42,17 @@ export const sanitizeQueryValue = ({
if (val.toLowerCase() === 'false') formattedValue = false if (val.toLowerCase() === 'false') formattedValue = false
} }
if (['all', 'in', 'not_in'].includes(operator) && typeof formattedValue === 'string') { if (['all', 'in', 'not_in'].includes(operator)) {
formattedValue = createArrayFromCommaDelineated(formattedValue) if (typeof formattedValue === 'string') {
formattedValue = createArrayFromCommaDelineated(formattedValue)
if (field.type === 'number') { if (field.type === 'number') {
formattedValue = formattedValue.map((arrayVal) => parseFloat(arrayVal)) formattedValue = formattedValue.map((arrayVal) => parseFloat(arrayVal))
}
}
if (!Array.isArray(formattedValue) || formattedValue.length === 0) {
return null
} }
} }

View File

@@ -6,11 +6,12 @@
// drizzle-kit@utils // drizzle-kit@utils
import { generateDrizzleJson, generateMigration, pushSchema } from 'drizzle-kit/utils'
import { drizzle } from 'drizzle-orm/node-postgres' import { drizzle } from 'drizzle-orm/node-postgres'
import { Pool } from 'pg' import { Pool } from 'pg'
async function generateUsage() { async function generateUsage() {
const { generateDrizzleJson, generateMigration } = require('drizzle-kit/utils')
// @ts-expect-error Just TypeScript being broken // TODO: Open TypeScript issue // @ts-expect-error Just TypeScript being broken // TODO: Open TypeScript issue
const schema = await import('./data/users') const schema = await import('./data/users')
// @ts-expect-error Just TypeScript being broken // TODO: Open TypeScript issue // @ts-expect-error Just TypeScript being broken // TODO: Open TypeScript issue
@@ -25,6 +26,8 @@ async function generateUsage() {
} }
async function pushUsage() { async function pushUsage() {
const { pushSchema } = require('drizzle-kit/utils')
// @ts-expect-error Just TypeScript being broken // TODO: Open TypeScript issue // @ts-expect-error Just TypeScript being broken // TODO: Open TypeScript issue
const schemaAfter = await import('./data/users-after') const schemaAfter = await import('./data/users-after')

View File

@@ -0,0 +1,13 @@
import type { Field } from 'payload/types'
export const idToUUID = (fields: Field[]): Field[] =>
fields.map((field) => {
if ('name' in field && field.name === 'id') {
return {
...field,
name: '_uuid',
}
}
return field
})

View File

@@ -26,6 +26,7 @@ import type { GenericColumns, PostgresAdapter } from '../types'
import { hasLocalesTable } from '../utilities/hasLocalesTable' import { hasLocalesTable } from '../utilities/hasLocalesTable'
import { buildTable } from './build' import { buildTable } from './build'
import { createIndex } from './createIndex' import { createIndex } from './createIndex'
import { idToUUID } from './idToUUID'
import { parentIDColumnMap } from './parentIDColumnMap' import { parentIDColumnMap } from './parentIDColumnMap'
import { validateExistingBlockIsIdentical } from './validateExistingBlockIsIdentical' import { validateExistingBlockIsIdentical } from './validateExistingBlockIsIdentical'
@@ -252,6 +253,8 @@ export const traverseFields = ({
} }
case 'array': { case 'array': {
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
const arrayTableName = `${newTableName}_${toSnakeCase(field.name)}` const arrayTableName = `${newTableName}_${toSnakeCase(field.name)}`
const baseColumns: Record<string, PgColumnBuilder> = { const baseColumns: Record<string, PgColumnBuilder> = {
_order: integer('_order').notNull(), _order: integer('_order').notNull(),
@@ -277,9 +280,9 @@ export const traverseFields = ({
adapter, adapter,
baseColumns, baseColumns,
baseExtraConfig, baseExtraConfig,
disableNotNull, disableNotNull: disableNotNullFromHere,
disableUnique, disableUnique,
fields: field.fields, fields: disableUnique ? idToUUID(field.fields) : field.fields,
rootRelationsToBuild, rootRelationsToBuild,
rootRelationships: relationships, rootRelationships: relationships,
rootTableIDColType, rootTableIDColType,
@@ -314,6 +317,8 @@ export const traverseFields = ({
} }
case 'blocks': { case 'blocks': {
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
field.blocks.forEach((block) => { field.blocks.forEach((block) => {
const blockTableName = `${rootTableName}_blocks_${toSnakeCase(block.slug)}` const blockTableName = `${rootTableName}_blocks_${toSnakeCase(block.slug)}`
if (!adapter.tables[blockTableName]) { if (!adapter.tables[blockTableName]) {
@@ -343,9 +348,9 @@ export const traverseFields = ({
adapter, adapter,
baseColumns, baseColumns,
baseExtraConfig, baseExtraConfig,
disableNotNull, disableNotNull: disableNotNullFromHere,
disableUnique, disableUnique,
fields: block.fields, fields: disableUnique ? idToUUID(block.fields) : block.fields,
rootRelationsToBuild, rootRelationsToBuild,
rootRelationships: relationships, rootRelationships: relationships,
rootTableIDColType, rootTableIDColType,
@@ -428,6 +433,8 @@ export const traverseFields = ({
break break
} }
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
const { const {
hasLocalizedField: groupHasLocalizedField, hasLocalizedField: groupHasLocalizedField,
hasLocalizedManyNumberField: groupHasLocalizedManyNumberField, hasLocalizedManyNumberField: groupHasLocalizedManyNumberField,
@@ -438,7 +445,7 @@ export const traverseFields = ({
buildRelationships, buildRelationships,
columnPrefix: `${columnName}_`, columnPrefix: `${columnName}_`,
columns, columns,
disableNotNull, disableNotNull: disableNotNullFromHere,
disableUnique, disableUnique,
fieldPrefix: `${fieldName}_`, fieldPrefix: `${fieldName}_`,
fields: field.fields, fields: field.fields,
@@ -463,6 +470,8 @@ export const traverseFields = ({
} }
case 'tabs': { case 'tabs': {
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
const { const {
hasLocalizedField: tabHasLocalizedField, hasLocalizedField: tabHasLocalizedField,
hasLocalizedManyNumberField: tabHasLocalizedManyNumberField, hasLocalizedManyNumberField: tabHasLocalizedManyNumberField,
@@ -473,7 +482,7 @@ export const traverseFields = ({
buildRelationships, buildRelationships,
columnPrefix, columnPrefix,
columns, columns,
disableNotNull, disableNotNull: disableNotNullFromHere,
disableUnique, disableUnique,
fieldPrefix, fieldPrefix,
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })), fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
@@ -500,6 +509,7 @@ export const traverseFields = ({
case 'row': case 'row':
case 'collapsible': { case 'collapsible': {
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
const { const {
hasLocalizedField: rowHasLocalizedField, hasLocalizedField: rowHasLocalizedField,
hasLocalizedManyNumberField: rowHasLocalizedManyNumberField, hasLocalizedManyNumberField: rowHasLocalizedManyNumberField,
@@ -510,7 +520,7 @@ export const traverseFields = ({
buildRelationships, buildRelationships,
columnPrefix, columnPrefix,
columns, columns,
disableNotNull, disableNotNull: disableNotNullFromHere,
disableUnique, disableUnique,
fieldPrefix, fieldPrefix,
fields: field.fields, fields: field.fields,

View File

@@ -22,7 +22,7 @@ export const validateExistingBlockIsIdentical = ({
const fieldNames = flattenTopLevelFields(block.fields).flatMap((field) => field.name) const fieldNames = flattenTopLevelFields(block.fields).flatMap((field) => field.name)
Object.keys(table).forEach((fieldName) => { Object.keys(table).forEach((fieldName) => {
if (!['_locale', '_order', '_parentID', '_path'].includes(fieldName)) { if (!['_locale', '_order', '_parentID', '_path', '_uuid'].includes(fieldName)) {
if (fieldNames.indexOf(fieldName) === -1) { if (fieldNames.indexOf(fieldName) === -1) {
throw new InvalidConfiguration( throw new InvalidConfiguration(
`The table ${rootTableName} has multiple blocks with slug ${block.slug}, but the schemas do not match. One block includes the field ${fieldName}, while the other block does not.`, `The table ${rootTableName} has multiple blocks with slug ${block.slug}, but the schemas do not match. One block includes the field ${fieldName}, while the other block does not.`,

View File

@@ -31,6 +31,7 @@ export const transform = <T extends TypeWithID>({ config, data, fields }: Transf
} }
const blocks = createBlocksMap(data) const blocks = createBlocksMap(data)
const deletions = []
const result = traverseFields<T>({ const result = traverseFields<T>({
blocks, blocks,
@@ -38,6 +39,7 @@ export const transform = <T extends TypeWithID>({ config, data, fields }: Transf
dataRef: { dataRef: {
id: data.id, id: data.id,
}, },
deletions,
fieldPrefix: '', fieldPrefix: '',
fields, fields,
numbers, numbers,
@@ -46,5 +48,7 @@ export const transform = <T extends TypeWithID>({ config, data, fields }: Transf
table: data, table: data,
}) })
deletions.forEach((deletion) => deletion())
return result return result
} }

View File

@@ -22,6 +22,10 @@ type TraverseFieldsArgs = {
* The data reference to be mutated within this recursive function * The data reference to be mutated within this recursive function
*/ */
dataRef: Record<string, unknown> dataRef: Record<string, unknown>
/**
* Data that needs to be removed from the result after all fields have populated
*/
deletions: (() => void)[]
/** /**
* Column prefix can be built up by group and named tab fields * Column prefix can be built up by group and named tab fields
*/ */
@@ -54,6 +58,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
blocks, blocks,
config, config,
dataRef, dataRef,
deletions,
fieldPrefix, fieldPrefix,
fields, fields,
numbers, numbers,
@@ -69,6 +74,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
blocks, blocks,
config, config,
dataRef, dataRef,
deletions,
fieldPrefix, fieldPrefix,
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })), fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
numbers, numbers,
@@ -87,6 +93,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
blocks, blocks,
config, config,
dataRef, dataRef,
deletions,
fieldPrefix, fieldPrefix,
fields: field.fields, fields: field.fields,
numbers, numbers,
@@ -99,7 +106,6 @@ export const traverseFields = <T extends Record<string, unknown>>({
if (fieldAffectsData(field)) { if (fieldAffectsData(field)) {
const fieldName = `${fieldPrefix || ''}${field.name}` const fieldName = `${fieldPrefix || ''}${field.name}`
const fieldData = table[fieldName] const fieldData = table[fieldName]
if (field.type === 'array') { if (field.type === 'array') {
if (Array.isArray(fieldData)) { if (Array.isArray(fieldData)) {
if (field.localized) { if (field.localized) {
@@ -109,11 +115,16 @@ export const traverseFields = <T extends Record<string, unknown>>({
const locale = row._locale const locale = row._locale
const data = {} const data = {}
delete row._locale delete row._locale
if (row._uuid) {
row.id = row._uuid
delete row._uuid
}
const rowResult = traverseFields<T>({ const rowResult = traverseFields<T>({
blocks, blocks,
config, config,
dataRef: data, dataRef: data,
deletions,
fieldPrefix: '', fieldPrefix: '',
fields: field.fields, fields: field.fields,
numbers, numbers,
@@ -129,10 +140,15 @@ export const traverseFields = <T extends Record<string, unknown>>({
}, {}) }, {})
} else { } else {
result[field.name] = fieldData.map((row, i) => { result[field.name] = fieldData.map((row, i) => {
if (row._uuid) {
row.id = row._uuid
delete row._uuid
}
return traverseFields<T>({ return traverseFields<T>({
blocks, blocks,
config, config,
dataRef: row, dataRef: row,
deletions,
fieldPrefix: '', fieldPrefix: '',
fields: field.fields, fields: field.fields,
numbers, numbers,
@@ -155,6 +171,10 @@ export const traverseFields = <T extends Record<string, unknown>>({
result[field.name] = {} result[field.name] = {}
blocks[blockFieldPath].forEach((row) => { blocks[blockFieldPath].forEach((row) => {
if (row._uuid) {
row.id = row._uuid
delete row._uuid
}
if (typeof row._locale === 'string') { if (typeof row._locale === 'string') {
if (!result[field.name][row._locale]) result[field.name][row._locale] = [] if (!result[field.name][row._locale]) result[field.name][row._locale] = []
result[field.name][row._locale].push(row) result[field.name][row._locale].push(row)
@@ -171,6 +191,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
blocks, blocks,
config, config,
dataRef: row, dataRef: row,
deletions,
fieldPrefix: '', fieldPrefix: '',
fields: block.fields, fields: block.fields,
numbers, numbers,
@@ -189,6 +210,10 @@ export const traverseFields = <T extends Record<string, unknown>>({
} else { } else {
result[field.name] = blocks[blockFieldPath].map((row, i) => { result[field.name] = blocks[blockFieldPath].map((row, i) => {
delete row._order delete row._order
if (row._uuid) {
row.id = row._uuid
delete row._uuid
}
const block = field.blocks.find(({ slug }) => slug === row.blockType) const block = field.blocks.find(({ slug }) => slug === row.blockType)
if (block) { if (block) {
@@ -196,6 +221,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
blocks, blocks,
config, config,
dataRef: row, dataRef: row,
deletions,
fieldPrefix: '', fieldPrefix: '',
fields: block.fields, fields: block.fields,
numbers, numbers,
@@ -345,6 +371,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
blocks, blocks,
config, config,
dataRef: groupLocaleData as Record<string, unknown>, dataRef: groupLocaleData as Record<string, unknown>,
deletions,
fieldPrefix: groupFieldPrefix, fieldPrefix: groupFieldPrefix,
fields: field.fields, fields: field.fields,
numbers, numbers,
@@ -360,6 +387,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
blocks, blocks,
config, config,
dataRef: groupData as Record<string, unknown>, dataRef: groupData as Record<string, unknown>,
deletions,
fieldPrefix: groupFieldPrefix, fieldPrefix: groupFieldPrefix,
fields: field.fields, fields: field.fields,
numbers, numbers,
@@ -425,5 +453,9 @@ export const traverseFields = <T extends Record<string, unknown>>({
return result return result
}, dataRef) }, dataRef)
if (Array.isArray(table._locales)) {
deletions.push(() => delete table._locales)
}
return formatted as T return formatted as T
} }

View File

@@ -1,12 +1,14 @@
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
import type { ArrayField } from 'payload/types' import type { ArrayField } from 'payload/types'
import type { PostgresAdapter } from '../../types'
import type { ArrayRowToInsert, BlockRowToInsert, RelationshipToDelete } from './types' import type { ArrayRowToInsert, BlockRowToInsert, RelationshipToDelete } from './types'
import { isArrayOfRows } from '../../utilities/isArrayOfRows' import { isArrayOfRows } from '../../utilities/isArrayOfRows'
import { traverseFields } from './traverseFields' import { traverseFields } from './traverseFields'
type Args = { type Args = {
adapter: PostgresAdapter
arrayTableName: string arrayTableName: string
baseTableName: string baseTableName: string
blocks: { blocks: {
@@ -25,6 +27,7 @@ type Args = {
} }
export const transformArray = ({ export const transformArray = ({
adapter,
arrayTableName, arrayTableName,
baseTableName, baseTableName,
blocks, blocks,
@@ -38,6 +41,7 @@ export const transformArray = ({
selects, selects,
}: Args) => { }: Args) => {
const newRows: ArrayRowToInsert[] = [] const newRows: ArrayRowToInsert[] = []
const hasUUID = adapter.tables[arrayTableName]._uuid
if (isArrayOfRows(data)) { if (isArrayOfRows(data)) {
data.forEach((arrayRow, i) => { data.forEach((arrayRow, i) => {
@@ -49,6 +53,16 @@ export const transformArray = ({
}, },
} }
// If we have declared a _uuid field on arrays,
// that means the ID has to be unique,
// and our ids within arrays are not unique.
// So move the ID to a uuid field for storage
// and allow the database to generate a serial id automatically
if (hasUUID) {
newRow.row._uuid = arrayRow.id
delete arrayRow.id
}
if (locale) { if (locale) {
newRow.locales[locale] = { newRow.locales[locale] = {
_locale: locale, _locale: locale,
@@ -60,6 +74,7 @@ export const transformArray = ({
} }
traverseFields({ traverseFields({
adapter,
arrays: newRow.arrays, arrays: newRow.arrays,
baseTableName, baseTableName,
blocks, blocks,

View File

@@ -3,11 +3,13 @@ import type { BlockField } from 'payload/types'
import toSnakeCase from 'to-snake-case' import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from '../../types'
import type { BlockRowToInsert, RelationshipToDelete } from './types' import type { BlockRowToInsert, RelationshipToDelete } from './types'
import { traverseFields } from './traverseFields' import { traverseFields } from './traverseFields'
type Args = { type Args = {
adapter: PostgresAdapter
baseTableName: string baseTableName: string
blocks: { blocks: {
[blockType: string]: BlockRowToInsert[] [blockType: string]: BlockRowToInsert[]
@@ -24,6 +26,7 @@ type Args = {
} }
} }
export const transformBlocks = ({ export const transformBlocks = ({
adapter,
baseTableName, baseTableName,
blocks, blocks,
data, data,
@@ -56,7 +59,20 @@ export const transformBlocks = ({
const blockTableName = `${baseTableName}_blocks_${blockType}` const blockTableName = `${baseTableName}_blocks_${blockType}`
const hasUUID = adapter.tables[blockTableName]._uuid
// If we have declared a _uuid field on arrays,
// that means the ID has to be unique,
// and our ids within arrays are not unique.
// So move the ID to a uuid field for storage
// and allow the database to generate a serial id automatically
if (hasUUID) {
newRow.row._uuid = blockRow.id
delete blockRow.id
}
traverseFields({ traverseFields({
adapter,
arrays: newRow.arrays, arrays: newRow.arrays,
baseTableName, baseTableName,
blocks, blocks,

View File

@@ -1,18 +1,26 @@
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
import type { Field } from 'payload/types' import type { Field } from 'payload/types'
import type { PostgresAdapter } from '../../types'
import type { RowToInsert } from './types' import type { RowToInsert } from './types'
import { traverseFields } from './traverseFields' import { traverseFields } from './traverseFields'
type Args = { type Args = {
adapter: PostgresAdapter
data: Record<string, unknown> data: Record<string, unknown>
fields: Field[] fields: Field[]
path?: string path?: string
tableName: string tableName: string
} }
export const transformForWrite = ({ data, fields, path = '', tableName }: Args): RowToInsert => { export const transformForWrite = ({
adapter,
data,
fields,
path = '',
tableName,
}: Args): RowToInsert => {
// Split out the incoming data into rows to insert / delete // Split out the incoming data into rows to insert / delete
const rowToInsert: RowToInsert = { const rowToInsert: RowToInsert = {
arrays: {}, arrays: {},
@@ -28,6 +36,7 @@ export const transformForWrite = ({ data, fields, path = '', tableName }: Args):
// This function is responsible for building up the // This function is responsible for building up the
// above rowToInsert // above rowToInsert
traverseFields({ traverseFields({
adapter,
arrays: rowToInsert.arrays, arrays: rowToInsert.arrays,
baseTableName: tableName, baseTableName: tableName,
blocks: rowToInsert.blocks, blocks: rowToInsert.blocks,

View File

@@ -4,6 +4,7 @@ import type { Field } from 'payload/types'
import { fieldAffectsData } from 'payload/types' import { fieldAffectsData } from 'payload/types'
import toSnakeCase from 'to-snake-case' import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from '../../types'
import type { ArrayRowToInsert, BlockRowToInsert, RelationshipToDelete } from './types' import type { ArrayRowToInsert, BlockRowToInsert, RelationshipToDelete } from './types'
import { isArrayOfRows } from '../../utilities/isArrayOfRows' import { isArrayOfRows } from '../../utilities/isArrayOfRows'
@@ -14,6 +15,7 @@ import { transformRelationship } from './relationships'
import { transformSelects } from './selects' import { transformSelects } from './selects'
type Args = { type Args = {
adapter: PostgresAdapter
arrays: { arrays: {
[tableName: string]: ArrayRowToInsert[] [tableName: string]: ArrayRowToInsert[]
} }
@@ -56,6 +58,7 @@ type Args = {
} }
export const traverseFields = ({ export const traverseFields = ({
adapter,
arrays, arrays,
baseTableName, baseTableName,
blocks, blocks,
@@ -95,6 +98,7 @@ export const traverseFields = ({
Object.entries(data[field.name]).forEach(([localeKey, localeData]) => { Object.entries(data[field.name]).forEach(([localeKey, localeData]) => {
if (Array.isArray(localeData)) { if (Array.isArray(localeData)) {
const newRows = transformArray({ const newRows = transformArray({
adapter,
arrayTableName, arrayTableName,
baseTableName, baseTableName,
blocks, blocks,
@@ -114,6 +118,7 @@ export const traverseFields = ({
} }
} else { } else {
const newRows = transformArray({ const newRows = transformArray({
adapter,
arrayTableName, arrayTableName,
baseTableName, baseTableName,
blocks, blocks,
@@ -138,6 +143,7 @@ export const traverseFields = ({
Object.entries(data[field.name]).forEach(([localeKey, localeData]) => { Object.entries(data[field.name]).forEach(([localeKey, localeData]) => {
if (Array.isArray(localeData)) { if (Array.isArray(localeData)) {
transformBlocks({ transformBlocks({
adapter,
baseTableName, baseTableName,
blocks, blocks,
data: localeData, data: localeData,
@@ -154,6 +160,7 @@ export const traverseFields = ({
} }
} else if (isArrayOfRows(fieldData)) { } else if (isArrayOfRows(fieldData)) {
transformBlocks({ transformBlocks({
adapter,
baseTableName, baseTableName,
blocks, blocks,
data: fieldData, data: fieldData,
@@ -174,6 +181,7 @@ export const traverseFields = ({
if (field.localized) { if (field.localized) {
Object.entries(data[field.name]).forEach(([localeKey, localeData]) => { Object.entries(data[field.name]).forEach(([localeKey, localeData]) => {
traverseFields({ traverseFields({
adapter,
arrays, arrays,
baseTableName, baseTableName,
blocks, blocks,
@@ -195,6 +203,7 @@ export const traverseFields = ({
}) })
} else { } else {
traverseFields({ traverseFields({
adapter,
arrays, arrays,
baseTableName, baseTableName,
blocks, blocks,
@@ -225,6 +234,7 @@ export const traverseFields = ({
if (tab.localized) { if (tab.localized) {
Object.entries(data[tab.name]).forEach(([localeKey, localeData]) => { Object.entries(data[tab.name]).forEach(([localeKey, localeData]) => {
traverseFields({ traverseFields({
adapter,
arrays, arrays,
baseTableName, baseTableName,
blocks, blocks,
@@ -246,6 +256,7 @@ export const traverseFields = ({
}) })
} else { } else {
traverseFields({ traverseFields({
adapter,
arrays, arrays,
baseTableName, baseTableName,
blocks, blocks,
@@ -267,6 +278,7 @@ export const traverseFields = ({
} }
} else { } else {
traverseFields({ traverseFields({
adapter,
arrays, arrays,
baseTableName, baseTableName,
blocks, blocks,
@@ -290,6 +302,7 @@ export const traverseFields = ({
if (field.type === 'row' || field.type === 'collapsible') { if (field.type === 'row' || field.type === 'collapsible') {
traverseFields({ traverseFields({
adapter,
arrays, arrays,
baseTableName, baseTableName,
blocks, blocks,

View File

@@ -28,6 +28,7 @@ export const upsertRow = async <T extends TypeWithID>({
// Split out the incoming data into the corresponding: // Split out the incoming data into the corresponding:
// base row, locales, relationships, blocks, and arrays // base row, locales, relationships, blocks, and arrays
const rowToInsert = transformForWrite({ const rowToInsert = transformForWrite({
adapter,
data, data,
fields, fields,
path, path,
@@ -107,6 +108,9 @@ export const upsertRow = async <T extends TypeWithID>({
rowToInsert.blocks[blockName].forEach((blockRow) => { rowToInsert.blocks[blockName].forEach((blockRow) => {
blockRow.row._parentID = insertedRow.id blockRow.row._parentID = insertedRow.id
if (!blocksToInsert[blockName]) blocksToInsert[blockName] = [] if (!blocksToInsert[blockName]) blocksToInsert[blockName] = []
if (blockRow.row.uuid) {
delete blockRow.row.uuid
}
blocksToInsert[blockName].push(blockRow) blocksToInsert[blockName].push(blockRow)
}) })
}) })

View File

@@ -1,6 +1,6 @@
{ {
"name": "@payloadcms/eslint-config", "name": "@payloadcms/eslint-config",
"version": "0.0.1", "version": "1.0.0",
"description": "Payload styles for ESLint and Prettier", "description": "Payload styles for ESLint and Prettier",
"license": "MIT", "license": "MIT",
"author": { "author": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@payloadcms/live-preview-react", "name": "@payloadcms/live-preview-react",
"version": "0.1.2", "version": "0.1.4",
"description": "The official live preview React SDK for Payload", "description": "The official live preview React SDK for Payload",
"repository": "https://github.com/payloadcms/payload", "repository": "https://github.com/payloadcms/payload",
"license": "MIT", "license": "MIT",
@@ -17,7 +17,7 @@
"prepublishOnly": "pnpm clean && pnpm build" "prepublishOnly": "pnpm clean && pnpm build"
}, },
"dependencies": { "dependencies": {
"@payloadcms/live-preview": "workspace:*" "@payloadcms/live-preview": "workspace:^0.x"
}, },
"devDependencies": { "devDependencies": {
"@payloadcms/eslint-config": "workspace:*", "@payloadcms/eslint-config": "workspace:*",
@@ -25,6 +25,7 @@
"payload": "workspace:*" "payload": "workspace:*"
}, },
"peerDependencies": { "peerDependencies": {
"payload": "^2.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0" "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}, },
"exports": { "exports": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@payloadcms/live-preview", "name": "@payloadcms/live-preview",
"version": "0.1.2", "version": "0.1.4",
"description": "The official live preview JavaScript SDK for Payload", "description": "The official live preview JavaScript SDK for Payload",
"repository": "https://github.com/payloadcms/payload", "repository": "https://github.com/payloadcms/payload",
"license": "MIT", "license": "MIT",
@@ -20,6 +20,9 @@
"@payloadcms/eslint-config": "workspace:*", "@payloadcms/eslint-config": "workspace:*",
"payload": "workspace:*" "payload": "workspace:*"
}, },
"peerDependencies": {
"payload": "^2.0.0"
},
"exports": { "exports": {
".": { ".": {
"default": "./src/index.ts", "default": "./src/index.ts",

View File

@@ -1,5 +1,11 @@
import { mergeData } from '.' import { mergeData } from '.'
// For performance reasons, `fieldSchemaJSON` will only be sent once on the initial message
// We need to cache this value so that it can be used across subsequent messages
// To do this, save `fieldSchemaJSON` when it arrives as a global variable
// Send this cached value to `mergeData`, instead of `eventData.fieldSchemaJSON` directly
let payloadLivePreviewFieldSchema = undefined // TODO: type this from `fieldSchemaToJSON` return type
export const handleMessage = async <T>(args: { export const handleMessage = async <T>(args: {
depth: number depth: number
event: MessageEvent event: MessageEvent
@@ -11,9 +17,13 @@ export const handleMessage = async <T>(args: {
const eventData = JSON.parse(event?.data) const eventData = JSON.parse(event?.data)
if (eventData.type === 'payload-live-preview') { if (eventData.type === 'payload-live-preview') {
if (!payloadLivePreviewFieldSchema && eventData.fieldSchemaJSON) {
payloadLivePreviewFieldSchema = eventData.fieldSchemaJSON
}
const mergedData = await mergeData<T>({ const mergedData = await mergeData<T>({
depth, depth,
fieldSchema: eventData.fieldSchemaJSON, fieldSchema: payloadLivePreviewFieldSchema,
incomingData: eventData.data, incomingData: eventData.data,
initialData, initialData,
serverURL, serverURL,

View File

@@ -1,9 +1,11 @@
import type { fieldSchemaToJSON } from 'payload/utilities'
import { traverseFields } from './traverseFields' import { traverseFields } from './traverseFields'
export type MergeLiveDataArgs<T> = { export type MergeLiveDataArgs<T> = {
apiRoute?: string apiRoute?: string
depth: number depth: number
fieldSchema: Record<string, unknown>[] fieldSchema: ReturnType<typeof fieldSchemaToJSON>
incomingData: T incomingData: T
initialData: T initialData: T
serverURL: string serverURL: string

View File

@@ -2,11 +2,14 @@ export const ready = (args: { serverURL: string }): void => {
const { serverURL } = args const { serverURL } = args
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
// This subscription may have been from either an iframe `src` or `window.open()` // This subscription may have been from either an iframe or a popup
// i.e. `window?.opener` || `window?.parent` // We need to report 'ready' to the parent window, whichever it may be
window?.opener?.postMessage( // i.e. `window?.opener` for popups, `window?.parent` for iframes
const windowToPostTo: Window = window?.opener || window?.parent
windowToPostTo?.postMessage(
JSON.stringify({ JSON.stringify({
popupReady: true, ready: true,
type: 'payload-live-preview', type: 'payload-live-preview',
}), }),
serverURL, serverURL,

View File

@@ -9,7 +9,7 @@ export const subscribe = <T>(args: {
const { callback, depth, initialData, serverURL } = args const { callback, depth, initialData, serverURL } = args
const onMessage = async (event: MessageEvent) => { const onMessage = async (event: MessageEvent) => {
const mergedData = await handleMessage({ depth, event, initialData, serverURL }) const mergedData = await handleMessage<T>({ depth, event, initialData, serverURL })
callback(mergedData) callback(mergedData)
} }

View File

@@ -1,9 +1,11 @@
import type { fieldSchemaToJSON } from 'payload/utilities'
import { promise } from './promise' import { promise } from './promise'
type Args<T> = { type Args<T> = {
apiRoute?: string apiRoute?: string
depth: number depth: number
fieldSchema: Record<string, unknown>[] fieldSchema: ReturnType<typeof fieldSchemaToJSON>
incomingData: T incomingData: T
populationPromises: Promise<void>[] populationPromises: Promise<void>[]
result: T result: T
@@ -19,12 +21,11 @@ export const traverseFields = <T>({
result, result,
serverURL, serverURL,
}: Args<T>): void => { }: Args<T>): void => {
fieldSchema.forEach((field) => { fieldSchema.forEach((fieldJSON) => {
if ('name' in field && typeof field.name === 'string') { if ('name' in fieldJSON && typeof fieldJSON.name === 'string') {
// TODO: type this const fieldName = fieldJSON.name
const fieldName = field.name
switch (field.type) { switch (fieldJSON.type) {
case 'array': case 'array':
if (Array.isArray(incomingData[fieldName])) { if (Array.isArray(incomingData[fieldName])) {
result[fieldName] = incomingData[fieldName].map((row, i) => { result[fieldName] = incomingData[fieldName].map((row, i) => {
@@ -38,7 +39,7 @@ export const traverseFields = <T>({
traverseFields({ traverseFields({
apiRoute, apiRoute,
depth, depth,
fieldSchema: field.fields as Record<string, unknown>[], // TODO: type this fieldSchema: fieldJSON.fields,
incomingData: row, incomingData: row,
populationPromises, populationPromises,
result: newRow, result: newRow,
@@ -53,7 +54,7 @@ export const traverseFields = <T>({
case 'blocks': case 'blocks':
if (Array.isArray(incomingData[fieldName])) { if (Array.isArray(incomingData[fieldName])) {
result[fieldName] = incomingData[fieldName].map((row, i) => { result[fieldName] = incomingData[fieldName].map((row, i) => {
const matchedBlock = field.blocks[row.blockType] const matchedBlock = fieldJSON.blocks[row.blockType]
const hasExistingRow = const hasExistingRow =
Array.isArray(result[fieldName]) && Array.isArray(result[fieldName]) &&
@@ -70,7 +71,7 @@ export const traverseFields = <T>({
traverseFields({ traverseFields({
apiRoute, apiRoute,
depth, depth,
fieldSchema: matchedBlock.fields as Record<string, unknown>[], // TODO: type this fieldSchema: matchedBlock.fields,
incomingData: row, incomingData: row,
populationPromises, populationPromises,
result: newRow, result: newRow,
@@ -82,7 +83,7 @@ export const traverseFields = <T>({
} }
break break
case 'tab': case 'tabs':
case 'group': case 'group':
if (!result[fieldName]) { if (!result[fieldName]) {
result[fieldName] = {} result[fieldName] = {}
@@ -91,7 +92,7 @@ export const traverseFields = <T>({
traverseFields({ traverseFields({
apiRoute, apiRoute,
depth, depth,
fieldSchema: field.fields as Record<string, unknown>[], // TODO: type this fieldSchema: fieldJSON.fields,
incomingData: incomingData[fieldName] || {}, incomingData: incomingData[fieldName] || {},
populationPromises, populationPromises,
result: result[fieldName], result: result[fieldName],
@@ -102,7 +103,7 @@ export const traverseFields = <T>({
case 'upload': case 'upload':
case 'relationship': case 'relationship':
if (field.hasMany && Array.isArray(incomingData[fieldName])) { if (fieldJSON.hasMany && Array.isArray(incomingData[fieldName])) {
const existingValue = Array.isArray(result[fieldName]) ? [...result[fieldName]] : [] const existingValue = Array.isArray(result[fieldName]) ? [...result[fieldName]] : []
result[fieldName] = Array.isArray(result[fieldName]) result[fieldName] = Array.isArray(result[fieldName])
? [...result[fieldName]].slice(0, incomingData[fieldName].length) ? [...result[fieldName]].slice(0, incomingData[fieldName].length)
@@ -110,7 +111,7 @@ export const traverseFields = <T>({
incomingData[fieldName].forEach((relation, i) => { incomingData[fieldName].forEach((relation, i) => {
// Handle `hasMany` polymorphic // Handle `hasMany` polymorphic
if (Array.isArray(field.relationTo)) { if (Array.isArray(fieldJSON.relationTo)) {
const existingID = existingValue[i]?.value?.id const existingID = existingValue[i]?.value?.id
if ( if (
@@ -134,7 +135,7 @@ export const traverseFields = <T>({
) )
} }
} else { } else {
// Handle `hasMany` singular // Handle `hasMany` monomorphic
const existingID = existingValue[i]?.id const existingID = existingValue[i]?.id
if (existingID !== relation) { if (existingID !== relation) {
@@ -143,7 +144,7 @@ export const traverseFields = <T>({
id: relation, id: relation,
accessor: i, accessor: i,
apiRoute, apiRoute,
collection: String(field.relationTo), collection: String(fieldJSON.relationTo),
depth, depth,
ref: result[fieldName], ref: result[fieldName],
serverURL, serverURL,
@@ -154,7 +155,7 @@ export const traverseFields = <T>({
}) })
} else { } else {
// Handle `hasOne` polymorphic // Handle `hasOne` polymorphic
if (Array.isArray(field.relationTo)) { if (Array.isArray(fieldJSON.relationTo)) {
const hasNewValue = const hasNewValue =
typeof incomingData[fieldName] === 'object' && incomingData[fieldName] !== null typeof incomingData[fieldName] === 'object' && incomingData[fieldName] !== null
const hasOldValue = const hasOldValue =
@@ -190,31 +191,37 @@ export const traverseFields = <T>({
result[fieldName] = null result[fieldName] = null
} }
} else { } else {
const hasNewValue = // Handle `hasOne` monomorphic
typeof incomingData[fieldName] === 'object' && incomingData[fieldName] !== null const newID: string =
const hasOldValue = (typeof incomingData[fieldName] === 'string' && incomingData[fieldName]) ||
typeof result[fieldName] === 'object' && result[fieldName] !== null (typeof incomingData[fieldName] === 'object' &&
incomingData[fieldName] !== null &&
incomingData[fieldName].id) ||
''
const newValue = hasNewValue ? incomingData[fieldName].value : '' const oldID: string =
(typeof result[fieldName] === 'string' && result[fieldName]) ||
(typeof result[fieldName] === 'object' &&
result[fieldName] !== null &&
result[fieldName].id) ||
''
const oldValue = hasOldValue ? result[fieldName].value : '' if (newID !== oldID) {
if (newID) {
if (newValue !== oldValue) {
if (newValue) {
populationPromises.push( populationPromises.push(
promise({ promise({
id: newValue, id: newID,
accessor: fieldName, accessor: fieldName,
apiRoute, apiRoute,
collection: String(field.relationTo), collection: String(fieldJSON.relationTo),
depth, depth,
ref: result as Record<string, unknown>, ref: result as Record<string, unknown>,
serverURL, serverURL,
}), }),
) )
} else {
result[fieldName] = null
} }
} else {
result[fieldName] = null
} }
} }
} }

View File

@@ -0,0 +1,51 @@
module.exports = {
verbose: true,
git: {
commitMessage: 'chore(release): v${version}',
requireCleanWorkingDir: false,
tagMatch: 'v*', // payload is tagged normally, other packages are tagged with a prefix
},
github: {
release: true,
},
npm: {
skipChecks: true,
},
hooks: {
'before:init': ['pnpm install', 'pnpm clean', 'pnpm build'], // Assume tests have already been run
},
plugins: {
'@release-it/conventional-changelog': {
infile: '../../CHANGELOG.md',
preset: {
name: 'conventionalcommits',
types: [
{ type: 'feat', section: 'Features' },
{ type: 'feature', section: 'Features' },
{ type: 'fix', section: 'Bug Fixes' },
{ type: 'docs', section: 'Documentation' },
],
},
writerOpts: {
commitGroupsSort: (a, b) => {
const groupOrder = ['Features', 'Bug Fixes', 'Documentation']
return groupOrder.indexOf(a.title) - groupOrder.indexOf(b.title)
},
// Scoped commits at the end, alphabetical sort
commitsSort: (a, b) => {
if (a.scope || b.scope) {
if (!a.scope) return -1
if (!b.scope) return 1
return a.scope === b.scope
? a.subject.localeCompare(b.subject)
: a.scope.localeCompare(b.scope)
}
// Alphabetical sort
return a.subject.localeCompare(b.subject)
},
},
},
},
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "payload", "name": "payload",
"version": "2.0.5", "version": "2.0.11",
"description": "Node, React and MongoDB Headless CMS and Application Framework", "description": "Node, React and MongoDB Headless CMS and Application Framework",
"license": "MIT", "license": "MIT",
"main": "./dist/index.js", "main": "./dist/index.js",
@@ -40,8 +40,7 @@
"lint": "eslint \"src/**/*.ts\"", "lint": "eslint \"src/**/*.ts\"",
"prepublishOnly": "pnpm clean && pnpm build", "prepublishOnly": "pnpm clean && pnpm build",
"pretest": "pnpm build", "pretest": "pnpm build",
"release:beta": "release-it pre --preReleaseId=beta --npm.tag=beta --config .release-it.pre.json", "release:beta": "release-it pre --preReleaseId=beta --npm.tag=beta --config .release-it.pre.js",
"release:canary": "release-it pre --preReleaseId=canary --npm.tag=canary --config .release-it.pre.json",
"release:major": "release-it major", "release:major": "release-it major",
"release:minor": "release-it minor", "release:minor": "release-it minor",
"release:patch": "release-it patch", "release:patch": "release-it patch",
@@ -128,7 +127,7 @@
"react-select": "5.7.4", "react-select": "5.7.4",
"react-toastify": "8.2.0", "react-toastify": "8.2.0",
"sanitize-filename": "1.6.3", "sanitize-filename": "1.6.3",
"sass": "1.64.0", "sass": "1.69.4",
"scheduler": "0.23.0", "scheduler": "0.23.0",
"scmp": "2.1.0", "scmp": "2.1.0",
"sharp": "0.31.3", "sharp": "0.31.3",
@@ -187,7 +186,6 @@
"file-loader": "6.2.0", "file-loader": "6.2.0",
"form-data": "3.0.1", "form-data": "3.0.1",
"get-port": "5.1.1", "get-port": "5.1.1",
"graphql-request": "6.1.0",
"mini-css-extract-plugin": "1.6.2", "mini-css-extract-plugin": "1.6.2",
"node-fetch": "2.6.12", "node-fetch": "2.6.12",
"nodemon": "3.0.1", "nodemon": "3.0.1",
@@ -208,8 +206,7 @@
"webpack": "^5.78.0" "webpack": "^5.78.0"
}, },
"engines": { "engines": {
"node": ">=14", "node": ">=14"
"pnpm": ">=8"
}, },
"files": [ "files": [
"bin.js", "bin.js",

View File

@@ -6,13 +6,13 @@ import { toast } from 'react-toastify'
import type { Props } from './types' import type { Props } from './types'
import useDebounce from '../../../hooks/useDebounce' import useDebounce from '../../../hooks/useDebounce'
import { formatTimeToNow } from '../../../utilities/formatDate'
import { useAllFormFields, useFormModified } from '../../forms/Form/context' import { useAllFormFields, useFormModified } from '../../forms/Form/context'
import reduceFieldsToValues from '../../forms/Form/reduceFieldsToValues' import reduceFieldsToValues from '../../forms/Form/reduceFieldsToValues'
import { useConfig } from '../../utilities/Config' import { useConfig } from '../../utilities/Config'
import { useDocumentInfo } from '../../utilities/DocumentInfo' import { useDocumentInfo } from '../../utilities/DocumentInfo'
import { useLocale } from '../../utilities/Locale' import { useLocale } from '../../utilities/Locale'
import './index.scss' import './index.scss'
const baseClass = 'autosave' const baseClass = 'autosave'
const Autosave: React.FC<Props> = ({ id, collection, global, publishedDocUpdatedAt }) => { const Autosave: React.FC<Props> = ({ id, collection, global, publishedDocUpdatedAt }) => {
@@ -163,7 +163,7 @@ const Autosave: React.FC<Props> = ({ id, collection, global, publishedDocUpdated
{!saving && lastSaved && ( {!saving && lastSaved && (
<React.Fragment> <React.Fragment>
{t('lastSavedAgo', { {t('lastSavedAgo', {
distance: Math.round((Number(new Date(lastSaved)) - Number(new Date())) / 1000 / 60), distance: formatTimeToNow(lastSaved, i18n.language),
})} })}
</React.Fragment> </React.Fragment>
)} )}

View File

@@ -31,13 +31,14 @@ const DeleteDocument: React.FC<Props> = (props) => {
routes: { admin, api }, routes: { admin, api },
serverURL, serverURL,
} = useConfig() } = useConfig()
const { setModified } = useForm() const { setModified } = useForm()
const [deleting, setDeleting] = useState(false) const [deleting, setDeleting] = useState(false)
const { toggleModal } = useModal() const { toggleModal } = useModal()
const history = useHistory() const history = useHistory()
const { i18n, t } = useTranslation('general') const { i18n, t } = useTranslation('general')
const title = useTitle({ collection }) const title = useTitle({ collection })
const titleToRender = titleFromProps || title const titleToRender = titleFromProps || title || id
const modalSlug = `delete-${id}` const modalSlug = `delete-${id}`

View File

@@ -1,7 +1,7 @@
@import '../../../scss/styles.scss'; @import '../../../scss/styles.scss';
.doc-controls { .doc-controls {
@include blur-bg; @include blur-bg-light;
position: sticky; position: sticky;
top: 0; top: 0;
width: 100%; width: 100%;
@@ -9,7 +9,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
&::after { &__divider {
content: ''; content: '';
display: block; display: block;
position: absolute; position: absolute;
@@ -28,10 +28,10 @@
gap: var(--base); gap: var(--base);
padding-bottom: 1px; padding-bottom: 1px;
z-index: 1; z-index: 1;
height: var(--doc-controls-height);
} }
&__content { &__content {
height: var(--doc-controls-height);
display: flex; display: flex;
align-items: center; align-items: center;
flex-grow: 1; flex-grow: 1;
@@ -140,21 +140,21 @@
// On mobile, only stick the controls to the top // On mobile, only stick the controls to the top
// The timestamps and meta can scroll past // The timestamps and meta can scroll past
// The same container needs to the sticky, though // The same container needs to the sticky, though
// So we use a static height with a negative top // So we use a static height with a negative top equal to the meta height plus top padding
top: calc(var(--doc-controls-height) * -1); top: calc(var(--base) * -2);
padding-right: 0; padding-right: 0;
padding-left: 0; padding-left: 0;
&__wrapper { &__wrapper {
flex-direction: column; flex-direction: column;
gap: 0; gap: 0;
height: unset;
} }
&__content { &__content {
width: 100%; width: 100%;
align-items: flex-start;
padding-top: calc(var(--base) / 2);
overflow: auto; overflow: auto;
height: calc(var(--base) * 2);
// this container has a fixed height // this container has a fixed height
// this means the scrollbar (when present) overlaps the content // this means the scrollbar (when present) overlaps the content
@@ -167,26 +167,52 @@
width: auto; width: auto;
gap: calc(var(--base) / 2); gap: calc(var(--base) / 2);
margin-left: var(--gutter-h); margin-left: var(--gutter-h);
margin-right: var(--gutter-h);
&::after {
content: '';
display: block;
position: sticky;
right: 0;
width: calc(var(--base) * 2);
height: var(--base);
background: linear-gradient(to right, transparent, var(--theme-bg));
flex-shrink: 0;
z-index: 1111;
pointer-events: none;
}
} }
&__controls-wrapper { &__controls-wrapper {
width: 100%; width: 100%;
transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0);
padding: calc(var(--base) / 2) 0; padding-right: var(--gutter-h);
overflow: auto; justify-content: space-between;
height: var(--doc-controls-height);
border-top: 1px solid var(--theme-elevation-100);
} }
&__controls { &__controls {
margin-left: var(--gutter-h); padding-left: var(--gutter-h);
margin-right: var(--gutter-h); overflow: auto;
}
&__popup { // do not show scrollbar because the parent container has a static height
// TODO: the container needs to overflow on mobile // this container has a gradient overlay as visual indication of `overflow: scroll`
// But the popup interferes with this because it requires overflow in order to be visible &::-webkit-scrollbar {
// So we likely need to outright show the controls nested within the popup on mobile display: none;
display: none; }
&::after {
content: '';
display: block;
position: sticky;
right: 0;
width: calc(var(--base) * 2);
height: calc(var(--base) * 1.5);
background: linear-gradient(to right, transparent, var(--theme-bg));
flex-shrink: 0;
z-index: 1111;
pointer-events: none;
}
} }
} }
} }

View File

@@ -2,7 +2,8 @@ import React, { Fragment } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import type { CollectionPermission, GlobalPermission } from '../../../../auth' import type { CollectionPermission, GlobalPermission } from '../../../../auth'
import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from '../../../../exports/types' import type { SanitizedCollectionConfig } from '../../../../collections/config/types'
import type { SanitizedGlobalConfig } from '../../../../globals/config/types'
import { formatDate } from '../../../utilities/formatDate' import { formatDate } from '../../../utilities/formatDate'
import { useConfig } from '../../utilities/Config' import { useConfig } from '../../utilities/Config'
@@ -55,24 +56,6 @@ export const DocumentControls: React.FC<{
const { i18n, t } = useTranslation('general') const { i18n, t } = useTranslation('general')
let showPreviewButton = false
if (collection) {
showPreviewButton =
isEditing &&
collection?.admin?.preview &&
collection?.versions?.drafts &&
!collection?.versions?.drafts?.autosave
}
if (global) {
showPreviewButton =
isEditing &&
global?.admin?.preview &&
global?.versions?.drafts &&
!global?.versions?.drafts?.autosave
}
const showDotMenu = Boolean(collection && id && !disableActions) const showDotMenu = Boolean(collection && id && !disableActions)
return ( return (
@@ -165,7 +148,7 @@ export const DocumentControls: React.FC<{
</div> </div>
<div className={`${baseClass}__controls-wrapper`}> <div className={`${baseClass}__controls-wrapper`}>
<div className={`${baseClass}__controls`}> <div className={`${baseClass}__controls`}>
{showPreviewButton && ( {(collection?.admin?.preview || global?.admin?.preview) && (
<PreviewButton <PreviewButton
CustomComponent={ CustomComponent={
collection?.admin?.components?.edit?.PreviewButton || collection?.admin?.components?.edit?.PreviewButton ||
@@ -242,6 +225,7 @@ export const DocumentControls: React.FC<{
)} )}
</div> </div>
</div> </div>
<div className={`${baseClass}__divider`} />
</Gutter> </Gutter>
) )
} }

View File

@@ -1,4 +1,5 @@
import { useModal } from '@faceless-ui/modal' import { useModal } from '@faceless-ui/modal'
import queryString from 'qs'
import React, { useCallback, useEffect, useRef, useState } from 'react' import React, { useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
@@ -16,6 +17,7 @@ import X from '../../icons/X'
import { useAuth } from '../../utilities/Auth' import { useAuth } from '../../utilities/Auth'
import { useConfig } from '../../utilities/Config' import { useConfig } from '../../utilities/Config'
import { DocumentInfoProvider, useDocumentInfo } from '../../utilities/DocumentInfo' import { DocumentInfoProvider, useDocumentInfo } from '../../utilities/DocumentInfo'
import { useFormQueryParams } from '../../utilities/FormQueryParams'
import { useLocale } from '../../utilities/Locale' import { useLocale } from '../../utilities/Locale'
import RenderCustomComponent from '../../utilities/RenderCustomComponent' import RenderCustomComponent from '../../utilities/RenderCustomComponent'
import DefaultEdit from '../../views/collections/Edit/Default' import DefaultEdit from '../../views/collections/Edit/Default'
@@ -42,6 +44,8 @@ const Content: React.FC<DocumentDrawerProps> = ({
const [isOpen, setIsOpen] = useState(false) const [isOpen, setIsOpen] = useState(false)
const [collectionConfig] = useRelatedCollections(collectionSlug) const [collectionConfig] = useRelatedCollections(collectionSlug)
const config = useConfig() const config = useConfig()
const { formQueryParams } = useFormQueryParams()
const formattedQueryParams = queryString.stringify(formQueryParams)
const { admin: { components: { views: { Edit } = {} } = {} } = {} } = collectionConfig const { admin: { components: { views: { Edit } = {} } = {} } = {} } = collectionConfig
@@ -117,8 +121,8 @@ const Content: React.FC<DocumentDrawerProps> = ({
const apiURL = id ? `${serverURL}${api}/${collectionSlug}/${id}?locale=${locale}` : null const apiURL = id ? `${serverURL}${api}/${collectionSlug}/${id}?locale=${locale}` : null
const action = `${serverURL}${api}/${collectionSlug}${ const action = `${serverURL}${api}/${collectionSlug}${
id ? `/${id}` : '' isEditing ? `/${id}` : ''
}?locale=${locale}&fallback-locale=null` }?${formattedQueryParams}`
const hasSavePermission = const hasSavePermission =
(isEditing && docPermissions?.update?.permission) || (isEditing && docPermissions?.update?.permission) ||

View File

@@ -10,6 +10,19 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
white-space: nowrap; white-space: nowrap;
// Use a pseudo element for the accessability so that it doesn't take up DOM space
// Also because the parent element has `overflow: hidden` which would clip an outline
&:focus-visible::after {
content: '';
border: var(--accessibility-outline);
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
pointer-events: none;
}
} }
&:focus:not(:focus-visible) { &:focus:not(:focus-visible) {

View File

@@ -64,6 +64,7 @@ export const DocumentTab: React.FC<DocumentTabProps & DocumentTabConfig> = (prop
className={`${baseClass}__link`} className={`${baseClass}__link`}
to={href} to={href}
{...(newTab && { rel: 'noopener noreferrer', target: '_blank' })} {...(newTab && { rel: 'noopener noreferrer', target: '_blank' })}
tabIndex={isActive ? -1 : 0}
> >
<span className={`${baseClass}__label`}> <span className={`${baseClass}__label`}>
{labelToRender} {labelToRender}

View File

@@ -17,6 +17,24 @@
padding: 0; padding: 0;
overflow: auto; overflow: auto;
// this container has a gradient overlay as visual indication of `overflow: scroll`
&::-webkit-scrollbar {
display: none;
}
&::after {
content: '';
display: block;
position: sticky;
right: 0;
width: calc(var(--base) * 2);
height: calc(var(--base) * 2);
background: linear-gradient(to right, transparent, var(--theme-bg));
flex-shrink: 0;
z-index: 1111;
pointer-events: none;
}
&__tabs { &__tabs {
padding: 0; padding: 0;
margin-left: var(--gutter-h); margin-left: var(--gutter-h);

View File

@@ -43,7 +43,7 @@ export const tabs: DocumentTabConfig[] = [
// API // API
{ {
condition: ({ collection, global }) => condition: ({ collection, global }) =>
!collection?.admin?.hideAPIURL || !global?.admin?.hideAPIURL, (collection && !collection?.admin?.hideAPIURL) || (global && !global?.admin?.hideAPIURL),
href: '/api', href: '/api',
label: 'API', label: 'API',
}, },

View File

@@ -1,6 +1,6 @@
@import '../../../scss/styles.scss'; @import '../../../scss/styles.scss';
$transTime: 200ms; $transTime: 200;
.drawer { .drawer {
display: flex; display: flex;
@@ -9,7 +9,7 @@ $transTime: 200ms;
height: 100vh; height: 100vh;
&__blur-bg { &__blur-bg {
@include blur-bg(); @include blur-bg;
position: absolute; position: absolute;
z-index: 1; z-index: 1;
top: 0; top: 0;
@@ -17,7 +17,7 @@ $transTime: 200ms;
bottom: 0; bottom: 0;
left: 0; left: 0;
opacity: 0; opacity: 0;
transition: all $transTime linear; transition: all #{$transTime}ms linear;
} }
&__content { &__content {
@@ -27,7 +27,8 @@ $transTime: 200ms;
z-index: 2; z-index: 2;
width: calc(100% - var(--gutter-h)); width: calc(100% - var(--gutter-h));
overflow: hidden; overflow: hidden;
transition: all $transTime linear; transition: all #{$transTime}ms linear;
background-color: var(--theme-bg);
} }
&__content-children { &__content-children {
@@ -40,14 +41,14 @@ $transTime: 200ms;
&--is-open { &--is-open {
.drawer { .drawer {
&__content, &__content,
&__blur-bg, &__blur-bg {
&__close {
opacity: 1; opacity: 1;
} }
&__close { &__close {
transition: opacity $transTime linear; opacity: 0.1;
transition-delay: $transTime; transition: opacity #{$transTime}ms linear;
transition-delay: #{calc($transTime / 2)}ms;
} }
&__content { &__content {
@@ -68,7 +69,7 @@ $transTime: 200ms;
transition: none; transition: none;
transition-delay: 0ms; transition-delay: 0ms;
flex-grow: 1; flex-grow: 1;
background: transparent; background: var(--theme-elevation-800);
&:active, &:active,
&:focus { &:focus {
@@ -120,7 +121,15 @@ $transTime: 200ms;
} }
html[data-theme='dark'] { html[data-theme='dark'] {
.drawer__close { .drawer {
background: rgba(0, 0, 0, 0.2); &__close {
background: var(--color-base-1000);
}
&--is-open {
.drawer__close {
opacity: 0.25;
}
}
} }
} }

View File

@@ -1,5 +1,4 @@
import { Modal, useModal } from '@faceless-ui/modal' import { Modal, useModal } from '@faceless-ui/modal'
import { useWindowInfo } from '@faceless-ui/window-info'
import React, { useCallback, useEffect, useState } from 'react' import React, { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@@ -52,9 +51,6 @@ export const Drawer: React.FC<Props> = ({
}) => { }) => {
const { t } = useTranslation('general') const { t } = useTranslation('general')
const { closeModal, modalState } = useModal() const { closeModal, modalState } = useModal()
const {
breakpoints: { m: midBreak },
} = useWindowInfo()
const drawerDepth = useEditDepth() const drawerDepth = useEditDepth()
const [isOpen, setIsOpen] = useState(false) const [isOpen, setIsOpen] = useState(false)
const [animateIn, setAnimateIn] = useState(false) const [animateIn, setAnimateIn] = useState(false)
@@ -72,7 +68,12 @@ export const Drawer: React.FC<Props> = ({
return ( return (
<Modal <Modal
className={[className, baseClass, animateIn && `${baseClass}--is-open`] className={[
className,
baseClass,
animateIn && `${baseClass}--is-open`,
drawerDepth > 1 && `${baseClass}--nested`,
]
.filter(Boolean) .filter(Boolean)
.join(' ')} .join(' ')}
slug={slug} slug={slug}
@@ -80,7 +81,7 @@ export const Drawer: React.FC<Props> = ({
zIndex: zBase + drawerDepth, zIndex: zBase + drawerDepth,
}} }}
> >
{drawerDepth === 1 && <div className={`${baseClass}__blur-bg`} />} {(!drawerDepth || drawerDepth === 1) && <div className={`${baseClass}__blur-bg`} />}
<button <button
aria-label={t('close')} aria-label={t('close')}
className={`${baseClass}__close`} className={`${baseClass}__close`}
@@ -89,7 +90,7 @@ export const Drawer: React.FC<Props> = ({
type="button" type="button"
/> />
<div className={`${baseClass}__content`}> <div className={`${baseClass}__content`}>
<div className={`${baseClass}__blur-bg`} /> <div className={`${baseClass}__blur-bg-content`} />
<Gutter className={`${baseClass}__content-children`} left={gutter} right={gutter}> <Gutter className={`${baseClass}__content-children`} left={gutter} right={gutter}>
<EditDepthContext.Provider value={drawerDepth + 1}> <EditDepthContext.Provider value={drawerDepth + 1}>
{header && header} {header && header}

View File

@@ -51,6 +51,7 @@ const Duplicate: React.FC<Props> = ({ id, collection, slug }) => {
}, },
params: { params: {
depth: 0, depth: 0,
draft: true,
locale, locale,
}, },
}) })

View File

@@ -35,11 +35,6 @@ $header-height: base(5);
text-wrap: nowrap; text-wrap: nowrap;
} }
&__cancel,
&__save {
width: 50%;
}
&__toolWrap { &__toolWrap {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;

View File

@@ -50,14 +50,13 @@ export const EditUpload: React.FC<{
y: uploadEdits?.focalPoint?.y || 50, y: uploadEdits?.focalPoint?.y || 50,
}) })
const [checkBounds, setCheckBounds] = useState<boolean>(false) const [checkBounds, setCheckBounds] = useState<boolean>(false)
const [originalHeight, setOriginalHeight] = useState<number>(0)
const [originalWidth, setOriginalWidth] = useState<number>(0)
const focalWrapRef = useRef<HTMLDivElement | undefined>() const focalWrapRef = useRef<HTMLDivElement | undefined>()
const imageRef = useRef<HTMLImageElement | undefined>() const imageRef = useRef<HTMLImageElement | undefined>()
const cropRef = useRef<HTMLDivElement | undefined>() const cropRef = useRef<HTMLDivElement | undefined>()
const originalHeight = imageRef.current ? imageRef.current.naturalHeight : 0
const originalWidth = imageRef.current ? imageRef.current.naturalWidth : 0
const fineTuneCrop = ({ dimension, value }: { dimension: 'height' | 'width'; value: string }) => { const fineTuneCrop = ({ dimension, value }: { dimension: 'height' | 'width'; value: string }) => {
const intValue = parseInt(value) const intValue = parseInt(value)
if (dimension === 'width' && intValue >= originalWidth) return null if (dimension === 'width' && intValue >= originalWidth) return null
@@ -98,7 +97,9 @@ export const EditUpload: React.FC<{
const centerFocalPoint = () => { const centerFocalPoint = () => {
const containerRect = focalWrapRef.current.getBoundingClientRect() const containerRect = focalWrapRef.current.getBoundingClientRect()
const boundsRect = cropRef.current.getBoundingClientRect() const boundsRect = showCrop
? cropRef.current.getBoundingClientRect()
: imageRef.current.getBoundingClientRect()
const xCenter = const xCenter =
((boundsRect.left - containerRect.left + boundsRect.width / 2) / containerRect.width) * 100 ((boundsRect.left - containerRect.left + boundsRect.width / 2) / containerRect.width) * 100
const yCenter = const yCenter =
@@ -152,22 +153,32 @@ export const EditUpload: React.FC<{
return <div className={`${baseClass}__crop-window`} ref={cropRef} /> return <div className={`${baseClass}__crop-window`} ref={cropRef} />
}} }}
> >
<img alt={t('upload:setCropArea')} ref={imageRef} src={fileSrcToUse} /> <img
alt={t('upload:setCropArea')}
onLoad={(e) => {
setOriginalHeight(e.currentTarget.naturalHeight)
setOriginalWidth(e.currentTarget.naturalWidth)
}}
ref={imageRef}
src={fileSrcToUse}
/>
</ReactCrop> </ReactCrop>
) : ( ) : (
<img alt={t('upload:setFocalPoint')} ref={imageRef} src={fileSrcToUse} /> <img alt={t('upload:setFocalPoint')} ref={imageRef} src={fileSrcToUse} />
)} )}
<DraggableElement {showFocalPoint && (
boundsRef={cropRef} <DraggableElement
checkBounds={checkBounds} boundsRef={showCrop ? cropRef : imageRef}
className={`${baseClass}__focalPoint`} checkBounds={showCrop ? checkBounds : false}
containerRef={focalWrapRef} className={`${baseClass}__focalPoint`}
initialPosition={pointPosition} containerRef={focalWrapRef}
onDragEnd={onDragEnd} initialPosition={pointPosition}
setCheckBounds={setCheckBounds} onDragEnd={onDragEnd}
> setCheckBounds={showCrop ? setCheckBounds : false}
<Plus /> >
</DraggableElement> <Plus />
</DraggableElement>
)}
</div> </div>
</div> </div>
{(showCrop || showFocalPoint) && ( {(showCrop || showFocalPoint) && (

View File

@@ -6,22 +6,37 @@
cursor: pointer; cursor: pointer;
background-color: transparent; background-color: transparent;
outline: none; outline: none;
position: absolute; position: relative;
@include blur-bg;
--hamburger-padding: 5px; --hamburger-padding: 8px;
--hamburger-size: 9px; --hamburger-size: 9px;
--hamburger-line-gap: 3px; --hamburger-line-gap: 3px;
padding: var(--hamburger-padding);
border: 1px solid var(--theme-elevation-350);
color: var(--theme-text);
border-radius: 3px;
&:focus { color: var(--theme-text);
outline: none;
&__wrapper {
border: 1px solid var(--theme-elevation-150);
padding: var(--hamburger-padding);
border-radius: 3px;
position: relative;
z-index: 1;
height: 100%;
width: 100%;
&:hover {
border: 1px solid var(--theme-elevation-500);
background-color: var(--theme-elevation-100);
}
&:focus {
outline: none;
}
} }
&__icon { &__icon {
position: relative; position: relative;
z-index: 1;
height: var(--hamburger-size); height: var(--hamburger-size);
width: var(--hamburger-size); width: var(--hamburger-size);
display: flex; display: flex;

View File

@@ -14,32 +14,34 @@ export const Hamburger: React.FC<{
const { closeIcon = 'x', isActive = false } = props const { closeIcon = 'x', isActive = false } = props
return ( return (
<div className={[baseClass].filter(Boolean).join(' ')}> <div className={baseClass}>
<div className={`${baseClass}__icon`}> <div className={`${baseClass}__wrapper`}>
{!isActive && ( <div className={`${baseClass}__icon`}>
<div className={`${baseClass}__lines`} title={t('open')}> {!isActive && (
<div className={`${baseClass}__line ${baseClass}__top`} /> <div className={`${baseClass}__lines`} title={t('open')}>
<div className={`${baseClass}__line ${baseClass}__middle`} /> <div className={`${baseClass}__line ${baseClass}__top`} />
<div className={`${baseClass}__line ${baseClass}__bottom`} /> <div className={`${baseClass}__line ${baseClass}__middle`} />
</div> <div className={`${baseClass}__line ${baseClass}__bottom`} />
)} </div>
{isActive && ( )}
<div {isActive && (
aria-label={closeIcon === 'collapse' ? t('collapse') : t('close')} <div
className={`${baseClass}__close-icon`} aria-label={closeIcon === 'collapse' ? t('collapse') : t('close')}
title={closeIcon === 'collapse' ? t('collapse') : t('close')} className={`${baseClass}__close-icon`}
> title={closeIcon === 'collapse' ? t('collapse') : t('close')}
{closeIcon === 'x' && ( >
<React.Fragment> {closeIcon === 'x' && (
<div className={`${baseClass}__line ${baseClass}__x-left`} /> <React.Fragment>
<div className={`${baseClass}__line ${baseClass}__x-right`} /> <div className={`${baseClass}__line ${baseClass}__x-left`} />
</React.Fragment> <div className={`${baseClass}__line ${baseClass}__x-right`} />
)} </React.Fragment>
{closeIcon === 'collapse' && ( )}
<Chevron className={`${baseClass}__collapse-chevron`} direction="left" /> {closeIcon === 'collapse' && (
)} <Chevron className={`${baseClass}__collapse-chevron`} direction="left" />
</div> )}
)} </div>
)}
</div>
</div> </div>
</div> </div>
) )

View File

@@ -24,7 +24,6 @@
} }
&__bg { &__bg {
@include blur-bg;
opacity: 0; opacity: 0;
position: absolute; position: absolute;
left: 0; left: 0;
@@ -58,9 +57,28 @@
} }
&__account { &__account {
position: relative;
&:focus:not(:focus-visible) { &:focus:not(:focus-visible) {
opacity: 1; opacity: 1;
} }
// Use a pseudo element for the accessability so that it doesn't take up DOM space
// Also because the parent element has `overflow: hidden` which would clip an outline
&:focus-visible {
outline: none;
&::after {
content: '';
border: var(--accessibility-outline);
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
pointer-events: none;
}
}
} }
&__controls-wrapper { &__controls-wrapper {
@@ -92,6 +110,12 @@
right: base(2); right: base(2);
} }
&--nav-open {
.app-header__localizer {
display: none;
}
}
&__mobile-nav-toggler { &__mobile-nav-toggler {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@@ -8,12 +8,13 @@ import { Hamburger } from '../Hamburger'
import Localizer from '../Localizer' import Localizer from '../Localizer'
import { LocalizerLabel } from '../Localizer/LocalizerLabel' import { LocalizerLabel } from '../Localizer/LocalizerLabel'
import { NavToggler } from '../Nav/NavToggler' import { NavToggler } from '../Nav/NavToggler'
import { useNav } from '../Nav/context'
import StepNav from '../StepNav' import StepNav from '../StepNav'
import './index.scss' import './index.scss'
const baseClass = 'app-header' const baseClass = 'app-header'
export const AppHeader: React.FC = (props) => { export const AppHeader: React.FC = () => {
const { t } = useTranslation() const { t } = useTranslation()
const { const {
@@ -21,12 +22,14 @@ export const AppHeader: React.FC = (props) => {
routes: { admin: adminRoute }, routes: { admin: adminRoute },
} = useConfig() } = useConfig()
const { navOpen } = useNav()
return ( return (
<header className={[baseClass].filter(Boolean).join(' ')}> <header className={[baseClass, navOpen && `${baseClass}--nav-open`].filter(Boolean).join(' ')}>
<div className={`${baseClass}__bg`} /> <div className={`${baseClass}__bg`} />
<div className={`${baseClass}__content`}> <div className={`${baseClass}__content`}>
<div className={`${baseClass}__wrapper`}> <div className={`${baseClass}__wrapper`}>
<NavToggler className={`${baseClass}__mobile-nav-toggler`}> <NavToggler className={`${baseClass}__mobile-nav-toggler`} tabIndex={-1}>
<Hamburger /> <Hamburger />
</NavToggler> </NavToggler>
<div className={`${baseClass}__controls-wrapper`}> <div className={`${baseClass}__controls-wrapper`}>
@@ -43,6 +46,7 @@ export const AppHeader: React.FC = (props) => {
<Link <Link
aria-label={t('authentication:account')} aria-label={t('authentication:account')}
className={`${baseClass}__account`} className={`${baseClass}__account`}
tabIndex={0}
to={`${adminRoute}/account`} to={`${adminRoute}/account`}
> >
<Account /> <Account />

View File

@@ -8,20 +8,22 @@ import RenderCustomComponent from '../../utilities/RenderCustomComponent'
const baseClass = 'nav' const baseClass = 'nav'
const DefaultLogout = () => { const DefaultLogout: React.FC<{
tabIndex?: number
}> = ({ tabIndex }) => {
const { t } = useTranslation('authentication') const { t } = useTranslation('authentication')
const config = useConfig() const config = useConfig()
const { const {
admin: { admin: { logoutRoute },
components: { logout },
logoutRoute,
},
routes: { admin }, routes: { admin },
} = config } = config
return ( return (
<Link <Link
aria-label={t('logOut')} aria-label={t('logOut')}
className={`${baseClass}__log-out`} className={`${baseClass}__log-out`}
tabIndex={tabIndex}
to={`${admin}${logoutRoute}`} to={`${admin}${logoutRoute}`}
> >
<LogOut /> <LogOut />
@@ -29,7 +31,9 @@ const DefaultLogout = () => {
) )
} }
const Logout: React.FC = () => { const Logout: React.FC<{
tabIndex?: number
}> = ({ tabIndex = 0 }) => {
const { const {
admin: { admin: {
components: { components: {
@@ -40,7 +44,15 @@ const Logout: React.FC = () => {
} = {}, } = {},
} = useConfig() } = useConfig()
return <RenderCustomComponent CustomComponent={CustomLogout} DefaultComponent={DefaultLogout} /> return (
<RenderCustomComponent
CustomComponent={CustomLogout}
DefaultComponent={DefaultLogout}
componentProps={{
tabIndex,
}}
/>
)
} }
export default Logout export default Logout

View File

@@ -6,11 +6,5 @@
padding: 0; padding: 0;
margin: 0; margin: 0;
border: 0; border: 0;
height: var(--app-header-height);
width: var(--gutter-h);
cursor: pointer; cursor: pointer;
@include small-break {
width: calc(var(--gutter-h) * 2);
}
} }

View File

@@ -12,8 +12,9 @@ export const NavToggler: React.FC<{
children?: React.ReactNode children?: React.ReactNode
className?: string className?: string
id?: string id?: string
tabIndex?: number
}> = (props) => { }> = (props) => {
const { id, children, className } = props const { id, children, className, tabIndex = 0 } = props
const { t } = useTranslation('general') const { t } = useTranslation('general')
@@ -43,6 +44,7 @@ export const NavToggler: React.FC<{
}) })
} }
}} }}
tabIndex={tabIndex}
type="button" type="button"
> >
{children} {children}

View File

@@ -19,11 +19,21 @@ export const NavContext = React.createContext<NavContextType>({
export const useNav = () => React.useContext(NavContext) export const useNav = () => React.useContext(NavContext)
const getNavPreference = async (getPreference): Promise<boolean> => {
const navPrefs = await getPreference('nav')
const preferredState = navPrefs?.open
if (typeof preferredState === 'boolean') {
return preferredState
} else {
return true
}
}
export const NavProvider: React.FC<{ export const NavProvider: React.FC<{
children: React.ReactNode children: React.ReactNode
}> = ({ children }) => { }> = ({ children }) => {
const { const {
breakpoints: { l: largeBreak }, breakpoints: { l: largeBreak, m: midBreak, s: smallBreak },
} = useWindowInfo() } = useWindowInfo()
const { getPreference } = usePreferences() const { getPreference } = usePreferences()
@@ -40,16 +50,11 @@ export const NavProvider: React.FC<{
useEffect(() => { useEffect(() => {
if (largeBreak === false) { if (largeBreak === false) {
const setNavFromPreferences = async () => { const setNavFromPreferences = async () => {
const navPrefs = await getPreference('nav') const preferredState = await getNavPreference(getPreference)
const preferredState = navPrefs?.open setNavOpen(preferredState)
if (typeof preferredState === 'boolean') {
setNavOpen(preferredState)
} else {
setNavOpen(true)
}
} }
setNavFromPreferences() setNavFromPreferences() // eslint-disable-line @typescript-eslint/no-floating-promises
} }
}, [largeBreak, getPreference, setNavOpen]) }, [largeBreak, getPreference, setNavOpen])
@@ -58,7 +63,7 @@ export const NavProvider: React.FC<{
useEffect(() => { useEffect(() => {
let unlisten: () => void let unlisten: () => void
if (largeBreak) { if (midBreak) {
unlisten = history.listen(() => { unlisten = history.listen(() => {
setNavOpen(false) setNavOpen(false)
}) })
@@ -67,27 +72,28 @@ export const NavProvider: React.FC<{
} }
return () => unlisten && unlisten() return () => unlisten && unlisten()
}, [history, setNavOpen, largeBreak]) }, [history, setNavOpen, midBreak])
// on smaller screens where the nav is a modal
// close the nav when the user resizes down to a smaller screen
useEffect(() => {
if (largeBreak) {
setNavOpen(false)
}
}, [largeBreak])
// on open and close, lock the body scroll // on open and close, lock the body scroll
// do not do this on desktop, the sidebar is not a modal // do not do this on desktop, the sidebar is not a modal
useEffect(() => { useEffect(() => {
if (navRef.current) { if (navRef.current) {
if (navOpen && largeBreak) { if (navOpen && midBreak) {
disableBodyScroll(navRef.current) disableBodyScroll(navRef.current)
} else { } else {
enableBodyScroll(navRef.current) enableBodyScroll(navRef.current)
} }
} }
}, [navOpen, largeBreak]) }, [navOpen, midBreak])
// on smaller screens where the nav is a modal
// close the nav when the user resizes down to mobile
// the sidebar is a modal on mobile
useEffect(() => {
if (largeBreak === false || midBreak === false || smallBreak === false) {
setNavOpen(false)
}
}, [largeBreak, midBreak, smallBreak])
// when the component unmounts, clear all body scroll locks // when the component unmounts, clear all body scroll locks
useEffect(() => { useEffect(() => {

Some files were not shown because too many files have changed in this diff Show More