diff --git a/.changeset/admin-meta-rename.md b/.changeset/admin-meta-rename.md new file mode 100644 index 00000000000..c1f25f01746 --- /dev/null +++ b/.changeset/admin-meta-rename.md @@ -0,0 +1,5 @@ +--- +"@keystone-6/core": minor +--- + +Renames `isHidden` to `hideNavigation` on the AdminMeta list GraphQL type, `isHidden` is now deprecated and will be removed in a breaking change diff --git a/.changeset/admin-ui-navigation.md b/.changeset/admin-ui-navigation.md new file mode 100644 index 00000000000..892ba0d6ce1 --- /dev/null +++ b/.changeset/admin-ui-navigation.md @@ -0,0 +1,5 @@ +--- +"@keystone-6/core": major +--- + +Removes `authenticatedItem` from `@keystone-6/core/admin-ui/components` exports diff --git a/.changeset/remove-end-session.md b/.changeset/remove-end-session.md new file mode 100644 index 00000000000..35482f2e091 --- /dev/null +++ b/.changeset/remove-end-session.md @@ -0,0 +1,5 @@ +--- +"@keystone-6/core": major +--- + +Removes the `EndSession` GraphQL mutation addition when `context.session.end` is defined, extend this yourself if required diff --git a/design-system/packages/button/package.json b/design-system/packages/button/package.json index 2522f425f13..889809abb18 100644 --- a/design-system/packages/button/package.json +++ b/design-system/packages/button/package.json @@ -6,6 +6,7 @@ "module": "dist/keystone-ui-button.esm.js", "exports": { ".": { + "types": "./dist/keystone-ui-button.cjs.js", "module": "./dist/keystone-ui-button.esm.js", "default": "./dist/keystone-ui-button.cjs.js" }, diff --git a/design-system/packages/core/package.json b/design-system/packages/core/package.json index 5fb189129e1..cc5eea896d7 100644 --- a/design-system/packages/core/package.json +++ b/design-system/packages/core/package.json @@ -6,6 +6,7 @@ "module": "dist/keystone-ui-core.esm.js", "exports": { ".": { + "types": "./dist/keystone-ui-core.cjs.js", "module": "./dist/keystone-ui-core.esm.js", "default": "./dist/keystone-ui-core.cjs.js" }, diff --git a/design-system/packages/core/src/components/Core.tsx b/design-system/packages/core/src/components/Core.tsx index 94a5513b608..56d358f4e74 100644 --- a/design-system/packages/core/src/components/Core.tsx +++ b/design-system/packages/core/src/components/Core.tsx @@ -51,7 +51,7 @@ const BaseCSS = ({ includeNormalize, optimizeLegibility }: BaseCSSProps) => { color: colors.foreground, fontSize: '1rem', fontWeight: typography.fontWeight.regular, - lineHeight: typography.leading.base, + // lineHeight: typography.leading.base, fontFamily: typography.fontFamily.body, // optimize legibility diff --git a/design-system/packages/fields/package.json b/design-system/packages/fields/package.json index 625081f0026..8c213d7e4dc 100644 --- a/design-system/packages/fields/package.json +++ b/design-system/packages/fields/package.json @@ -6,6 +6,7 @@ "module": "dist/keystone-ui-fields.esm.js", "exports": { ".": { + "types": "./dist/keystone-ui-fields.cjs.js", "module": "./dist/keystone-ui-fields.esm.js", "default": "./dist/keystone-ui-fields.cjs.js" }, diff --git a/design-system/packages/fields/src/FieldContainer.tsx b/design-system/packages/fields/src/FieldContainer.tsx deleted file mode 100644 index 6bef2fcf039..00000000000 --- a/design-system/packages/fields/src/FieldContainer.tsx +++ /dev/null @@ -1,7 +0,0 @@ -/** @jsxRuntime classic */ -/** @jsx jsx */ -import { jsx, forwardRefWithAs } from '@keystone-ui/core' - -export const FieldContainer = forwardRefWithAs<'div', unknown>(({ as: Tag = 'div', ...props }, ref) => { - return -}) diff --git a/design-system/packages/icons/package.json b/design-system/packages/icons/package.json index 3d200806747..91d7ac3ac62 100644 --- a/design-system/packages/icons/package.json +++ b/design-system/packages/icons/package.json @@ -6,1150 +6,1437 @@ "module": "dist/keystone-ui-icons.esm.js", "exports": { ".": { + "types": "./dist/keystone-ui-icons.cjs.js", "module": "./dist/keystone-ui-icons.esm.js", "default": "./dist/keystone-ui-icons.cjs.js" }, "./icons/XIcon": { + "types": "./icons/XIcon/dist/keystone-ui-icons-icons-XIcon.cjs.js", "module": "./icons/XIcon/dist/keystone-ui-icons-icons-XIcon.esm.js", "default": "./icons/XIcon/dist/keystone-ui-icons-icons-XIcon.cjs.js" }, "./icons/TvIcon": { + "types": "./icons/TvIcon/dist/keystone-ui-icons-icons-TvIcon.cjs.js", "module": "./icons/TvIcon/dist/keystone-ui-icons-icons-TvIcon.esm.js", "default": "./icons/TvIcon/dist/keystone-ui-icons-icons-TvIcon.cjs.js" }, "./icons/BoxIcon": { + "types": "./icons/BoxIcon/dist/keystone-ui-icons-icons-BoxIcon.cjs.js", "module": "./icons/BoxIcon/dist/keystone-ui-icons-icons-BoxIcon.esm.js", "default": "./icons/BoxIcon/dist/keystone-ui-icons-icons-BoxIcon.cjs.js" }, "./icons/CpuIcon": { + "types": "./icons/CpuIcon/dist/keystone-ui-icons-icons-CpuIcon.cjs.js", "module": "./icons/CpuIcon/dist/keystone-ui-icons-icons-CpuIcon.esm.js", "default": "./icons/CpuIcon/dist/keystone-ui-icons-icons-CpuIcon.cjs.js" }, "./icons/EyeIcon": { + "types": "./icons/EyeIcon/dist/keystone-ui-icons-icons-EyeIcon.cjs.js", "module": "./icons/EyeIcon/dist/keystone-ui-icons-icons-EyeIcon.esm.js", "default": "./icons/EyeIcon/dist/keystone-ui-icons-icons-EyeIcon.cjs.js" }, "./icons/KeyIcon": { + "types": "./icons/KeyIcon/dist/keystone-ui-icons-icons-KeyIcon.cjs.js", "module": "./icons/KeyIcon/dist/keystone-ui-icons-icons-KeyIcon.esm.js", "default": "./icons/KeyIcon/dist/keystone-ui-icons-icons-KeyIcon.cjs.js" }, "./icons/MapIcon": { + "types": "./icons/MapIcon/dist/keystone-ui-icons-icons-MapIcon.cjs.js", "module": "./icons/MapIcon/dist/keystone-ui-icons-icons-MapIcon.esm.js", "default": "./icons/MapIcon/dist/keystone-ui-icons-icons-MapIcon.cjs.js" }, "./icons/MehIcon": { + "types": "./icons/MehIcon/dist/keystone-ui-icons-icons-MehIcon.cjs.js", "module": "./icons/MehIcon/dist/keystone-ui-icons-icons-MehIcon.esm.js", "default": "./icons/MehIcon/dist/keystone-ui-icons-icons-MehIcon.cjs.js" }, "./icons/MicIcon": { + "types": "./icons/MicIcon/dist/keystone-ui-icons-icons-MicIcon.cjs.js", "module": "./icons/MicIcon/dist/keystone-ui-icons-icons-MicIcon.esm.js", "default": "./icons/MicIcon/dist/keystone-ui-icons-icons-MicIcon.cjs.js" }, "./icons/RssIcon": { + "types": "./icons/RssIcon/dist/keystone-ui-icons-icons-RssIcon.cjs.js", "module": "./icons/RssIcon/dist/keystone-ui-icons-icons-RssIcon.esm.js", "default": "./icons/RssIcon/dist/keystone-ui-icons-icons-RssIcon.cjs.js" }, "./icons/SunIcon": { + "types": "./icons/SunIcon/dist/keystone-ui-icons-icons-SunIcon.cjs.js", "module": "./icons/SunIcon/dist/keystone-ui-icons-icons-SunIcon.esm.js", "default": "./icons/SunIcon/dist/keystone-ui-icons-icons-SunIcon.cjs.js" }, "./icons/TagIcon": { + "types": "./icons/TagIcon/dist/keystone-ui-icons-icons-TagIcon.cjs.js", "module": "./icons/TagIcon/dist/keystone-ui-icons-icons-TagIcon.esm.js", "default": "./icons/TagIcon/dist/keystone-ui-icons-icons-TagIcon.cjs.js" }, "./icons/ZapIcon": { + "types": "./icons/ZapIcon/dist/keystone-ui-icons-icons-ZapIcon.cjs.js", "module": "./icons/ZapIcon/dist/keystone-ui-icons-icons-ZapIcon.esm.js", "default": "./icons/ZapIcon/dist/keystone-ui-icons-icons-ZapIcon.cjs.js" }, "./icons/BellIcon": { + "types": "./icons/BellIcon/dist/keystone-ui-icons-icons-BellIcon.cjs.js", "module": "./icons/BellIcon/dist/keystone-ui-icons-icons-BellIcon.esm.js", "default": "./icons/BellIcon/dist/keystone-ui-icons-icons-BellIcon.cjs.js" }, "./icons/BoldIcon": { + "types": "./icons/BoldIcon/dist/keystone-ui-icons-icons-BoldIcon.cjs.js", "module": "./icons/BoldIcon/dist/keystone-ui-icons-icons-BoldIcon.esm.js", "default": "./icons/BoldIcon/dist/keystone-ui-icons-icons-BoldIcon.cjs.js" }, "./icons/BookIcon": { + "types": "./icons/BookIcon/dist/keystone-ui-icons-icons-BookIcon.cjs.js", "module": "./icons/BookIcon/dist/keystone-ui-icons-icons-BookIcon.esm.js", "default": "./icons/BookIcon/dist/keystone-ui-icons-icons-BookIcon.cjs.js" }, "./icons/CastIcon": { + "types": "./icons/CastIcon/dist/keystone-ui-icons-icons-CastIcon.cjs.js", "module": "./icons/CastIcon/dist/keystone-ui-icons-icons-CastIcon.esm.js", "default": "./icons/CastIcon/dist/keystone-ui-icons-icons-CastIcon.cjs.js" }, "./icons/CodeIcon": { + "types": "./icons/CodeIcon/dist/keystone-ui-icons-icons-CodeIcon.cjs.js", "module": "./icons/CodeIcon/dist/keystone-ui-icons-icons-CodeIcon.esm.js", "default": "./icons/CodeIcon/dist/keystone-ui-icons-icons-CodeIcon.cjs.js" }, "./icons/CopyIcon": { + "types": "./icons/CopyIcon/dist/keystone-ui-icons-icons-CopyIcon.cjs.js", "module": "./icons/CopyIcon/dist/keystone-ui-icons-icons-CopyIcon.esm.js", "default": "./icons/CopyIcon/dist/keystone-ui-icons-icons-CopyIcon.cjs.js" }, "./icons/CropIcon": { + "types": "./icons/CropIcon/dist/keystone-ui-icons-icons-CropIcon.cjs.js", "module": "./icons/CropIcon/dist/keystone-ui-icons-icons-CropIcon.esm.js", "default": "./icons/CropIcon/dist/keystone-ui-icons-icons-CropIcon.cjs.js" }, "./icons/DiscIcon": { + "types": "./icons/DiscIcon/dist/keystone-ui-icons-icons-DiscIcon.cjs.js", "module": "./icons/DiscIcon/dist/keystone-ui-icons-icons-DiscIcon.esm.js", "default": "./icons/DiscIcon/dist/keystone-ui-icons-icons-DiscIcon.cjs.js" }, "./icons/EditIcon": { + "types": "./icons/EditIcon/dist/keystone-ui-icons-icons-EditIcon.cjs.js", "module": "./icons/EditIcon/dist/keystone-ui-icons-icons-EditIcon.esm.js", "default": "./icons/EditIcon/dist/keystone-ui-icons-icons-EditIcon.cjs.js" }, "./icons/FileIcon": { + "types": "./icons/FileIcon/dist/keystone-ui-icons-icons-FileIcon.cjs.js", "module": "./icons/FileIcon/dist/keystone-ui-icons-icons-FileIcon.esm.js", "default": "./icons/FileIcon/dist/keystone-ui-icons-icons-FileIcon.cjs.js" }, "./icons/FilmIcon": { + "types": "./icons/FilmIcon/dist/keystone-ui-icons-icons-FilmIcon.cjs.js", "module": "./icons/FilmIcon/dist/keystone-ui-icons-icons-FilmIcon.esm.js", "default": "./icons/FilmIcon/dist/keystone-ui-icons-icons-FilmIcon.cjs.js" }, "./icons/FlagIcon": { + "types": "./icons/FlagIcon/dist/keystone-ui-icons-icons-FlagIcon.cjs.js", "module": "./icons/FlagIcon/dist/keystone-ui-icons-icons-FlagIcon.esm.js", "default": "./icons/FlagIcon/dist/keystone-ui-icons-icons-FlagIcon.cjs.js" }, "./icons/GiftIcon": { + "types": "./icons/GiftIcon/dist/keystone-ui-icons-icons-GiftIcon.cjs.js", "module": "./icons/GiftIcon/dist/keystone-ui-icons-icons-GiftIcon.esm.js", "default": "./icons/GiftIcon/dist/keystone-ui-icons-icons-GiftIcon.cjs.js" }, "./icons/GridIcon": { + "types": "./icons/GridIcon/dist/keystone-ui-icons-icons-GridIcon.cjs.js", "module": "./icons/GridIcon/dist/keystone-ui-icons-icons-GridIcon.esm.js", "default": "./icons/GridIcon/dist/keystone-ui-icons-icons-GridIcon.cjs.js" }, "./icons/HashIcon": { + "types": "./icons/HashIcon/dist/keystone-ui-icons-icons-HashIcon.cjs.js", "module": "./icons/HashIcon/dist/keystone-ui-icons-icons-HashIcon.esm.js", "default": "./icons/HashIcon/dist/keystone-ui-icons-icons-HashIcon.cjs.js" }, "./icons/HomeIcon": { + "types": "./icons/HomeIcon/dist/keystone-ui-icons-icons-HomeIcon.cjs.js", "module": "./icons/HomeIcon/dist/keystone-ui-icons-icons-HomeIcon.esm.js", "default": "./icons/HomeIcon/dist/keystone-ui-icons-icons-HomeIcon.cjs.js" }, "./icons/InfoIcon": { + "types": "./icons/InfoIcon/dist/keystone-ui-icons-icons-InfoIcon.cjs.js", "module": "./icons/InfoIcon/dist/keystone-ui-icons-icons-InfoIcon.esm.js", "default": "./icons/InfoIcon/dist/keystone-ui-icons-icons-InfoIcon.cjs.js" }, "./icons/LinkIcon": { + "types": "./icons/LinkIcon/dist/keystone-ui-icons-icons-LinkIcon.cjs.js", "module": "./icons/LinkIcon/dist/keystone-ui-icons-icons-LinkIcon.esm.js", "default": "./icons/LinkIcon/dist/keystone-ui-icons-icons-LinkIcon.cjs.js" }, "./icons/ListIcon": { + "types": "./icons/ListIcon/dist/keystone-ui-icons-icons-ListIcon.cjs.js", "module": "./icons/ListIcon/dist/keystone-ui-icons-icons-ListIcon.esm.js", "default": "./icons/ListIcon/dist/keystone-ui-icons-icons-ListIcon.cjs.js" }, "./icons/LockIcon": { + "types": "./icons/LockIcon/dist/keystone-ui-icons-icons-LockIcon.cjs.js", "module": "./icons/LockIcon/dist/keystone-ui-icons-icons-LockIcon.esm.js", "default": "./icons/LockIcon/dist/keystone-ui-icons-icons-LockIcon.cjs.js" }, "./icons/MailIcon": { + "types": "./icons/MailIcon/dist/keystone-ui-icons-icons-MailIcon.cjs.js", "module": "./icons/MailIcon/dist/keystone-ui-icons-icons-MailIcon.esm.js", "default": "./icons/MailIcon/dist/keystone-ui-icons-icons-MailIcon.cjs.js" }, "./icons/MenuIcon": { + "types": "./icons/MenuIcon/dist/keystone-ui-icons-icons-MenuIcon.cjs.js", "module": "./icons/MenuIcon/dist/keystone-ui-icons-icons-MenuIcon.esm.js", "default": "./icons/MenuIcon/dist/keystone-ui-icons-icons-MenuIcon.cjs.js" }, "./icons/MoonIcon": { + "types": "./icons/MoonIcon/dist/keystone-ui-icons-icons-MoonIcon.cjs.js", "module": "./icons/MoonIcon/dist/keystone-ui-icons-icons-MoonIcon.esm.js", "default": "./icons/MoonIcon/dist/keystone-ui-icons-icons-MoonIcon.cjs.js" }, "./icons/MoveIcon": { + "types": "./icons/MoveIcon/dist/keystone-ui-icons-icons-MoveIcon.cjs.js", "module": "./icons/MoveIcon/dist/keystone-ui-icons-icons-MoveIcon.esm.js", "default": "./icons/MoveIcon/dist/keystone-ui-icons-icons-MoveIcon.cjs.js" }, "./icons/PlayIcon": { + "types": "./icons/PlayIcon/dist/keystone-ui-icons-icons-PlayIcon.cjs.js", "module": "./icons/PlayIcon/dist/keystone-ui-icons-icons-PlayIcon.esm.js", "default": "./icons/PlayIcon/dist/keystone-ui-icons-icons-PlayIcon.cjs.js" }, "./icons/PlusIcon": { + "types": "./icons/PlusIcon/dist/keystone-ui-icons-icons-PlusIcon.cjs.js", "module": "./icons/PlusIcon/dist/keystone-ui-icons-icons-PlusIcon.esm.js", "default": "./icons/PlusIcon/dist/keystone-ui-icons-icons-PlusIcon.cjs.js" }, "./icons/SaveIcon": { + "types": "./icons/SaveIcon/dist/keystone-ui-icons-icons-SaveIcon.cjs.js", "module": "./icons/SaveIcon/dist/keystone-ui-icons-icons-SaveIcon.esm.js", "default": "./icons/SaveIcon/dist/keystone-ui-icons-icons-SaveIcon.cjs.js" }, "./icons/SendIcon": { + "types": "./icons/SendIcon/dist/keystone-ui-icons-icons-SendIcon.cjs.js", "module": "./icons/SendIcon/dist/keystone-ui-icons-icons-SendIcon.esm.js", "default": "./icons/SendIcon/dist/keystone-ui-icons-icons-SendIcon.cjs.js" }, "./icons/StarIcon": { + "types": "./icons/StarIcon/dist/keystone-ui-icons-icons-StarIcon.cjs.js", "module": "./icons/StarIcon/dist/keystone-ui-icons-icons-StarIcon.esm.js", "default": "./icons/StarIcon/dist/keystone-ui-icons-icons-StarIcon.cjs.js" }, "./icons/ToolIcon": { + "types": "./icons/ToolIcon/dist/keystone-ui-icons-icons-ToolIcon.cjs.js", "module": "./icons/ToolIcon/dist/keystone-ui-icons-icons-ToolIcon.esm.js", "default": "./icons/ToolIcon/dist/keystone-ui-icons-icons-ToolIcon.cjs.js" }, "./icons/TypeIcon": { + "types": "./icons/TypeIcon/dist/keystone-ui-icons-icons-TypeIcon.cjs.js", "module": "./icons/TypeIcon/dist/keystone-ui-icons-icons-TypeIcon.esm.js", "default": "./icons/TypeIcon/dist/keystone-ui-icons-icons-TypeIcon.cjs.js" }, "./icons/UserIcon": { + "types": "./icons/UserIcon/dist/keystone-ui-icons-icons-UserIcon.cjs.js", "module": "./icons/UserIcon/dist/keystone-ui-icons-icons-UserIcon.esm.js", "default": "./icons/UserIcon/dist/keystone-ui-icons-icons-UserIcon.cjs.js" }, "./icons/WifiIcon": { + "types": "./icons/WifiIcon/dist/keystone-ui-icons-icons-WifiIcon.cjs.js", "module": "./icons/WifiIcon/dist/keystone-ui-icons-icons-WifiIcon.esm.js", "default": "./icons/WifiIcon/dist/keystone-ui-icons-icons-WifiIcon.cjs.js" }, "./icons/WindIcon": { + "types": "./icons/WindIcon/dist/keystone-ui-icons-icons-WindIcon.cjs.js", "module": "./icons/WindIcon/dist/keystone-ui-icons-icons-WindIcon.esm.js", "default": "./icons/WindIcon/dist/keystone-ui-icons-icons-WindIcon.cjs.js" }, "./icons/AwardIcon": { + "types": "./icons/AwardIcon/dist/keystone-ui-icons-icons-AwardIcon.cjs.js", "module": "./icons/AwardIcon/dist/keystone-ui-icons-icons-AwardIcon.esm.js", "default": "./icons/AwardIcon/dist/keystone-ui-icons-icons-AwardIcon.cjs.js" }, "./icons/CheckIcon": { + "types": "./icons/CheckIcon/dist/keystone-ui-icons-icons-CheckIcon.cjs.js", "module": "./icons/CheckIcon/dist/keystone-ui-icons-icons-CheckIcon.esm.js", "default": "./icons/CheckIcon/dist/keystone-ui-icons-icons-CheckIcon.cjs.js" }, "./icons/ClockIcon": { + "types": "./icons/ClockIcon/dist/keystone-ui-icons-icons-ClockIcon.cjs.js", "module": "./icons/ClockIcon/dist/keystone-ui-icons-icons-ClockIcon.esm.js", "default": "./icons/ClockIcon/dist/keystone-ui-icons-icons-ClockIcon.cjs.js" }, "./icons/CloudIcon": { + "types": "./icons/CloudIcon/dist/keystone-ui-icons-icons-CloudIcon.cjs.js", "module": "./icons/CloudIcon/dist/keystone-ui-icons-icons-CloudIcon.esm.js", "default": "./icons/CloudIcon/dist/keystone-ui-icons-icons-CloudIcon.cjs.js" }, "./icons/Edit2Icon": { + "types": "./icons/Edit2Icon/dist/keystone-ui-icons-icons-Edit2Icon.cjs.js", "module": "./icons/Edit2Icon/dist/keystone-ui-icons-icons-Edit2Icon.esm.js", "default": "./icons/Edit2Icon/dist/keystone-ui-icons-icons-Edit2Icon.cjs.js" }, "./icons/Edit3Icon": { + "types": "./icons/Edit3Icon/dist/keystone-ui-icons-icons-Edit3Icon.cjs.js", "module": "./icons/Edit3Icon/dist/keystone-ui-icons-icons-Edit3Icon.esm.js", "default": "./icons/Edit3Icon/dist/keystone-ui-icons-icons-Edit3Icon.cjs.js" }, "./icons/FigmaIcon": { + "types": "./icons/FigmaIcon/dist/keystone-ui-icons-icons-FigmaIcon.cjs.js", "module": "./icons/FigmaIcon/dist/keystone-ui-icons-icons-FigmaIcon.esm.js", "default": "./icons/FigmaIcon/dist/keystone-ui-icons-icons-FigmaIcon.cjs.js" }, "./icons/FrownIcon": { + "types": "./icons/FrownIcon/dist/keystone-ui-icons-icons-FrownIcon.cjs.js", "module": "./icons/FrownIcon/dist/keystone-ui-icons-icons-FrownIcon.esm.js", "default": "./icons/FrownIcon/dist/keystone-ui-icons-icons-FrownIcon.cjs.js" }, "./icons/GlobeIcon": { + "types": "./icons/GlobeIcon/dist/keystone-ui-icons-icons-GlobeIcon.cjs.js", "module": "./icons/GlobeIcon/dist/keystone-ui-icons-icons-GlobeIcon.esm.js", "default": "./icons/GlobeIcon/dist/keystone-ui-icons-icons-GlobeIcon.cjs.js" }, "./icons/HeartIcon": { + "types": "./icons/HeartIcon/dist/keystone-ui-icons-icons-HeartIcon.cjs.js", "module": "./icons/HeartIcon/dist/keystone-ui-icons-icons-HeartIcon.esm.js", "default": "./icons/HeartIcon/dist/keystone-ui-icons-icons-HeartIcon.cjs.js" }, "./icons/ImageIcon": { + "types": "./icons/ImageIcon/dist/keystone-ui-icons-icons-ImageIcon.cjs.js", "module": "./icons/ImageIcon/dist/keystone-ui-icons-icons-ImageIcon.esm.js", "default": "./icons/ImageIcon/dist/keystone-ui-icons-icons-ImageIcon.cjs.js" }, "./icons/InboxIcon": { + "types": "./icons/InboxIcon/dist/keystone-ui-icons-icons-InboxIcon.cjs.js", "module": "./icons/InboxIcon/dist/keystone-ui-icons-icons-InboxIcon.esm.js", "default": "./icons/InboxIcon/dist/keystone-ui-icons-icons-InboxIcon.cjs.js" }, "./icons/Link2Icon": { + "types": "./icons/Link2Icon/dist/keystone-ui-icons-icons-Link2Icon.cjs.js", "module": "./icons/Link2Icon/dist/keystone-ui-icons-icons-Link2Icon.esm.js", "default": "./icons/Link2Icon/dist/keystone-ui-icons-icons-Link2Icon.cjs.js" }, "./icons/LogInIcon": { + "types": "./icons/LogInIcon/dist/keystone-ui-icons-icons-LogInIcon.cjs.js", "module": "./icons/LogInIcon/dist/keystone-ui-icons-icons-LogInIcon.esm.js", "default": "./icons/LogInIcon/dist/keystone-ui-icons-icons-LogInIcon.cjs.js" }, "./icons/MinusIcon": { + "types": "./icons/MinusIcon/dist/keystone-ui-icons-icons-MinusIcon.cjs.js", "module": "./icons/MinusIcon/dist/keystone-ui-icons-icons-MinusIcon.esm.js", "default": "./icons/MinusIcon/dist/keystone-ui-icons-icons-MinusIcon.cjs.js" }, "./icons/MusicIcon": { + "types": "./icons/MusicIcon/dist/keystone-ui-icons-icons-MusicIcon.cjs.js", "module": "./icons/MusicIcon/dist/keystone-ui-icons-icons-MusicIcon.esm.js", "default": "./icons/MusicIcon/dist/keystone-ui-icons-icons-MusicIcon.cjs.js" }, "./icons/PauseIcon": { + "types": "./icons/PauseIcon/dist/keystone-ui-icons-icons-PauseIcon.cjs.js", "module": "./icons/PauseIcon/dist/keystone-ui-icons-icons-PauseIcon.esm.js", "default": "./icons/PauseIcon/dist/keystone-ui-icons-icons-PauseIcon.cjs.js" }, "./icons/PhoneIcon": { + "types": "./icons/PhoneIcon/dist/keystone-ui-icons-icons-PhoneIcon.cjs.js", "module": "./icons/PhoneIcon/dist/keystone-ui-icons-icons-PhoneIcon.esm.js", "default": "./icons/PhoneIcon/dist/keystone-ui-icons-icons-PhoneIcon.cjs.js" }, "./icons/PowerIcon": { + "types": "./icons/PowerIcon/dist/keystone-ui-icons-icons-PowerIcon.cjs.js", "module": "./icons/PowerIcon/dist/keystone-ui-icons-icons-PowerIcon.esm.js", "default": "./icons/PowerIcon/dist/keystone-ui-icons-icons-PowerIcon.cjs.js" }, "./icons/RadioIcon": { + "types": "./icons/RadioIcon/dist/keystone-ui-icons-icons-RadioIcon.cjs.js", "module": "./icons/RadioIcon/dist/keystone-ui-icons-icons-RadioIcon.esm.js", "default": "./icons/RadioIcon/dist/keystone-ui-icons-icons-RadioIcon.cjs.js" }, "./icons/ShareIcon": { + "types": "./icons/ShareIcon/dist/keystone-ui-icons-icons-ShareIcon.cjs.js", "module": "./icons/ShareIcon/dist/keystone-ui-icons-icons-ShareIcon.esm.js", "default": "./icons/ShareIcon/dist/keystone-ui-icons-icons-ShareIcon.cjs.js" }, "./icons/SlackIcon": { + "types": "./icons/SlackIcon/dist/keystone-ui-icons-icons-SlackIcon.cjs.js", "module": "./icons/SlackIcon/dist/keystone-ui-icons-icons-SlackIcon.esm.js", "default": "./icons/SlackIcon/dist/keystone-ui-icons-icons-SlackIcon.cjs.js" }, "./icons/SlashIcon": { + "types": "./icons/SlashIcon/dist/keystone-ui-icons-icons-SlashIcon.cjs.js", "module": "./icons/SlashIcon/dist/keystone-ui-icons-icons-SlashIcon.esm.js", "default": "./icons/SlashIcon/dist/keystone-ui-icons-icons-SlashIcon.cjs.js" }, "./icons/SmileIcon": { + "types": "./icons/SmileIcon/dist/keystone-ui-icons-icons-SmileIcon.cjs.js", "module": "./icons/SmileIcon/dist/keystone-ui-icons-icons-SmileIcon.esm.js", "default": "./icons/SmileIcon/dist/keystone-ui-icons-icons-SmileIcon.cjs.js" }, "./icons/TrashIcon": { + "types": "./icons/TrashIcon/dist/keystone-ui-icons-icons-TrashIcon.cjs.js", "module": "./icons/TrashIcon/dist/keystone-ui-icons-icons-TrashIcon.esm.js", "default": "./icons/TrashIcon/dist/keystone-ui-icons-icons-TrashIcon.cjs.js" }, "./icons/TruckIcon": { + "types": "./icons/TruckIcon/dist/keystone-ui-icons-icons-TruckIcon.cjs.js", "module": "./icons/TruckIcon/dist/keystone-ui-icons-icons-TruckIcon.esm.js", "default": "./icons/TruckIcon/dist/keystone-ui-icons-icons-TruckIcon.cjs.js" }, "./icons/UserXIcon": { + "types": "./icons/UserXIcon/dist/keystone-ui-icons-icons-UserXIcon.cjs.js", "module": "./icons/UserXIcon/dist/keystone-ui-icons-icons-UserXIcon.esm.js", "default": "./icons/UserXIcon/dist/keystone-ui-icons-icons-UserXIcon.cjs.js" }, "./icons/UsersIcon": { + "types": "./icons/UsersIcon/dist/keystone-ui-icons-icons-UsersIcon.cjs.js", "module": "./icons/UsersIcon/dist/keystone-ui-icons-icons-UsersIcon.esm.js", "default": "./icons/UsersIcon/dist/keystone-ui-icons-icons-UsersIcon.cjs.js" }, "./icons/VideoIcon": { + "types": "./icons/VideoIcon/dist/keystone-ui-icons-icons-VideoIcon.cjs.js", "module": "./icons/VideoIcon/dist/keystone-ui-icons-icons-VideoIcon.esm.js", "default": "./icons/VideoIcon/dist/keystone-ui-icons-icons-VideoIcon.cjs.js" }, "./icons/WatchIcon": { + "types": "./icons/WatchIcon/dist/keystone-ui-icons-icons-WatchIcon.cjs.js", "module": "./icons/WatchIcon/dist/keystone-ui-icons-icons-WatchIcon.esm.js", "default": "./icons/WatchIcon/dist/keystone-ui-icons-icons-WatchIcon.cjs.js" }, "./icons/AnchorIcon": { + "types": "./icons/AnchorIcon/dist/keystone-ui-icons-icons-AnchorIcon.cjs.js", "module": "./icons/AnchorIcon/dist/keystone-ui-icons-icons-AnchorIcon.esm.js", "default": "./icons/AnchorIcon/dist/keystone-ui-icons-icons-AnchorIcon.cjs.js" }, "./icons/AtSignIcon": { + "types": "./icons/AtSignIcon/dist/keystone-ui-icons-icons-AtSignIcon.cjs.js", "module": "./icons/AtSignIcon/dist/keystone-ui-icons-icons-AtSignIcon.esm.js", "default": "./icons/AtSignIcon/dist/keystone-ui-icons-icons-AtSignIcon.cjs.js" }, "./icons/CameraIcon": { + "types": "./icons/CameraIcon/dist/keystone-ui-icons-icons-CameraIcon.cjs.js", "module": "./icons/CameraIcon/dist/keystone-ui-icons-icons-CameraIcon.esm.js", "default": "./icons/CameraIcon/dist/keystone-ui-icons-icons-CameraIcon.cjs.js" }, "./icons/ChromeIcon": { + "types": "./icons/ChromeIcon/dist/keystone-ui-icons-icons-ChromeIcon.cjs.js", "module": "./icons/ChromeIcon/dist/keystone-ui-icons-icons-ChromeIcon.esm.js", "default": "./icons/ChromeIcon/dist/keystone-ui-icons-icons-ChromeIcon.cjs.js" }, "./icons/CircleIcon": { + "types": "./icons/CircleIcon/dist/keystone-ui-icons-icons-CircleIcon.cjs.js", "module": "./icons/CircleIcon/dist/keystone-ui-icons-icons-CircleIcon.esm.js", "default": "./icons/CircleIcon/dist/keystone-ui-icons-icons-CircleIcon.cjs.js" }, "./icons/CoffeeIcon": { + "types": "./icons/CoffeeIcon/dist/keystone-ui-icons-icons-CoffeeIcon.cjs.js", "module": "./icons/CoffeeIcon/dist/keystone-ui-icons-icons-CoffeeIcon.esm.js", "default": "./icons/CoffeeIcon/dist/keystone-ui-icons-icons-CoffeeIcon.cjs.js" }, "./icons/DeleteIcon": { + "types": "./icons/DeleteIcon/dist/keystone-ui-icons-icons-DeleteIcon.cjs.js", "module": "./icons/DeleteIcon/dist/keystone-ui-icons-icons-DeleteIcon.esm.js", "default": "./icons/DeleteIcon/dist/keystone-ui-icons-icons-DeleteIcon.cjs.js" }, "./icons/DivideIcon": { + "types": "./icons/DivideIcon/dist/keystone-ui-icons-icons-DivideIcon.cjs.js", "module": "./icons/DivideIcon/dist/keystone-ui-icons-icons-DivideIcon.esm.js", "default": "./icons/DivideIcon/dist/keystone-ui-icons-icons-DivideIcon.cjs.js" }, "./icons/EyeOffIcon": { + "types": "./icons/EyeOffIcon/dist/keystone-ui-icons-icons-EyeOffIcon.cjs.js", "module": "./icons/EyeOffIcon/dist/keystone-ui-icons-icons-EyeOffIcon.esm.js", "default": "./icons/EyeOffIcon/dist/keystone-ui-icons-icons-EyeOffIcon.cjs.js" }, "./icons/FilterIcon": { + "types": "./icons/FilterIcon/dist/keystone-ui-icons-icons-FilterIcon.cjs.js", "module": "./icons/FilterIcon/dist/keystone-ui-icons-icons-FilterIcon.esm.js", "default": "./icons/FilterIcon/dist/keystone-ui-icons-icons-FilterIcon.cjs.js" }, "./icons/FolderIcon": { + "types": "./icons/FolderIcon/dist/keystone-ui-icons-icons-FolderIcon.cjs.js", "module": "./icons/FolderIcon/dist/keystone-ui-icons-icons-FolderIcon.esm.js", "default": "./icons/FolderIcon/dist/keystone-ui-icons-icons-FolderIcon.cjs.js" }, "./icons/FramerIcon": { + "types": "./icons/FramerIcon/dist/keystone-ui-icons-icons-FramerIcon.cjs.js", "module": "./icons/FramerIcon/dist/keystone-ui-icons-icons-FramerIcon.esm.js", "default": "./icons/FramerIcon/dist/keystone-ui-icons-icons-FramerIcon.cjs.js" }, "./icons/GithubIcon": { + "types": "./icons/GithubIcon/dist/keystone-ui-icons-icons-GithubIcon.cjs.js", "module": "./icons/GithubIcon/dist/keystone-ui-icons-icons-GithubIcon.esm.js", "default": "./icons/GithubIcon/dist/keystone-ui-icons-icons-GithubIcon.cjs.js" }, "./icons/GitlabIcon": { + "types": "./icons/GitlabIcon/dist/keystone-ui-icons-icons-GitlabIcon.cjs.js", "module": "./icons/GitlabIcon/dist/keystone-ui-icons-icons-GitlabIcon.esm.js", "default": "./icons/GitlabIcon/dist/keystone-ui-icons-icons-GitlabIcon.cjs.js" }, "./icons/ItalicIcon": { + "types": "./icons/ItalicIcon/dist/keystone-ui-icons-icons-ItalicIcon.cjs.js", "module": "./icons/ItalicIcon/dist/keystone-ui-icons-icons-ItalicIcon.esm.js", "default": "./icons/ItalicIcon/dist/keystone-ui-icons-icons-ItalicIcon.cjs.js" }, "./icons/LayersIcon": { + "types": "./icons/LayersIcon/dist/keystone-ui-icons-icons-LayersIcon.cjs.js", "module": "./icons/LayersIcon/dist/keystone-ui-icons-icons-LayersIcon.esm.js", "default": "./icons/LayersIcon/dist/keystone-ui-icons-icons-LayersIcon.cjs.js" }, "./icons/LayoutIcon": { + "types": "./icons/LayoutIcon/dist/keystone-ui-icons-icons-LayoutIcon.cjs.js", "module": "./icons/LayoutIcon/dist/keystone-ui-icons-icons-LayoutIcon.esm.js", "default": "./icons/LayoutIcon/dist/keystone-ui-icons-icons-LayoutIcon.cjs.js" }, "./icons/LoaderIcon": { + "types": "./icons/LoaderIcon/dist/keystone-ui-icons-icons-LoaderIcon.cjs.js", "module": "./icons/LoaderIcon/dist/keystone-ui-icons-icons-LoaderIcon.esm.js", "default": "./icons/LoaderIcon/dist/keystone-ui-icons-icons-LoaderIcon.cjs.js" }, "./icons/LogOutIcon": { + "types": "./icons/LogOutIcon/dist/keystone-ui-icons-icons-LogOutIcon.cjs.js", "module": "./icons/LogOutIcon/dist/keystone-ui-icons-icons-LogOutIcon.esm.js", "default": "./icons/LogOutIcon/dist/keystone-ui-icons-icons-LogOutIcon.cjs.js" }, "./icons/MapPinIcon": { + "types": "./icons/MapPinIcon/dist/keystone-ui-icons-icons-MapPinIcon.cjs.js", "module": "./icons/MapPinIcon/dist/keystone-ui-icons-icons-MapPinIcon.esm.js", "default": "./icons/MapPinIcon/dist/keystone-ui-icons-icons-MapPinIcon.cjs.js" }, "./icons/MicOffIcon": { + "types": "./icons/MicOffIcon/dist/keystone-ui-icons-icons-MicOffIcon.cjs.js", "module": "./icons/MicOffIcon/dist/keystone-ui-icons-icons-MicOffIcon.esm.js", "default": "./icons/MicOffIcon/dist/keystone-ui-icons-icons-MicOffIcon.cjs.js" }, "./icons/PocketIcon": { + "types": "./icons/PocketIcon/dist/keystone-ui-icons-icons-PocketIcon.cjs.js", "module": "./icons/PocketIcon/dist/keystone-ui-icons-icons-PocketIcon.esm.js", "default": "./icons/PocketIcon/dist/keystone-ui-icons-icons-PocketIcon.cjs.js" }, "./icons/RepeatIcon": { + "types": "./icons/RepeatIcon/dist/keystone-ui-icons-icons-RepeatIcon.cjs.js", "module": "./icons/RepeatIcon/dist/keystone-ui-icons-icons-RepeatIcon.esm.js", "default": "./icons/RepeatIcon/dist/keystone-ui-icons-icons-RepeatIcon.cjs.js" }, "./icons/RewindIcon": { + "types": "./icons/RewindIcon/dist/keystone-ui-icons-icons-RewindIcon.cjs.js", "module": "./icons/RewindIcon/dist/keystone-ui-icons-icons-RewindIcon.esm.js", "default": "./icons/RewindIcon/dist/keystone-ui-icons-icons-RewindIcon.cjs.js" }, "./icons/SearchIcon": { + "types": "./icons/SearchIcon/dist/keystone-ui-icons-icons-SearchIcon.cjs.js", "module": "./icons/SearchIcon/dist/keystone-ui-icons-icons-SearchIcon.esm.js", "default": "./icons/SearchIcon/dist/keystone-ui-icons-icons-SearchIcon.cjs.js" }, "./icons/ServerIcon": { + "types": "./icons/ServerIcon/dist/keystone-ui-icons-icons-ServerIcon.cjs.js", "module": "./icons/ServerIcon/dist/keystone-ui-icons-icons-ServerIcon.esm.js", "default": "./icons/ServerIcon/dist/keystone-ui-icons-icons-ServerIcon.cjs.js" }, "./icons/Share2Icon": { + "types": "./icons/Share2Icon/dist/keystone-ui-icons-icons-Share2Icon.cjs.js", "module": "./icons/Share2Icon/dist/keystone-ui-icons-icons-Share2Icon.esm.js", "default": "./icons/Share2Icon/dist/keystone-ui-icons-icons-Share2Icon.cjs.js" }, "./icons/ShieldIcon": { + "types": "./icons/ShieldIcon/dist/keystone-ui-icons-icons-ShieldIcon.cjs.js", "module": "./icons/ShieldIcon/dist/keystone-ui-icons-icons-ShieldIcon.esm.js", "default": "./icons/ShieldIcon/dist/keystone-ui-icons-icons-ShieldIcon.cjs.js" }, "./icons/SquareIcon": { + "types": "./icons/SquareIcon/dist/keystone-ui-icons-icons-SquareIcon.cjs.js", "module": "./icons/SquareIcon/dist/keystone-ui-icons-icons-SquareIcon.esm.js", "default": "./icons/SquareIcon/dist/keystone-ui-icons-icons-SquareIcon.cjs.js" }, "./icons/SunsetIcon": { + "types": "./icons/SunsetIcon/dist/keystone-ui-icons-icons-SunsetIcon.cjs.js", "module": "./icons/SunsetIcon/dist/keystone-ui-icons-icons-SunsetIcon.esm.js", "default": "./icons/SunsetIcon/dist/keystone-ui-icons-icons-SunsetIcon.cjs.js" }, "./icons/TabletIcon": { + "types": "./icons/TabletIcon/dist/keystone-ui-icons-icons-TabletIcon.cjs.js", "module": "./icons/TabletIcon/dist/keystone-ui-icons-icons-TabletIcon.esm.js", "default": "./icons/TabletIcon/dist/keystone-ui-icons-icons-TabletIcon.cjs.js" }, "./icons/TargetIcon": { + "types": "./icons/TargetIcon/dist/keystone-ui-icons-icons-TargetIcon.cjs.js", "module": "./icons/TargetIcon/dist/keystone-ui-icons-icons-TargetIcon.esm.js", "default": "./icons/TargetIcon/dist/keystone-ui-icons-icons-TargetIcon.cjs.js" }, "./icons/Trash2Icon": { + "types": "./icons/Trash2Icon/dist/keystone-ui-icons-icons-Trash2Icon.cjs.js", "module": "./icons/Trash2Icon/dist/keystone-ui-icons-icons-Trash2Icon.esm.js", "default": "./icons/Trash2Icon/dist/keystone-ui-icons-icons-Trash2Icon.cjs.js" }, "./icons/TrelloIcon": { + "types": "./icons/TrelloIcon/dist/keystone-ui-icons-icons-TrelloIcon.cjs.js", "module": "./icons/TrelloIcon/dist/keystone-ui-icons-icons-TrelloIcon.esm.js", "default": "./icons/TrelloIcon/dist/keystone-ui-icons-icons-TrelloIcon.cjs.js" }, "./icons/TwitchIcon": { + "types": "./icons/TwitchIcon/dist/keystone-ui-icons-icons-TwitchIcon.cjs.js", "module": "./icons/TwitchIcon/dist/keystone-ui-icons-icons-TwitchIcon.esm.js", "default": "./icons/TwitchIcon/dist/keystone-ui-icons-icons-TwitchIcon.cjs.js" }, "./icons/UnlockIcon": { + "types": "./icons/UnlockIcon/dist/keystone-ui-icons-icons-UnlockIcon.cjs.js", "module": "./icons/UnlockIcon/dist/keystone-ui-icons-icons-UnlockIcon.esm.js", "default": "./icons/UnlockIcon/dist/keystone-ui-icons-icons-UnlockIcon.cjs.js" }, "./icons/UploadIcon": { + "types": "./icons/UploadIcon/dist/keystone-ui-icons-icons-UploadIcon.cjs.js", "module": "./icons/UploadIcon/dist/keystone-ui-icons-icons-UploadIcon.esm.js", "default": "./icons/UploadIcon/dist/keystone-ui-icons-icons-UploadIcon.cjs.js" }, "./icons/VolumeIcon": { + "types": "./icons/VolumeIcon/dist/keystone-ui-icons-icons-VolumeIcon.cjs.js", "module": "./icons/VolumeIcon/dist/keystone-ui-icons-icons-VolumeIcon.esm.js", "default": "./icons/VolumeIcon/dist/keystone-ui-icons-icons-VolumeIcon.cjs.js" }, "./icons/ZapOffIcon": { + "types": "./icons/ZapOffIcon/dist/keystone-ui-icons-icons-ZapOffIcon.cjs.js", "module": "./icons/ZapOffIcon/dist/keystone-ui-icons-icons-ZapOffIcon.esm.js", "default": "./icons/ZapOffIcon/dist/keystone-ui-icons-icons-ZapOffIcon.cjs.js" }, "./icons/ZoomInIcon": { + "types": "./icons/ZoomInIcon/dist/keystone-ui-icons-icons-ZoomInIcon.cjs.js", "module": "./icons/ZoomInIcon/dist/keystone-ui-icons-icons-ZoomInIcon.esm.js", "default": "./icons/ZoomInIcon/dist/keystone-ui-icons-icons-ZoomInIcon.cjs.js" }, "./icons/AirplayIcon": { + "types": "./icons/AirplayIcon/dist/keystone-ui-icons-icons-AirplayIcon.cjs.js", "module": "./icons/AirplayIcon/dist/keystone-ui-icons-icons-AirplayIcon.esm.js", "default": "./icons/AirplayIcon/dist/keystone-ui-icons-icons-AirplayIcon.cjs.js" }, "./icons/ArchiveIcon": { + "types": "./icons/ArchiveIcon/dist/keystone-ui-icons-icons-ArchiveIcon.cjs.js", "module": "./icons/ArchiveIcon/dist/keystone-ui-icons-icons-ArchiveIcon.esm.js", "default": "./icons/ArchiveIcon/dist/keystone-ui-icons-icons-ArchiveIcon.cjs.js" }, "./icons/ArrowUpIcon": { + "types": "./icons/ArrowUpIcon/dist/keystone-ui-icons-icons-ArrowUpIcon.cjs.js", "module": "./icons/ArrowUpIcon/dist/keystone-ui-icons-icons-ArrowUpIcon.esm.js", "default": "./icons/ArrowUpIcon/dist/keystone-ui-icons-icons-ArrowUpIcon.cjs.js" }, "./icons/BatteryIcon": { + "types": "./icons/BatteryIcon/dist/keystone-ui-icons-icons-BatteryIcon.cjs.js", "module": "./icons/BatteryIcon/dist/keystone-ui-icons-icons-BatteryIcon.esm.js", "default": "./icons/BatteryIcon/dist/keystone-ui-icons-icons-BatteryIcon.cjs.js" }, "./icons/BellOffIcon": { + "types": "./icons/BellOffIcon/dist/keystone-ui-icons-icons-BellOffIcon.cjs.js", "module": "./icons/BellOffIcon/dist/keystone-ui-icons-icons-BellOffIcon.esm.js", "default": "./icons/BellOffIcon/dist/keystone-ui-icons-icons-BellOffIcon.cjs.js" }, "./icons/CodepenIcon": { + "types": "./icons/CodepenIcon/dist/keystone-ui-icons-icons-CodepenIcon.cjs.js", "module": "./icons/CodepenIcon/dist/keystone-ui-icons-icons-CodepenIcon.esm.js", "default": "./icons/CodepenIcon/dist/keystone-ui-icons-icons-CodepenIcon.cjs.js" }, "./icons/ColumnsIcon": { + "types": "./icons/ColumnsIcon/dist/keystone-ui-icons-icons-ColumnsIcon.cjs.js", "module": "./icons/ColumnsIcon/dist/keystone-ui-icons-icons-ColumnsIcon.esm.js", "default": "./icons/ColumnsIcon/dist/keystone-ui-icons-icons-ColumnsIcon.cjs.js" }, "./icons/CommandIcon": { + "types": "./icons/CommandIcon/dist/keystone-ui-icons-icons-CommandIcon.cjs.js", "module": "./icons/CommandIcon/dist/keystone-ui-icons-icons-CommandIcon.esm.js", "default": "./icons/CommandIcon/dist/keystone-ui-icons-icons-CommandIcon.cjs.js" }, "./icons/CompassIcon": { + "types": "./icons/CompassIcon/dist/keystone-ui-icons-icons-CompassIcon.cjs.js", "module": "./icons/CompassIcon/dist/keystone-ui-icons-icons-CompassIcon.esm.js", "default": "./icons/CompassIcon/dist/keystone-ui-icons-icons-CompassIcon.cjs.js" }, "./icons/DropletIcon": { + "types": "./icons/DropletIcon/dist/keystone-ui-icons-icons-DropletIcon.cjs.js", "module": "./icons/DropletIcon/dist/keystone-ui-icons-icons-DropletIcon.esm.js", "default": "./icons/DropletIcon/dist/keystone-ui-icons-icons-DropletIcon.cjs.js" }, "./icons/FeatherIcon": { + "types": "./icons/FeatherIcon/dist/keystone-ui-icons-icons-FeatherIcon.cjs.js", "module": "./icons/FeatherIcon/dist/keystone-ui-icons-icons-FeatherIcon.esm.js", "default": "./icons/FeatherIcon/dist/keystone-ui-icons-icons-FeatherIcon.cjs.js" }, "./icons/HexagonIcon": { + "types": "./icons/HexagonIcon/dist/keystone-ui-icons-icons-HexagonIcon.cjs.js", "module": "./icons/HexagonIcon/dist/keystone-ui-icons-icons-HexagonIcon.esm.js", "default": "./icons/HexagonIcon/dist/keystone-ui-icons-icons-HexagonIcon.cjs.js" }, "./icons/MonitorIcon": { + "types": "./icons/MonitorIcon/dist/keystone-ui-icons-icons-MonitorIcon.cjs.js", "module": "./icons/MonitorIcon/dist/keystone-ui-icons-icons-MonitorIcon.esm.js", "default": "./icons/MonitorIcon/dist/keystone-ui-icons-icons-MonitorIcon.cjs.js" }, "./icons/OctagonIcon": { + "types": "./icons/OctagonIcon/dist/keystone-ui-icons-icons-OctagonIcon.cjs.js", "module": "./icons/OctagonIcon/dist/keystone-ui-icons-icons-OctagonIcon.esm.js", "default": "./icons/OctagonIcon/dist/keystone-ui-icons-icons-OctagonIcon.cjs.js" }, "./icons/PackageIcon": { + "types": "./icons/PackageIcon/dist/keystone-ui-icons-icons-PackageIcon.cjs.js", "module": "./icons/PackageIcon/dist/keystone-ui-icons-icons-PackageIcon.esm.js", "default": "./icons/PackageIcon/dist/keystone-ui-icons-icons-PackageIcon.cjs.js" }, "./icons/PenToolIcon": { + "types": "./icons/PenToolIcon/dist/keystone-ui-icons-icons-PenToolIcon.cjs.js", "module": "./icons/PenToolIcon/dist/keystone-ui-icons-icons-PenToolIcon.esm.js", "default": "./icons/PenToolIcon/dist/keystone-ui-icons-icons-PenToolIcon.cjs.js" }, "./icons/PercentIcon": { + "types": "./icons/PercentIcon/dist/keystone-ui-icons-icons-PercentIcon.cjs.js", "module": "./icons/PercentIcon/dist/keystone-ui-icons-icons-PercentIcon.esm.js", "default": "./icons/PercentIcon/dist/keystone-ui-icons-icons-PercentIcon.cjs.js" }, "./icons/PrinterIcon": { + "types": "./icons/PrinterIcon/dist/keystone-ui-icons-icons-PrinterIcon.cjs.js", "module": "./icons/PrinterIcon/dist/keystone-ui-icons-icons-PrinterIcon.esm.js", "default": "./icons/PrinterIcon/dist/keystone-ui-icons-icons-PrinterIcon.cjs.js" }, "./icons/ShuffleIcon": { + "types": "./icons/ShuffleIcon/dist/keystone-ui-icons-icons-ShuffleIcon.cjs.js", "module": "./icons/ShuffleIcon/dist/keystone-ui-icons-icons-ShuffleIcon.esm.js", "default": "./icons/ShuffleIcon/dist/keystone-ui-icons-icons-ShuffleIcon.cjs.js" }, "./icons/SidebarIcon": { + "types": "./icons/SidebarIcon/dist/keystone-ui-icons-icons-SidebarIcon.cjs.js", "module": "./icons/SidebarIcon/dist/keystone-ui-icons-icons-SidebarIcon.esm.js", "default": "./icons/SidebarIcon/dist/keystone-ui-icons-icons-SidebarIcon.cjs.js" }, "./icons/SlidersIcon": { + "types": "./icons/SlidersIcon/dist/keystone-ui-icons-icons-SlidersIcon.cjs.js", "module": "./icons/SlidersIcon/dist/keystone-ui-icons-icons-SlidersIcon.esm.js", "default": "./icons/SlidersIcon/dist/keystone-ui-icons-icons-SlidersIcon.cjs.js" }, "./icons/SpeakerIcon": { + "types": "./icons/SpeakerIcon/dist/keystone-ui-icons-icons-SpeakerIcon.cjs.js", "module": "./icons/SpeakerIcon/dist/keystone-ui-icons-icons-SpeakerIcon.esm.js", "default": "./icons/SpeakerIcon/dist/keystone-ui-icons-icons-SpeakerIcon.cjs.js" }, "./icons/SunriseIcon": { + "types": "./icons/SunriseIcon/dist/keystone-ui-icons-icons-SunriseIcon.cjs.js", "module": "./icons/SunriseIcon/dist/keystone-ui-icons-icons-SunriseIcon.esm.js", "default": "./icons/SunriseIcon/dist/keystone-ui-icons-icons-SunriseIcon.cjs.js" }, "./icons/TwitterIcon": { + "types": "./icons/TwitterIcon/dist/keystone-ui-icons-icons-TwitterIcon.cjs.js", "module": "./icons/TwitterIcon/dist/keystone-ui-icons-icons-TwitterIcon.esm.js", "default": "./icons/TwitterIcon/dist/keystone-ui-icons-icons-TwitterIcon.cjs.js" }, "./icons/Volume1Icon": { + "types": "./icons/Volume1Icon/dist/keystone-ui-icons-icons-Volume1Icon.cjs.js", "module": "./icons/Volume1Icon/dist/keystone-ui-icons-icons-Volume1Icon.esm.js", "default": "./icons/Volume1Icon/dist/keystone-ui-icons-icons-Volume1Icon.cjs.js" }, "./icons/Volume2Icon": { + "types": "./icons/Volume2Icon/dist/keystone-ui-icons-icons-Volume2Icon.cjs.js", "module": "./icons/Volume2Icon/dist/keystone-ui-icons-icons-Volume2Icon.esm.js", "default": "./icons/Volume2Icon/dist/keystone-ui-icons-icons-Volume2Icon.cjs.js" }, "./icons/VolumeXIcon": { + "types": "./icons/VolumeXIcon/dist/keystone-ui-icons-icons-VolumeXIcon.cjs.js", "module": "./icons/VolumeXIcon/dist/keystone-ui-icons-icons-VolumeXIcon.esm.js", "default": "./icons/VolumeXIcon/dist/keystone-ui-icons-icons-VolumeXIcon.cjs.js" }, "./icons/WifiOffIcon": { + "types": "./icons/WifiOffIcon/dist/keystone-ui-icons-icons-WifiOffIcon.cjs.js", "module": "./icons/WifiOffIcon/dist/keystone-ui-icons-icons-WifiOffIcon.esm.js", "default": "./icons/WifiOffIcon/dist/keystone-ui-icons-icons-WifiOffIcon.cjs.js" }, "./icons/XCircleIcon": { + "types": "./icons/XCircleIcon/dist/keystone-ui-icons-icons-XCircleIcon.cjs.js", "module": "./icons/XCircleIcon/dist/keystone-ui-icons-icons-XCircleIcon.esm.js", "default": "./icons/XCircleIcon/dist/keystone-ui-icons-icons-XCircleIcon.cjs.js" }, "./icons/XSquareIcon": { + "types": "./icons/XSquareIcon/dist/keystone-ui-icons-icons-XSquareIcon.cjs.js", "module": "./icons/XSquareIcon/dist/keystone-ui-icons-icons-XSquareIcon.esm.js", "default": "./icons/XSquareIcon/dist/keystone-ui-icons-icons-XSquareIcon.cjs.js" }, "./icons/YoutubeIcon": { + "types": "./icons/YoutubeIcon/dist/keystone-ui-icons-icons-YoutubeIcon.cjs.js", "module": "./icons/YoutubeIcon/dist/keystone-ui-icons-icons-YoutubeIcon.esm.js", "default": "./icons/YoutubeIcon/dist/keystone-ui-icons-icons-YoutubeIcon.cjs.js" }, "./icons/ZoomOutIcon": { + "types": "./icons/ZoomOutIcon/dist/keystone-ui-icons-icons-ZoomOutIcon.cjs.js", "module": "./icons/ZoomOutIcon/dist/keystone-ui-icons-icons-ZoomOutIcon.esm.js", "default": "./icons/ZoomOutIcon/dist/keystone-ui-icons-icons-ZoomOutIcon.cjs.js" }, "./icons/ActivityIcon": { + "types": "./icons/ActivityIcon/dist/keystone-ui-icons-icons-ActivityIcon.cjs.js", "module": "./icons/ActivityIcon/dist/keystone-ui-icons-icons-ActivityIcon.esm.js", "default": "./icons/ActivityIcon/dist/keystone-ui-icons-icons-ActivityIcon.cjs.js" }, "./icons/ApertureIcon": { + "types": "./icons/ApertureIcon/dist/keystone-ui-icons-icons-ApertureIcon.cjs.js", "module": "./icons/ApertureIcon/dist/keystone-ui-icons-icons-ApertureIcon.esm.js", "default": "./icons/ApertureIcon/dist/keystone-ui-icons-icons-ApertureIcon.cjs.js" }, "./icons/BarChartIcon": { + "types": "./icons/BarChartIcon/dist/keystone-ui-icons-icons-BarChartIcon.cjs.js", "module": "./icons/BarChartIcon/dist/keystone-ui-icons-icons-BarChartIcon.esm.js", "default": "./icons/BarChartIcon/dist/keystone-ui-icons-icons-BarChartIcon.cjs.js" }, "./icons/BookOpenIcon": { + "types": "./icons/BookOpenIcon/dist/keystone-ui-icons-icons-BookOpenIcon.cjs.js", "module": "./icons/BookOpenIcon/dist/keystone-ui-icons-icons-BookOpenIcon.esm.js", "default": "./icons/BookOpenIcon/dist/keystone-ui-icons-icons-BookOpenIcon.cjs.js" }, "./icons/BookmarkIcon": { + "types": "./icons/BookmarkIcon/dist/keystone-ui-icons-icons-BookmarkIcon.cjs.js", "module": "./icons/BookmarkIcon/dist/keystone-ui-icons-icons-BookmarkIcon.esm.js", "default": "./icons/BookmarkIcon/dist/keystone-ui-icons-icons-BookmarkIcon.cjs.js" }, "./icons/CalendarIcon": { + "types": "./icons/CalendarIcon/dist/keystone-ui-icons-icons-CalendarIcon.cjs.js", "module": "./icons/CalendarIcon/dist/keystone-ui-icons-icons-CalendarIcon.esm.js", "default": "./icons/CalendarIcon/dist/keystone-ui-icons-icons-CalendarIcon.cjs.js" }, "./icons/CloudOffIcon": { + "types": "./icons/CloudOffIcon/dist/keystone-ui-icons-icons-CloudOffIcon.cjs.js", "module": "./icons/CloudOffIcon/dist/keystone-ui-icons-icons-CloudOffIcon.esm.js", "default": "./icons/CloudOffIcon/dist/keystone-ui-icons-icons-CloudOffIcon.cjs.js" }, "./icons/DatabaseIcon": { + "types": "./icons/DatabaseIcon/dist/keystone-ui-icons-icons-DatabaseIcon.cjs.js", "module": "./icons/DatabaseIcon/dist/keystone-ui-icons-icons-DatabaseIcon.esm.js", "default": "./icons/DatabaseIcon/dist/keystone-ui-icons-icons-DatabaseIcon.cjs.js" }, "./icons/DownloadIcon": { + "types": "./icons/DownloadIcon/dist/keystone-ui-icons-icons-DownloadIcon.cjs.js", "module": "./icons/DownloadIcon/dist/keystone-ui-icons-icons-DownloadIcon.esm.js", "default": "./icons/DownloadIcon/dist/keystone-ui-icons-icons-DownloadIcon.cjs.js" }, "./icons/DribbbleIcon": { + "types": "./icons/DribbbleIcon/dist/keystone-ui-icons-icons-DribbbleIcon.cjs.js", "module": "./icons/DribbbleIcon/dist/keystone-ui-icons-icons-DribbbleIcon.esm.js", "default": "./icons/DribbbleIcon/dist/keystone-ui-icons-icons-DribbbleIcon.cjs.js" }, "./icons/FacebookIcon": { + "types": "./icons/FacebookIcon/dist/keystone-ui-icons-icons-FacebookIcon.cjs.js", "module": "./icons/FacebookIcon/dist/keystone-ui-icons-icons-FacebookIcon.esm.js", "default": "./icons/FacebookIcon/dist/keystone-ui-icons-icons-FacebookIcon.cjs.js" }, "./icons/FilePlusIcon": { + "types": "./icons/FilePlusIcon/dist/keystone-ui-icons-icons-FilePlusIcon.cjs.js", "module": "./icons/FilePlusIcon/dist/keystone-ui-icons-icons-FilePlusIcon.esm.js", "default": "./icons/FilePlusIcon/dist/keystone-ui-icons-icons-FilePlusIcon.cjs.js" }, "./icons/FileTextIcon": { + "types": "./icons/FileTextIcon/dist/keystone-ui-icons-icons-FileTextIcon.cjs.js", "module": "./icons/FileTextIcon/dist/keystone-ui-icons-icons-FileTextIcon.esm.js", "default": "./icons/FileTextIcon/dist/keystone-ui-icons-icons-FileTextIcon.cjs.js" }, "./icons/GitMergeIcon": { + "types": "./icons/GitMergeIcon/dist/keystone-ui-icons-icons-GitMergeIcon.cjs.js", "module": "./icons/GitMergeIcon/dist/keystone-ui-icons-icons-GitMergeIcon.esm.js", "default": "./icons/GitMergeIcon/dist/keystone-ui-icons-icons-GitMergeIcon.cjs.js" }, "./icons/LifeBuoyIcon": { + "types": "./icons/LifeBuoyIcon/dist/keystone-ui-icons-icons-LifeBuoyIcon.cjs.js", "module": "./icons/LifeBuoyIcon/dist/keystone-ui-icons-icons-LifeBuoyIcon.esm.js", "default": "./icons/LifeBuoyIcon/dist/keystone-ui-icons-icons-LifeBuoyIcon.cjs.js" }, "./icons/LinkedinIcon": { + "types": "./icons/LinkedinIcon/dist/keystone-ui-icons-icons-LinkedinIcon.cjs.js", "module": "./icons/LinkedinIcon/dist/keystone-ui-icons-icons-LinkedinIcon.esm.js", "default": "./icons/LinkedinIcon/dist/keystone-ui-icons-icons-LinkedinIcon.cjs.js" }, "./icons/MaximizeIcon": { + "types": "./icons/MaximizeIcon/dist/keystone-ui-icons-icons-MaximizeIcon.cjs.js", "module": "./icons/MaximizeIcon/dist/keystone-ui-icons-icons-MaximizeIcon.esm.js", "default": "./icons/MaximizeIcon/dist/keystone-ui-icons-icons-MaximizeIcon.cjs.js" }, "./icons/MinimizeIcon": { + "types": "./icons/MinimizeIcon/dist/keystone-ui-icons-icons-MinimizeIcon.cjs.js", "module": "./icons/MinimizeIcon/dist/keystone-ui-icons-icons-MinimizeIcon.esm.js", "default": "./icons/MinimizeIcon/dist/keystone-ui-icons-icons-MinimizeIcon.cjs.js" }, "./icons/PhoneOffIcon": { + "types": "./icons/PhoneOffIcon/dist/keystone-ui-icons-icons-PhoneOffIcon.cjs.js", "module": "./icons/PhoneOffIcon/dist/keystone-ui-icons-icons-PhoneOffIcon.esm.js", "default": "./icons/PhoneOffIcon/dist/keystone-ui-icons-icons-PhoneOffIcon.cjs.js" }, "./icons/PieChartIcon": { + "types": "./icons/PieChartIcon/dist/keystone-ui-icons-icons-PieChartIcon.cjs.js", "module": "./icons/PieChartIcon/dist/keystone-ui-icons-icons-PieChartIcon.esm.js", "default": "./icons/PieChartIcon/dist/keystone-ui-icons-icons-PieChartIcon.cjs.js" }, "./icons/RotateCwIcon": { + "types": "./icons/RotateCwIcon/dist/keystone-ui-icons-icons-RotateCwIcon.cjs.js", "module": "./icons/RotateCwIcon/dist/keystone-ui-icons-icons-RotateCwIcon.esm.js", "default": "./icons/RotateCwIcon/dist/keystone-ui-icons-icons-RotateCwIcon.cjs.js" }, "./icons/ScissorsIcon": { + "types": "./icons/ScissorsIcon/dist/keystone-ui-icons-icons-ScissorsIcon.cjs.js", "module": "./icons/ScissorsIcon/dist/keystone-ui-icons-icons-ScissorsIcon.esm.js", "default": "./icons/ScissorsIcon/dist/keystone-ui-icons-icons-ScissorsIcon.cjs.js" }, "./icons/SettingsIcon": { + "types": "./icons/SettingsIcon/dist/keystone-ui-icons-icons-SettingsIcon.cjs.js", "module": "./icons/SettingsIcon/dist/keystone-ui-icons-icons-SettingsIcon.esm.js", "default": "./icons/SettingsIcon/dist/keystone-ui-icons-icons-SettingsIcon.cjs.js" }, "./icons/SkipBackIcon": { + "types": "./icons/SkipBackIcon/dist/keystone-ui-icons-icons-SkipBackIcon.cjs.js", "module": "./icons/SkipBackIcon/dist/keystone-ui-icons-icons-SkipBackIcon.esm.js", "default": "./icons/SkipBackIcon/dist/keystone-ui-icons-icons-SkipBackIcon.cjs.js" }, "./icons/TerminalIcon": { + "types": "./icons/TerminalIcon/dist/keystone-ui-icons-icons-TerminalIcon.cjs.js", "module": "./icons/TerminalIcon/dist/keystone-ui-icons-icons-TerminalIcon.esm.js", "default": "./icons/TerminalIcon/dist/keystone-ui-icons-icons-TerminalIcon.cjs.js" }, "./icons/ThumbsUpIcon": { + "types": "./icons/ThumbsUpIcon/dist/keystone-ui-icons-icons-ThumbsUpIcon.cjs.js", "module": "./icons/ThumbsUpIcon/dist/keystone-ui-icons-icons-ThumbsUpIcon.esm.js", "default": "./icons/ThumbsUpIcon/dist/keystone-ui-icons-icons-ThumbsUpIcon.cjs.js" }, "./icons/TriangleIcon": { + "types": "./icons/TriangleIcon/dist/keystone-ui-icons-icons-TriangleIcon.cjs.js", "module": "./icons/TriangleIcon/dist/keystone-ui-icons-icons-TriangleIcon.esm.js", "default": "./icons/TriangleIcon/dist/keystone-ui-icons-icons-TriangleIcon.cjs.js" }, "./icons/UmbrellaIcon": { + "types": "./icons/UmbrellaIcon/dist/keystone-ui-icons-icons-UmbrellaIcon.cjs.js", "module": "./icons/UmbrellaIcon/dist/keystone-ui-icons-icons-UmbrellaIcon.esm.js", "default": "./icons/UmbrellaIcon/dist/keystone-ui-icons-icons-UmbrellaIcon.cjs.js" }, "./icons/UserPlusIcon": { + "types": "./icons/UserPlusIcon/dist/keystone-ui-icons-icons-UserPlusIcon.cjs.js", "module": "./icons/UserPlusIcon/dist/keystone-ui-icons-icons-UserPlusIcon.esm.js", "default": "./icons/UserPlusIcon/dist/keystone-ui-icons-icons-UserPlusIcon.cjs.js" }, "./icons/VideoOffIcon": { + "types": "./icons/VideoOffIcon/dist/keystone-ui-icons-icons-VideoOffIcon.cjs.js", "module": "./icons/VideoOffIcon/dist/keystone-ui-icons-icons-VideoOffIcon.esm.js", "default": "./icons/VideoOffIcon/dist/keystone-ui-icons-icons-VideoOffIcon.cjs.js" }, "./icons/XOctagonIcon": { + "types": "./icons/XOctagonIcon/dist/keystone-ui-icons-icons-XOctagonIcon.cjs.js", "module": "./icons/XOctagonIcon/dist/keystone-ui-icons-icons-XOctagonIcon.esm.js", "default": "./icons/XOctagonIcon/dist/keystone-ui-icons-icons-XOctagonIcon.cjs.js" }, "./icons/AlignLeftIcon": { + "types": "./icons/AlignLeftIcon/dist/keystone-ui-icons-icons-AlignLeftIcon.cjs.js", "module": "./icons/AlignLeftIcon/dist/keystone-ui-icons-icons-AlignLeftIcon.esm.js", "default": "./icons/AlignLeftIcon/dist/keystone-ui-icons-icons-AlignLeftIcon.cjs.js" }, "./icons/ArrowDownIcon": { + "types": "./icons/ArrowDownIcon/dist/keystone-ui-icons-icons-ArrowDownIcon.cjs.js", "module": "./icons/ArrowDownIcon/dist/keystone-ui-icons-icons-ArrowDownIcon.esm.js", "default": "./icons/ArrowDownIcon/dist/keystone-ui-icons-icons-ArrowDownIcon.cjs.js" }, "./icons/ArrowLeftIcon": { + "types": "./icons/ArrowLeftIcon/dist/keystone-ui-icons-icons-ArrowLeftIcon.cjs.js", "module": "./icons/ArrowLeftIcon/dist/keystone-ui-icons-icons-ArrowLeftIcon.esm.js", "default": "./icons/ArrowLeftIcon/dist/keystone-ui-icons-icons-ArrowLeftIcon.cjs.js" }, "./icons/BarChart2Icon": { + "types": "./icons/BarChart2Icon/dist/keystone-ui-icons-icons-BarChart2Icon.cjs.js", "module": "./icons/BarChart2Icon/dist/keystone-ui-icons-icons-BarChart2Icon.esm.js", "default": "./icons/BarChart2Icon/dist/keystone-ui-icons-icons-BarChart2Icon.cjs.js" }, "./icons/BluetoothIcon": { + "types": "./icons/BluetoothIcon/dist/keystone-ui-icons-icons-BluetoothIcon.cjs.js", "module": "./icons/BluetoothIcon/dist/keystone-ui-icons-icons-BluetoothIcon.esm.js", "default": "./icons/BluetoothIcon/dist/keystone-ui-icons-icons-BluetoothIcon.cjs.js" }, "./icons/BriefcaseIcon": { + "types": "./icons/BriefcaseIcon/dist/keystone-ui-icons-icons-BriefcaseIcon.cjs.js", "module": "./icons/BriefcaseIcon/dist/keystone-ui-icons-icons-BriefcaseIcon.esm.js", "default": "./icons/BriefcaseIcon/dist/keystone-ui-icons-icons-BriefcaseIcon.cjs.js" }, "./icons/CameraOffIcon": { + "types": "./icons/CameraOffIcon/dist/keystone-ui-icons-icons-CameraOffIcon.cjs.js", "module": "./icons/CameraOffIcon/dist/keystone-ui-icons-icons-CameraOffIcon.esm.js", "default": "./icons/CameraOffIcon/dist/keystone-ui-icons-icons-CameraOffIcon.cjs.js" }, "./icons/ChevronUpIcon": { + "types": "./icons/ChevronUpIcon/dist/keystone-ui-icons-icons-ChevronUpIcon.cjs.js", "module": "./icons/ChevronUpIcon/dist/keystone-ui-icons-icons-ChevronUpIcon.esm.js", "default": "./icons/ChevronUpIcon/dist/keystone-ui-icons-icons-ChevronUpIcon.cjs.js" }, "./icons/ClipboardIcon": { + "types": "./icons/ClipboardIcon/dist/keystone-ui-icons-icons-ClipboardIcon.cjs.js", "module": "./icons/ClipboardIcon/dist/keystone-ui-icons-icons-ClipboardIcon.esm.js", "default": "./icons/ClipboardIcon/dist/keystone-ui-icons-icons-ClipboardIcon.cjs.js" }, "./icons/CloudRainIcon": { + "types": "./icons/CloudRainIcon/dist/keystone-ui-icons-icons-CloudRainIcon.cjs.js", "module": "./icons/CloudRainIcon/dist/keystone-ui-icons-icons-CloudRainIcon.esm.js", "default": "./icons/CloudRainIcon/dist/keystone-ui-icons-icons-CloudRainIcon.cjs.js" }, "./icons/CloudSnowIcon": { + "types": "./icons/CloudSnowIcon/dist/keystone-ui-icons-icons-CloudSnowIcon.cjs.js", "module": "./icons/CloudSnowIcon/dist/keystone-ui-icons-icons-CloudSnowIcon.esm.js", "default": "./icons/CloudSnowIcon/dist/keystone-ui-icons-icons-CloudSnowIcon.cjs.js" }, "./icons/CrosshairIcon": { + "types": "./icons/CrosshairIcon/dist/keystone-ui-icons-icons-CrosshairIcon.cjs.js", "module": "./icons/CrosshairIcon/dist/keystone-ui-icons-icons-CrosshairIcon.esm.js", "default": "./icons/CrosshairIcon/dist/keystone-ui-icons-icons-CrosshairIcon.cjs.js" }, "./icons/FileMinusIcon": { + "types": "./icons/FileMinusIcon/dist/keystone-ui-icons-icons-FileMinusIcon.cjs.js", "module": "./icons/FileMinusIcon/dist/keystone-ui-icons-icons-FileMinusIcon.esm.js", "default": "./icons/FileMinusIcon/dist/keystone-ui-icons-icons-FileMinusIcon.cjs.js" }, "./icons/GitBranchIcon": { + "types": "./icons/GitBranchIcon/dist/keystone-ui-icons-icons-GitBranchIcon.cjs.js", "module": "./icons/GitBranchIcon/dist/keystone-ui-icons-icons-GitBranchIcon.esm.js", "default": "./icons/GitBranchIcon/dist/keystone-ui-icons-icons-GitBranchIcon.cjs.js" }, "./icons/GitCommitIcon": { + "types": "./icons/GitCommitIcon/dist/keystone-ui-icons-icons-GitCommitIcon.cjs.js", "module": "./icons/GitCommitIcon/dist/keystone-ui-icons-icons-GitCommitIcon.esm.js", "default": "./icons/GitCommitIcon/dist/keystone-ui-icons-icons-GitCommitIcon.cjs.js" }, "./icons/HardDriveIcon": { + "types": "./icons/HardDriveIcon/dist/keystone-ui-icons-icons-HardDriveIcon.cjs.js", "module": "./icons/HardDriveIcon/dist/keystone-ui-icons-icons-HardDriveIcon.esm.js", "default": "./icons/HardDriveIcon/dist/keystone-ui-icons-icons-HardDriveIcon.cjs.js" }, "./icons/InstagramIcon": { + "types": "./icons/InstagramIcon/dist/keystone-ui-icons-icons-InstagramIcon.cjs.js", "module": "./icons/InstagramIcon/dist/keystone-ui-icons-icons-InstagramIcon.esm.js", "default": "./icons/InstagramIcon/dist/keystone-ui-icons-icons-InstagramIcon.cjs.js" }, "./icons/Maximize2Icon": { + "types": "./icons/Maximize2Icon/dist/keystone-ui-icons-icons-Maximize2Icon.cjs.js", "module": "./icons/Maximize2Icon/dist/keystone-ui-icons-icons-Maximize2Icon.esm.js", "default": "./icons/Maximize2Icon/dist/keystone-ui-icons-icons-Maximize2Icon.cjs.js" }, "./icons/Minimize2Icon": { + "types": "./icons/Minimize2Icon/dist/keystone-ui-icons-icons-Minimize2Icon.cjs.js", "module": "./icons/Minimize2Icon/dist/keystone-ui-icons-icons-Minimize2Icon.esm.js", "default": "./icons/Minimize2Icon/dist/keystone-ui-icons-icons-Minimize2Icon.cjs.js" }, "./icons/PaperclipIcon": { + "types": "./icons/PaperclipIcon/dist/keystone-ui-icons-icons-PaperclipIcon.cjs.js", "module": "./icons/PaperclipIcon/dist/keystone-ui-icons-icons-PaperclipIcon.esm.js", "default": "./icons/PaperclipIcon/dist/keystone-ui-icons-icons-PaperclipIcon.cjs.js" }, "./icons/PhoneCallIcon": { + "types": "./icons/PhoneCallIcon/dist/keystone-ui-icons-icons-PhoneCallIcon.cjs.js", "module": "./icons/PhoneCallIcon/dist/keystone-ui-icons-icons-PhoneCallIcon.esm.js", "default": "./icons/PhoneCallIcon/dist/keystone-ui-icons-icons-PhoneCallIcon.cjs.js" }, "./icons/RefreshCwIcon": { + "types": "./icons/RefreshCwIcon/dist/keystone-ui-icons-icons-RefreshCwIcon.cjs.js", "module": "./icons/RefreshCwIcon/dist/keystone-ui-icons-icons-RefreshCwIcon.esm.js", "default": "./icons/RefreshCwIcon/dist/keystone-ui-icons-icons-RefreshCwIcon.cjs.js" }, "./icons/RotateCcwIcon": { + "types": "./icons/RotateCcwIcon/dist/keystone-ui-icons-icons-RotateCcwIcon.cjs.js", "module": "./icons/RotateCcwIcon/dist/keystone-ui-icons-icons-RotateCcwIcon.esm.js", "default": "./icons/RotateCcwIcon/dist/keystone-ui-icons-icons-RotateCcwIcon.cjs.js" }, "./icons/ShieldOffIcon": { + "types": "./icons/ShieldOffIcon/dist/keystone-ui-icons-icons-ShieldOffIcon.cjs.js", "module": "./icons/ShieldOffIcon/dist/keystone-ui-icons-icons-ShieldOffIcon.esm.js", "default": "./icons/ShieldOffIcon/dist/keystone-ui-icons-icons-ShieldOffIcon.cjs.js" }, "./icons/UnderlineIcon": { + "types": "./icons/UnderlineIcon/dist/keystone-ui-icons-icons-UnderlineIcon.cjs.js", "module": "./icons/UnderlineIcon/dist/keystone-ui-icons-icons-UnderlineIcon.esm.js", "default": "./icons/UnderlineIcon/dist/keystone-ui-icons-icons-UnderlineIcon.cjs.js" }, "./icons/UserCheckIcon": { + "types": "./icons/UserCheckIcon/dist/keystone-ui-icons-icons-UserCheckIcon.cjs.js", "module": "./icons/UserCheckIcon/dist/keystone-ui-icons-icons-UserCheckIcon.esm.js", "default": "./icons/UserCheckIcon/dist/keystone-ui-icons-icons-UserCheckIcon.cjs.js" }, "./icons/UserMinusIcon": { + "types": "./icons/UserMinusIcon/dist/keystone-ui-icons-icons-UserMinusIcon.cjs.js", "module": "./icons/UserMinusIcon/dist/keystone-ui-icons-icons-UserMinusIcon.esm.js", "default": "./icons/UserMinusIcon/dist/keystone-ui-icons-icons-UserMinusIcon.cjs.js" }, "./icons/VoicemailIcon": { + "types": "./icons/VoicemailIcon/dist/keystone-ui-icons-icons-VoicemailIcon.cjs.js", "module": "./icons/VoicemailIcon/dist/keystone-ui-icons-icons-VoicemailIcon.esm.js", "default": "./icons/VoicemailIcon/dist/keystone-ui-icons-icons-VoicemailIcon.cjs.js" }, "./icons/AlignRightIcon": { + "types": "./icons/AlignRightIcon/dist/keystone-ui-icons-icons-AlignRightIcon.cjs.js", "module": "./icons/AlignRightIcon/dist/keystone-ui-icons-icons-AlignRightIcon.esm.js", "default": "./icons/AlignRightIcon/dist/keystone-ui-icons-icons-AlignRightIcon.cjs.js" }, "./icons/ArrowRightIcon": { + "types": "./icons/ArrowRightIcon/dist/keystone-ui-icons-icons-ArrowRightIcon.cjs.js", "module": "./icons/ArrowRightIcon/dist/keystone-ui-icons-icons-ArrowRightIcon.esm.js", "default": "./icons/ArrowRightIcon/dist/keystone-ui-icons-icons-ArrowRightIcon.cjs.js" }, "./icons/ChevronsUpIcon": { + "types": "./icons/ChevronsUpIcon/dist/keystone-ui-icons-icons-ChevronsUpIcon.cjs.js", "module": "./icons/ChevronsUpIcon/dist/keystone-ui-icons-icons-ChevronsUpIcon.esm.js", "default": "./icons/ChevronsUpIcon/dist/keystone-ui-icons-icons-ChevronsUpIcon.cjs.js" }, "./icons/CreditCardIcon": { + "types": "./icons/CreditCardIcon/dist/keystone-ui-icons-icons-CreditCardIcon.cjs.js", "module": "./icons/CreditCardIcon/dist/keystone-ui-icons-icons-CreditCardIcon.esm.js", "default": "./icons/CreditCardIcon/dist/keystone-ui-icons-icons-CreditCardIcon.cjs.js" }, "./icons/DollarSignIcon": { + "types": "./icons/DollarSignIcon/dist/keystone-ui-icons-icons-DollarSignIcon.cjs.js", "module": "./icons/DollarSignIcon/dist/keystone-ui-icons-icons-DollarSignIcon.esm.js", "default": "./icons/DollarSignIcon/dist/keystone-ui-icons-icons-DollarSignIcon.cjs.js" }, "./icons/FolderPlusIcon": { + "types": "./icons/FolderPlusIcon/dist/keystone-ui-icons-icons-FolderPlusIcon.cjs.js", "module": "./icons/FolderPlusIcon/dist/keystone-ui-icons-icons-FolderPlusIcon.esm.js", "default": "./icons/FolderPlusIcon/dist/keystone-ui-icons-icons-FolderPlusIcon.cjs.js" }, "./icons/HeadphonesIcon": { + "types": "./icons/HeadphonesIcon/dist/keystone-ui-icons-icons-HeadphonesIcon.cjs.js", "module": "./icons/HeadphonesIcon/dist/keystone-ui-icons-icons-HeadphonesIcon.esm.js", "default": "./icons/HeadphonesIcon/dist/keystone-ui-icons-icons-HeadphonesIcon.cjs.js" }, "./icons/HelpCircleIcon": { + "types": "./icons/HelpCircleIcon/dist/keystone-ui-icons-icons-HelpCircleIcon.cjs.js", "module": "./icons/HelpCircleIcon/dist/keystone-ui-icons-icons-HelpCircleIcon.esm.js", "default": "./icons/HelpCircleIcon/dist/keystone-ui-icons-icons-HelpCircleIcon.cjs.js" }, "./icons/NavigationIcon": { + "types": "./icons/NavigationIcon/dist/keystone-ui-icons-icons-NavigationIcon.cjs.js", "module": "./icons/NavigationIcon/dist/keystone-ui-icons-icons-NavigationIcon.esm.js", "default": "./icons/NavigationIcon/dist/keystone-ui-icons-icons-NavigationIcon.cjs.js" }, "./icons/PlayCircleIcon": { + "types": "./icons/PlayCircleIcon/dist/keystone-ui-icons-icons-PlayCircleIcon.cjs.js", "module": "./icons/PlayCircleIcon/dist/keystone-ui-icons-icons-PlayCircleIcon.esm.js", "default": "./icons/PlayCircleIcon/dist/keystone-ui-icons-icons-PlayCircleIcon.cjs.js" }, "./icons/PlusCircleIcon": { + "types": "./icons/PlusCircleIcon/dist/keystone-ui-icons-icons-PlusCircleIcon.cjs.js", "module": "./icons/PlusCircleIcon/dist/keystone-ui-icons-icons-PlusCircleIcon.esm.js", "default": "./icons/PlusCircleIcon/dist/keystone-ui-icons-icons-PlusCircleIcon.cjs.js" }, "./icons/PlusSquareIcon": { + "types": "./icons/PlusSquareIcon/dist/keystone-ui-icons-icons-PlusSquareIcon.cjs.js", "module": "./icons/PlusSquareIcon/dist/keystone-ui-icons-icons-PlusSquareIcon.esm.js", "default": "./icons/PlusSquareIcon/dist/keystone-ui-icons-icons-PlusSquareIcon.cjs.js" }, "./icons/RefreshCcwIcon": { + "types": "./icons/RefreshCcwIcon/dist/keystone-ui-icons-icons-RefreshCcwIcon.cjs.js", "module": "./icons/RefreshCcwIcon/dist/keystone-ui-icons-icons-RefreshCcwIcon.esm.js", "default": "./icons/RefreshCcwIcon/dist/keystone-ui-icons-icons-RefreshCcwIcon.cjs.js" }, "./icons/SmartphoneIcon": { + "types": "./icons/SmartphoneIcon/dist/keystone-ui-icons-icons-SmartphoneIcon.cjs.js", "module": "./icons/SmartphoneIcon/dist/keystone-ui-icons-icons-SmartphoneIcon.esm.js", "default": "./icons/SmartphoneIcon/dist/keystone-ui-icons-icons-SmartphoneIcon.cjs.js" }, "./icons/StopCircleIcon": { + "types": "./icons/StopCircleIcon/dist/keystone-ui-icons-icons-StopCircleIcon.cjs.js", "module": "./icons/StopCircleIcon/dist/keystone-ui-icons-icons-StopCircleIcon.esm.js", "default": "./icons/StopCircleIcon/dist/keystone-ui-icons-icons-StopCircleIcon.cjs.js" }, "./icons/ThumbsDownIcon": { + "types": "./icons/ThumbsDownIcon/dist/keystone-ui-icons-icons-ThumbsDownIcon.cjs.js", "module": "./icons/ThumbsDownIcon/dist/keystone-ui-icons-icons-ThumbsDownIcon.esm.js", "default": "./icons/ThumbsDownIcon/dist/keystone-ui-icons-icons-ThumbsDownIcon.cjs.js" }, "./icons/ToggleLeftIcon": { + "types": "./icons/ToggleLeftIcon/dist/keystone-ui-icons-icons-ToggleLeftIcon.cjs.js", "module": "./icons/ToggleLeftIcon/dist/keystone-ui-icons-icons-ToggleLeftIcon.esm.js", "default": "./icons/ToggleLeftIcon/dist/keystone-ui-icons-icons-ToggleLeftIcon.cjs.js" }, "./icons/TrendingUpIcon": { + "types": "./icons/TrendingUpIcon/dist/keystone-ui-icons-icons-TrendingUpIcon.cjs.js", "module": "./icons/TrendingUpIcon/dist/keystone-ui-icons-icons-TrendingUpIcon.esm.js", "default": "./icons/TrendingUpIcon/dist/keystone-ui-icons-icons-TrendingUpIcon.cjs.js" }, "./icons/AlertCircleIcon": { + "types": "./icons/AlertCircleIcon/dist/keystone-ui-icons-icons-AlertCircleIcon.cjs.js", "module": "./icons/AlertCircleIcon/dist/keystone-ui-icons-icons-AlertCircleIcon.esm.js", "default": "./icons/AlertCircleIcon/dist/keystone-ui-icons-icons-AlertCircleIcon.cjs.js" }, "./icons/AlignCenterIcon": { + "types": "./icons/AlignCenterIcon/dist/keystone-ui-icons-icons-AlignCenterIcon.cjs.js", "module": "./icons/AlignCenterIcon/dist/keystone-ui-icons-icons-AlignCenterIcon.esm.js", "default": "./icons/AlignCenterIcon/dist/keystone-ui-icons-icons-AlignCenterIcon.cjs.js" }, "./icons/ArrowUpLeftIcon": { + "types": "./icons/ArrowUpLeftIcon/dist/keystone-ui-icons-icons-ArrowUpLeftIcon.cjs.js", "module": "./icons/ArrowUpLeftIcon/dist/keystone-ui-icons-icons-ArrowUpLeftIcon.esm.js", "default": "./icons/ArrowUpLeftIcon/dist/keystone-ui-icons-icons-ArrowUpLeftIcon.cjs.js" }, "./icons/CheckCircleIcon": { + "types": "./icons/CheckCircleIcon/dist/keystone-ui-icons-icons-CheckCircleIcon.cjs.js", "module": "./icons/CheckCircleIcon/dist/keystone-ui-icons-icons-CheckCircleIcon.esm.js", "default": "./icons/CheckCircleIcon/dist/keystone-ui-icons-icons-CheckCircleIcon.cjs.js" }, "./icons/CheckSquareIcon": { + "types": "./icons/CheckSquareIcon/dist/keystone-ui-icons-icons-CheckSquareIcon.cjs.js", "module": "./icons/CheckSquareIcon/dist/keystone-ui-icons-icons-CheckSquareIcon.esm.js", "default": "./icons/CheckSquareIcon/dist/keystone-ui-icons-icons-CheckSquareIcon.cjs.js" }, "./icons/ChevronDownIcon": { + "types": "./icons/ChevronDownIcon/dist/keystone-ui-icons-icons-ChevronDownIcon.cjs.js", "module": "./icons/ChevronDownIcon/dist/keystone-ui-icons-icons-ChevronDownIcon.esm.js", "default": "./icons/ChevronDownIcon/dist/keystone-ui-icons-icons-ChevronDownIcon.cjs.js" }, "./icons/ChevronLeftIcon": { + "types": "./icons/ChevronLeftIcon/dist/keystone-ui-icons-icons-ChevronLeftIcon.cjs.js", "module": "./icons/ChevronLeftIcon/dist/keystone-ui-icons-icons-ChevronLeftIcon.esm.js", "default": "./icons/ChevronLeftIcon/dist/keystone-ui-icons-icons-ChevronLeftIcon.cjs.js" }, "./icons/CodesandboxIcon": { + "types": "./icons/CodesandboxIcon/dist/keystone-ui-icons-icons-CodesandboxIcon.cjs.js", "module": "./icons/CodesandboxIcon/dist/keystone-ui-icons-icons-CodesandboxIcon.esm.js", "default": "./icons/CodesandboxIcon/dist/keystone-ui-icons-icons-CodesandboxIcon.cjs.js" }, "./icons/FastForwardIcon": { + "types": "./icons/FastForwardIcon/dist/keystone-ui-icons-icons-FastForwardIcon.cjs.js", "module": "./icons/FastForwardIcon/dist/keystone-ui-icons-icons-FastForwardIcon.esm.js", "default": "./icons/FastForwardIcon/dist/keystone-ui-icons-icons-FastForwardIcon.cjs.js" }, "./icons/FolderMinusIcon": { + "types": "./icons/FolderMinusIcon/dist/keystone-ui-icons-icons-FolderMinusIcon.cjs.js", "module": "./icons/FolderMinusIcon/dist/keystone-ui-icons-icons-FolderMinusIcon.esm.js", "default": "./icons/FolderMinusIcon/dist/keystone-ui-icons-icons-FolderMinusIcon.cjs.js" }, "./icons/MinusCircleIcon": { + "types": "./icons/MinusCircleIcon/dist/keystone-ui-icons-icons-MinusCircleIcon.cjs.js", "module": "./icons/MinusCircleIcon/dist/keystone-ui-icons-icons-MinusCircleIcon.esm.js", "default": "./icons/MinusCircleIcon/dist/keystone-ui-icons-icons-MinusCircleIcon.cjs.js" }, "./icons/MinusSquareIcon": { + "types": "./icons/MinusSquareIcon/dist/keystone-ui-icons-icons-MinusSquareIcon.cjs.js", "module": "./icons/MinusSquareIcon/dist/keystone-ui-icons-icons-MinusSquareIcon.esm.js", "default": "./icons/MinusSquareIcon/dist/keystone-ui-icons-icons-MinusSquareIcon.cjs.js" }, "./icons/Navigation2Icon": { + "types": "./icons/Navigation2Icon/dist/keystone-ui-icons-icons-Navigation2Icon.cjs.js", "module": "./icons/Navigation2Icon/dist/keystone-ui-icons-icons-Navigation2Icon.esm.js", "default": "./icons/Navigation2Icon/dist/keystone-ui-icons-icons-Navigation2Icon.cjs.js" }, "./icons/PauseCircleIcon": { + "types": "./icons/PauseCircleIcon/dist/keystone-ui-icons-icons-PauseCircleIcon.cjs.js", "module": "./icons/PauseCircleIcon/dist/keystone-ui-icons-icons-PauseCircleIcon.esm.js", "default": "./icons/PauseCircleIcon/dist/keystone-ui-icons-icons-PauseCircleIcon.cjs.js" }, "./icons/PhoneMissedIcon": { + "types": "./icons/PhoneMissedIcon/dist/keystone-ui-icons-icons-PhoneMissedIcon.cjs.js", "module": "./icons/PhoneMissedIcon/dist/keystone-ui-icons-icons-PhoneMissedIcon.esm.js", "default": "./icons/PhoneMissedIcon/dist/keystone-ui-icons-icons-PhoneMissedIcon.cjs.js" }, "./icons/ShoppingBagIcon": { + "types": "./icons/ShoppingBagIcon/dist/keystone-ui-icons-icons-ShoppingBagIcon.cjs.js", "module": "./icons/ShoppingBagIcon/dist/keystone-ui-icons-icons-ShoppingBagIcon.esm.js", "default": "./icons/ShoppingBagIcon/dist/keystone-ui-icons-icons-ShoppingBagIcon.cjs.js" }, "./icons/SkipForwardIcon": { + "types": "./icons/SkipForwardIcon/dist/keystone-ui-icons-icons-SkipForwardIcon.cjs.js", "module": "./icons/SkipForwardIcon/dist/keystone-ui-icons-icons-SkipForwardIcon.esm.js", "default": "./icons/SkipForwardIcon/dist/keystone-ui-icons-icons-SkipForwardIcon.cjs.js" }, "./icons/ThermometerIcon": { + "types": "./icons/ThermometerIcon/dist/keystone-ui-icons-icons-ThermometerIcon.cjs.js", "module": "./icons/ThermometerIcon/dist/keystone-ui-icons-icons-ThermometerIcon.esm.js", "default": "./icons/ThermometerIcon/dist/keystone-ui-icons-icons-ThermometerIcon.cjs.js" }, "./icons/ToggleRightIcon": { + "types": "./icons/ToggleRightIcon/dist/keystone-ui-icons-icons-ToggleRightIcon.cjs.js", "module": "./icons/ToggleRightIcon/dist/keystone-ui-icons-icons-ToggleRightIcon.esm.js", "default": "./icons/ToggleRightIcon/dist/keystone-ui-icons-icons-ToggleRightIcon.cjs.js" }, "./icons/UploadCloudIcon": { + "types": "./icons/UploadCloudIcon/dist/keystone-ui-icons-icons-UploadCloudIcon.cjs.js", "module": "./icons/UploadCloudIcon/dist/keystone-ui-icons-icons-UploadCloudIcon.esm.js", "default": "./icons/UploadCloudIcon/dist/keystone-ui-icons-icons-UploadCloudIcon.cjs.js" }, "./icons/AlertOctagonIcon": { + "types": "./icons/AlertOctagonIcon/dist/keystone-ui-icons-icons-AlertOctagonIcon.cjs.js", "module": "./icons/AlertOctagonIcon/dist/keystone-ui-icons-icons-AlertOctagonIcon.esm.js", "default": "./icons/AlertOctagonIcon/dist/keystone-ui-icons-icons-AlertOctagonIcon.cjs.js" }, "./icons/AlignJustifyIcon": { + "types": "./icons/AlignJustifyIcon/dist/keystone-ui-icons-icons-AlignJustifyIcon.cjs.js", "module": "./icons/AlignJustifyIcon/dist/keystone-ui-icons-icons-AlignJustifyIcon.esm.js", "default": "./icons/AlignJustifyIcon/dist/keystone-ui-icons-icons-AlignJustifyIcon.cjs.js" }, "./icons/ArrowUpRightIcon": { + "types": "./icons/ArrowUpRightIcon/dist/keystone-ui-icons-icons-ArrowUpRightIcon.cjs.js", "module": "./icons/ArrowUpRightIcon/dist/keystone-ui-icons-icons-ArrowUpRightIcon.esm.js", "default": "./icons/ArrowUpRightIcon/dist/keystone-ui-icons-icons-ArrowUpRightIcon.cjs.js" }, "./icons/ChevronRightIcon": { + "types": "./icons/ChevronRightIcon/dist/keystone-ui-icons-icons-ChevronRightIcon.cjs.js", "module": "./icons/ChevronRightIcon/dist/keystone-ui-icons-icons-ChevronRightIcon.esm.js", "default": "./icons/ChevronRightIcon/dist/keystone-ui-icons-icons-ChevronRightIcon.cjs.js" }, "./icons/ChevronsDownIcon": { + "types": "./icons/ChevronsDownIcon/dist/keystone-ui-icons-icons-ChevronsDownIcon.cjs.js", "module": "./icons/ChevronsDownIcon/dist/keystone-ui-icons-icons-ChevronsDownIcon.esm.js", "default": "./icons/ChevronsDownIcon/dist/keystone-ui-icons-icons-ChevronsDownIcon.cjs.js" }, "./icons/ChevronsLeftIcon": { + "types": "./icons/ChevronsLeftIcon/dist/keystone-ui-icons-icons-ChevronsLeftIcon.cjs.js", "module": "./icons/ChevronsLeftIcon/dist/keystone-ui-icons-icons-ChevronsLeftIcon.esm.js", "default": "./icons/ChevronsLeftIcon/dist/keystone-ui-icons-icons-ChevronsLeftIcon.cjs.js" }, "./icons/CloudDrizzleIcon": { + "types": "./icons/CloudDrizzleIcon/dist/keystone-ui-icons-icons-CloudDrizzleIcon.cjs.js", "module": "./icons/CloudDrizzleIcon/dist/keystone-ui-icons-icons-CloudDrizzleIcon.esm.js", "default": "./icons/CloudDrizzleIcon/dist/keystone-ui-icons-icons-CloudDrizzleIcon.cjs.js" }, "./icons/CornerLeftUpIcon": { + "types": "./icons/CornerLeftUpIcon/dist/keystone-ui-icons-icons-CornerLeftUpIcon.cjs.js", "module": "./icons/CornerLeftUpIcon/dist/keystone-ui-icons-icons-CornerLeftUpIcon.esm.js", "default": "./icons/CornerLeftUpIcon/dist/keystone-ui-icons-icons-CornerLeftUpIcon.cjs.js" }, "./icons/CornerUpLeftIcon": { + "types": "./icons/CornerUpLeftIcon/dist/keystone-ui-icons-icons-CornerUpLeftIcon.cjs.js", "module": "./icons/CornerUpLeftIcon/dist/keystone-ui-icons-icons-CornerUpLeftIcon.esm.js", "default": "./icons/CornerUpLeftIcon/dist/keystone-ui-icons-icons-CornerUpLeftIcon.cjs.js" }, "./icons/DivideCircleIcon": { + "types": "./icons/DivideCircleIcon/dist/keystone-ui-icons-icons-DivideCircleIcon.cjs.js", "module": "./icons/DivideCircleIcon/dist/keystone-ui-icons-icons-DivideCircleIcon.esm.js", "default": "./icons/DivideCircleIcon/dist/keystone-ui-icons-icons-DivideCircleIcon.cjs.js" }, "./icons/DivideSquareIcon": { + "types": "./icons/DivideSquareIcon/dist/keystone-ui-icons-icons-DivideSquareIcon.cjs.js", "module": "./icons/DivideSquareIcon/dist/keystone-ui-icons-icons-DivideSquareIcon.esm.js", "default": "./icons/DivideSquareIcon/dist/keystone-ui-icons-icons-DivideSquareIcon.cjs.js" }, "./icons/ExternalLinkIcon": { + "types": "./icons/ExternalLinkIcon/dist/keystone-ui-icons-icons-ExternalLinkIcon.cjs.js", "module": "./icons/ExternalLinkIcon/dist/keystone-ui-icons-icons-ExternalLinkIcon.esm.js", "default": "./icons/ExternalLinkIcon/dist/keystone-ui-icons-icons-ExternalLinkIcon.cjs.js" }, "./icons/MoreVerticalIcon": { + "types": "./icons/MoreVerticalIcon/dist/keystone-ui-icons-icons-MoreVerticalIcon.cjs.js", "module": "./icons/MoreVerticalIcon/dist/keystone-ui-icons-icons-MoreVerticalIcon.esm.js", "default": "./icons/MoreVerticalIcon/dist/keystone-ui-icons-icons-MoreVerticalIcon.cjs.js" }, "./icons/MousePointerIcon": { + "types": "./icons/MousePointerIcon/dist/keystone-ui-icons-icons-MousePointerIcon.cjs.js", "module": "./icons/MousePointerIcon/dist/keystone-ui-icons-icons-MousePointerIcon.esm.js", "default": "./icons/MousePointerIcon/dist/keystone-ui-icons-icons-MousePointerIcon.cjs.js" }, "./icons/ShoppingCartIcon": { + "types": "./icons/ShoppingCartIcon/dist/keystone-ui-icons-icons-ShoppingCartIcon.cjs.js", "module": "./icons/ShoppingCartIcon/dist/keystone-ui-icons-icons-ShoppingCartIcon.esm.js", "default": "./icons/ShoppingCartIcon/dist/keystone-ui-icons-icons-ShoppingCartIcon.cjs.js" }, "./icons/TrendingDownIcon": { + "types": "./icons/TrendingDownIcon/dist/keystone-ui-icons-icons-TrendingDownIcon.cjs.js", "module": "./icons/TrendingDownIcon/dist/keystone-ui-icons-icons-TrendingDownIcon.esm.js", "default": "./icons/TrendingDownIcon/dist/keystone-ui-icons-icons-TrendingDownIcon.cjs.js" }, "./icons/AlertTriangleIcon": { + "types": "./icons/AlertTriangleIcon/dist/keystone-ui-icons-icons-AlertTriangleIcon.cjs.js", "module": "./icons/AlertTriangleIcon/dist/keystone-ui-icons-icons-AlertTriangleIcon.esm.js", "default": "./icons/AlertTriangleIcon/dist/keystone-ui-icons-icons-AlertTriangleIcon.cjs.js" }, "./icons/ArrowDownLeftIcon": { + "types": "./icons/ArrowDownLeftIcon/dist/keystone-ui-icons-icons-ArrowDownLeftIcon.cjs.js", "module": "./icons/ArrowDownLeftIcon/dist/keystone-ui-icons-icons-ArrowDownLeftIcon.esm.js", "default": "./icons/ArrowDownLeftIcon/dist/keystone-ui-icons-icons-ArrowDownLeftIcon.cjs.js" }, "./icons/ArrowUpCircleIcon": { + "types": "./icons/ArrowUpCircleIcon/dist/keystone-ui-icons-icons-ArrowUpCircleIcon.cjs.js", "module": "./icons/ArrowUpCircleIcon/dist/keystone-ui-icons-icons-ArrowUpCircleIcon.esm.js", "default": "./icons/ArrowUpCircleIcon/dist/keystone-ui-icons-icons-ArrowUpCircleIcon.cjs.js" }, "./icons/ChevronsRightIcon": { + "types": "./icons/ChevronsRightIcon/dist/keystone-ui-icons-icons-ChevronsRightIcon.cjs.js", "module": "./icons/ChevronsRightIcon/dist/keystone-ui-icons-icons-ChevronsRightIcon.esm.js", "default": "./icons/ChevronsRightIcon/dist/keystone-ui-icons-icons-ChevronsRightIcon.cjs.js" }, "./icons/CornerRightUpIcon": { + "types": "./icons/CornerRightUpIcon/dist/keystone-ui-icons-icons-CornerRightUpIcon.cjs.js", "module": "./icons/CornerRightUpIcon/dist/keystone-ui-icons-icons-CornerRightUpIcon.esm.js", "default": "./icons/CornerRightUpIcon/dist/keystone-ui-icons-icons-CornerRightUpIcon.cjs.js" }, "./icons/CornerUpRightIcon": { + "types": "./icons/CornerUpRightIcon/dist/keystone-ui-icons-icons-CornerUpRightIcon.cjs.js", "module": "./icons/CornerUpRightIcon/dist/keystone-ui-icons-icons-CornerUpRightIcon.esm.js", "default": "./icons/CornerUpRightIcon/dist/keystone-ui-icons-icons-CornerUpRightIcon.cjs.js" }, "./icons/DownloadCloudIcon": { + "types": "./icons/DownloadCloudIcon/dist/keystone-ui-icons-icons-DownloadCloudIcon.cjs.js", "module": "./icons/DownloadCloudIcon/dist/keystone-ui-icons-icons-DownloadCloudIcon.esm.js", "default": "./icons/DownloadCloudIcon/dist/keystone-ui-icons-icons-DownloadCloudIcon.cjs.js" }, "./icons/MessageCircleIcon": { + "types": "./icons/MessageCircleIcon/dist/keystone-ui-icons-icons-MessageCircleIcon.cjs.js", "module": "./icons/MessageCircleIcon/dist/keystone-ui-icons-icons-MessageCircleIcon.esm.js", "default": "./icons/MessageCircleIcon/dist/keystone-ui-icons-icons-MessageCircleIcon.cjs.js" }, "./icons/MessageSquareIcon": { + "types": "./icons/MessageSquareIcon/dist/keystone-ui-icons-icons-MessageSquareIcon.cjs.js", "module": "./icons/MessageSquareIcon/dist/keystone-ui-icons-icons-MessageSquareIcon.esm.js", "default": "./icons/MessageSquareIcon/dist/keystone-ui-icons-icons-MessageSquareIcon.cjs.js" }, "./icons/PhoneIncomingIcon": { + "types": "./icons/PhoneIncomingIcon/dist/keystone-ui-icons-icons-PhoneIncomingIcon.cjs.js", "module": "./icons/PhoneIncomingIcon/dist/keystone-ui-icons-icons-PhoneIncomingIcon.esm.js", "default": "./icons/PhoneIncomingIcon/dist/keystone-ui-icons-icons-PhoneIncomingIcon.cjs.js" }, "./icons/PhoneOutgoingIcon": { + "types": "./icons/PhoneOutgoingIcon/dist/keystone-ui-icons-icons-PhoneOutgoingIcon.cjs.js", "module": "./icons/PhoneOutgoingIcon/dist/keystone-ui-icons-icons-PhoneOutgoingIcon.esm.js", "default": "./icons/PhoneOutgoingIcon/dist/keystone-ui-icons-icons-PhoneOutgoingIcon.cjs.js" }, "./icons/ArrowDownRightIcon": { + "types": "./icons/ArrowDownRightIcon/dist/keystone-ui-icons-icons-ArrowDownRightIcon.cjs.js", "module": "./icons/ArrowDownRightIcon/dist/keystone-ui-icons-icons-ArrowDownRightIcon.esm.js", "default": "./icons/ArrowDownRightIcon/dist/keystone-ui-icons-icons-ArrowDownRightIcon.cjs.js" }, "./icons/CloudLightningIcon": { + "types": "./icons/CloudLightningIcon/dist/keystone-ui-icons-icons-CloudLightningIcon.cjs.js", "module": "./icons/CloudLightningIcon/dist/keystone-ui-icons-icons-CloudLightningIcon.esm.js", "default": "./icons/CloudLightningIcon/dist/keystone-ui-icons-icons-CloudLightningIcon.cjs.js" }, "./icons/CornerDownLeftIcon": { + "types": "./icons/CornerDownLeftIcon/dist/keystone-ui-icons-icons-CornerDownLeftIcon.cjs.js", "module": "./icons/CornerDownLeftIcon/dist/keystone-ui-icons-icons-CornerDownLeftIcon.esm.js", "default": "./icons/CornerDownLeftIcon/dist/keystone-ui-icons-icons-CornerDownLeftIcon.cjs.js" }, "./icons/CornerLeftDownIcon": { + "types": "./icons/CornerLeftDownIcon/dist/keystone-ui-icons-icons-CornerLeftDownIcon.cjs.js", "module": "./icons/CornerLeftDownIcon/dist/keystone-ui-icons-icons-CornerLeftDownIcon.esm.js", "default": "./icons/CornerLeftDownIcon/dist/keystone-ui-icons-icons-CornerLeftDownIcon.cjs.js" }, "./icons/GitPullRequestIcon": { + "types": "./icons/GitPullRequestIcon/dist/keystone-ui-icons-icons-GitPullRequestIcon.cjs.js", "module": "./icons/GitPullRequestIcon/dist/keystone-ui-icons-icons-GitPullRequestIcon.esm.js", "default": "./icons/GitPullRequestIcon/dist/keystone-ui-icons-icons-GitPullRequestIcon.cjs.js" }, "./icons/MoreHorizontalIcon": { + "types": "./icons/MoreHorizontalIcon/dist/keystone-ui-icons-icons-MoreHorizontalIcon.cjs.js", "module": "./icons/MoreHorizontalIcon/dist/keystone-ui-icons-icons-MoreHorizontalIcon.esm.js", "default": "./icons/MoreHorizontalIcon/dist/keystone-ui-icons-icons-MoreHorizontalIcon.cjs.js" }, "./icons/PhoneForwardedIcon": { + "types": "./icons/PhoneForwardedIcon/dist/keystone-ui-icons-icons-PhoneForwardedIcon.cjs.js", "module": "./icons/PhoneForwardedIcon/dist/keystone-ui-icons-icons-PhoneForwardedIcon.esm.js", "default": "./icons/PhoneForwardedIcon/dist/keystone-ui-icons-icons-PhoneForwardedIcon.cjs.js" }, "./icons/ArrowDownCircleIcon": { + "types": "./icons/ArrowDownCircleIcon/dist/keystone-ui-icons-icons-ArrowDownCircleIcon.cjs.js", "module": "./icons/ArrowDownCircleIcon/dist/keystone-ui-icons-icons-ArrowDownCircleIcon.esm.js", "default": "./icons/ArrowDownCircleIcon/dist/keystone-ui-icons-icons-ArrowDownCircleIcon.cjs.js" }, "./icons/ArrowLeftCircleIcon": { + "types": "./icons/ArrowLeftCircleIcon/dist/keystone-ui-icons-icons-ArrowLeftCircleIcon.cjs.js", "module": "./icons/ArrowLeftCircleIcon/dist/keystone-ui-icons-icons-ArrowLeftCircleIcon.esm.js", "default": "./icons/ArrowLeftCircleIcon/dist/keystone-ui-icons-icons-ArrowLeftCircleIcon.cjs.js" }, "./icons/BatteryChargingIcon": { + "types": "./icons/BatteryChargingIcon/dist/keystone-ui-icons-icons-BatteryChargingIcon.cjs.js", "module": "./icons/BatteryChargingIcon/dist/keystone-ui-icons-icons-BatteryChargingIcon.esm.js", "default": "./icons/BatteryChargingIcon/dist/keystone-ui-icons-icons-BatteryChargingIcon.cjs.js" }, "./icons/CornerDownRightIcon": { + "types": "./icons/CornerDownRightIcon/dist/keystone-ui-icons-icons-CornerDownRightIcon.cjs.js", "module": "./icons/CornerDownRightIcon/dist/keystone-ui-icons-icons-CornerDownRightIcon.esm.js", "default": "./icons/CornerDownRightIcon/dist/keystone-ui-icons-icons-CornerDownRightIcon.cjs.js" }, "./icons/CornerRightDownIcon": { + "types": "./icons/CornerRightDownIcon/dist/keystone-ui-icons-icons-CornerRightDownIcon.cjs.js", "module": "./icons/CornerRightDownIcon/dist/keystone-ui-icons-icons-CornerRightDownIcon.esm.js", "default": "./icons/CornerRightDownIcon/dist/keystone-ui-icons-icons-CornerRightDownIcon.cjs.js" }, "./icons/ArrowRightCircleIcon": { + "types": "./icons/ArrowRightCircleIcon/dist/keystone-ui-icons-icons-ArrowRightCircleIcon.cjs.js", "module": "./icons/ArrowRightCircleIcon/dist/keystone-ui-icons-icons-ArrowRightCircleIcon.esm.js", "default": "./icons/ArrowRightCircleIcon/dist/keystone-ui-icons-icons-ArrowRightCircleIcon.cjs.js" }, diff --git a/design-system/packages/loading/package.json b/design-system/packages/loading/package.json index 161bef8e560..b15c45bdb7a 100644 --- a/design-system/packages/loading/package.json +++ b/design-system/packages/loading/package.json @@ -6,6 +6,7 @@ "module": "dist/keystone-ui-loading.esm.js", "exports": { ".": { + "types": "./dist/keystone-ui-loading.cjs.js", "module": "./dist/keystone-ui-loading.esm.js", "default": "./dist/keystone-ui-loading.cjs.js" }, diff --git a/design-system/packages/modals/package.json b/design-system/packages/modals/package.json index 0ec62b0f393..6f8d0a0eeca 100644 --- a/design-system/packages/modals/package.json +++ b/design-system/packages/modals/package.json @@ -6,6 +6,7 @@ "module": "dist/keystone-ui-modals.esm.js", "exports": { ".": { + "types": "./dist/keystone-ui-modals.cjs.js", "module": "./dist/keystone-ui-modals.esm.js", "default": "./dist/keystone-ui-modals.cjs.js" }, diff --git a/design-system/packages/notice/package.json b/design-system/packages/notice/package.json index bb87940b905..63819a4eec3 100644 --- a/design-system/packages/notice/package.json +++ b/design-system/packages/notice/package.json @@ -6,6 +6,7 @@ "module": "dist/keystone-ui-notice.esm.js", "exports": { ".": { + "types": "./dist/keystone-ui-notice.cjs.js", "module": "./dist/keystone-ui-notice.esm.js", "default": "./dist/keystone-ui-notice.cjs.js" }, diff --git a/design-system/packages/options/package.json b/design-system/packages/options/package.json index 38e4ec075b4..bbd4010c015 100644 --- a/design-system/packages/options/package.json +++ b/design-system/packages/options/package.json @@ -6,6 +6,7 @@ "module": "dist/keystone-ui-options.esm.js", "exports": { ".": { + "types": "./dist/keystone-ui-options.cjs.js", "module": "./dist/keystone-ui-options.esm.js", "default": "./dist/keystone-ui-options.cjs.js" }, diff --git a/design-system/packages/pill/package.json b/design-system/packages/pill/package.json index 4cfa2e947f7..445995f015a 100644 --- a/design-system/packages/pill/package.json +++ b/design-system/packages/pill/package.json @@ -6,6 +6,7 @@ "module": "dist/keystone-ui-pill.esm.js", "exports": { ".": { + "types": "./dist/keystone-ui-pill.cjs.js", "module": "./dist/keystone-ui-pill.esm.js", "default": "./dist/keystone-ui-pill.cjs.js" }, diff --git a/design-system/packages/popover/package.json b/design-system/packages/popover/package.json index a7164d3d0b6..e5a7acdb124 100644 --- a/design-system/packages/popover/package.json +++ b/design-system/packages/popover/package.json @@ -6,6 +6,7 @@ "module": "dist/keystone-ui-popover.esm.js", "exports": { ".": { + "types": "./dist/keystone-ui-popover.cjs.js", "module": "./dist/keystone-ui-popover.esm.js", "default": "./dist/keystone-ui-popover.cjs.js" }, diff --git a/design-system/packages/segmented-control/package.json b/design-system/packages/segmented-control/package.json index 12fdfb60c44..be35abbd46f 100644 --- a/design-system/packages/segmented-control/package.json +++ b/design-system/packages/segmented-control/package.json @@ -6,6 +6,7 @@ "module": "dist/keystone-ui-segmented-control.esm.js", "exports": { ".": { + "types": "./dist/keystone-ui-segmented-control.cjs.js", "module": "./dist/keystone-ui-segmented-control.esm.js", "default": "./dist/keystone-ui-segmented-control.cjs.js" }, diff --git a/design-system/packages/toast/package.json b/design-system/packages/toast/package.json index 76f139fceb0..db12310372b 100644 --- a/design-system/packages/toast/package.json +++ b/design-system/packages/toast/package.json @@ -6,6 +6,7 @@ "module": "dist/keystone-ui-toast.esm.js", "exports": { ".": { + "types": "./dist/keystone-ui-toast.cjs.js", "module": "./dist/keystone-ui-toast.esm.js", "default": "./dist/keystone-ui-toast.cjs.js" }, diff --git a/design-system/packages/tooltip/package.json b/design-system/packages/tooltip/package.json index e4bc853c3c5..6eaa0b3aa7a 100644 --- a/design-system/packages/tooltip/package.json +++ b/design-system/packages/tooltip/package.json @@ -6,6 +6,7 @@ "module": "dist/keystone-ui-tooltip.esm.js", "exports": { ".": { + "types": "./dist/keystone-ui-tooltip.cjs.js", "module": "./dist/keystone-ui-tooltip.esm.js", "default": "./dist/keystone-ui-tooltip.cjs.js" }, diff --git a/design-system/packages/tooltip/src/Tooltip.tsx b/design-system/packages/tooltip/src/Tooltip.tsx index 3771b4261e9..a78c73bc947 100644 --- a/design-system/packages/tooltip/src/Tooltip.tsx +++ b/design-system/packages/tooltip/src/Tooltip.tsx @@ -136,6 +136,14 @@ export const TooltipElement = memo( const { elevation, radii, colors, spacing, typography } = useTheme() const arrowStyles = useArrowStyles() + // Fix for hidden tooltips breaking responsive layout. There's no + // animation so not sure why we weren't doing this already. + // Leaving everything else as is for now. We should replace instances with + // Keystar UI components eventually. + if (!isVisible) { + return null + } + return (
{ +function Brand () { const { palette } = useTheme() return (

@@ -25,7 +25,7 @@ const Brand = () => { } type SectionProps = { label: string, children: ReactNode } -const Section = ({ label, children }: SectionProps) => { +function Section ({ label, children }: SectionProps) { return (

{label}

@@ -35,7 +35,7 @@ const Section = ({ label, children }: SectionProps) => { } type NavItemProps = { href: string, children: ReactNode } -const NavItem = ({ href, children }: NavItemProps) => { +function NavItem ({ href, children }: NavItemProps) { const { palette, radii, spacing } = useTheme() const router = useRouter() const isSelected = router.pathname === href @@ -68,7 +68,7 @@ const NavItem = ({ href, children }: NavItemProps) => { ) } -export const Navigation = () => { +export function Navigation () { return ( diff --git a/docs/content/docs/config/auth.md b/docs/content/docs/config/auth.md index ddd01245534..606cb0cdc1c 100644 --- a/docs/content/docs/config/auth.md +++ b/docs/content/docs/config/auth.md @@ -81,6 +81,7 @@ The following elements will be added to the GraphQL API. ```graphql type Mutation { authenticateUserWithPassword(email: String!, password: String!): UserAuthenticationWithPasswordResult! + endSession: Boolean! } type Query { diff --git a/docs/content/docs/config/lists.md b/docs/content/docs/config/lists.md index eebfed74b33..3898a1d5c6b 100644 --- a/docs/content/docs/config/lists.md +++ b/docs/content/docs/config/lists.md @@ -81,7 +81,7 @@ Options: It is always possible to search by an id and `'id'` should not be specified in this option. By default, the `labelField` is used if it has a string `contains` filter, otherwise none. - `description` (default: `undefined`): Sets the list description displayed in the Admin UI. -- `isHidden` (default: `false`): Controls whether the list is visible in the navigation elements of the Admin UI. +- `hideNavigation` (default: `false`): Controls whether the list is visible in the navigation elements of the Admin UI. Can be either a boolean value or an async function with an argument `{ session, context }` that returns a boolean value. - `hideCreate` (default: `false`): Controls whether the `create` button is available in the Admin UI for this list. Can be either a boolean value or an async function with an argument `{ session, context }` that returns a boolean value. @@ -124,7 +124,7 @@ export default config({ labelField: 'name', searchFields: ['name', 'alternativeName'], description: '...', - isHidden: ({ session, context }) => false, + hideNavigation: ({ session, context }) => false, hideCreate: ({ session, context }) => false, hideDelete: ({ session, context }) => false, createView: { diff --git a/docs/content/docs/fields/relationship.md b/docs/content/docs/fields/relationship.md index 16416f3a9fe..f173d1f8166 100644 --- a/docs/content/docs/fields/relationship.md +++ b/docs/content/docs/fields/relationship.md @@ -11,23 +11,11 @@ Read our [relationships guide](../guides/relationships) for details on Keystone - `many` (default: `false`): Configures the cardinality of the relationship. - `db.foreignKey`: When `true` or an object, ensures the foreign Key for two-sided relationships is stored in the table for this list (only available on single relationships, and not on both sides of a 1:1 relationship) - `map`: Changes the column name in the database -- `ui` (default: `{ hideCreate: false, displayMode: 'select' }`): Configures the display mode of the field in the Admin UI. - - `hideCreate` (default: `false`). If `true`, the "Create related item" button is not shown in the item view. - - `displayMode` (default: `'select'`): Controls the mode used to display the field in the item view. The mode `'select'` displays related items in a select component, while `'cards'` displays the related items in a card layout. Each display mode supports further configuration. -- `displayMode === 'select'` options: +- `ui`: Configures the display mode of the field in the Admin UI + - `displayMode` (default: `select`): If `count`, only `many: true` relationships are supported - `labelField`: The field path from the related list to use for item labels in the select. Defaults to the `labelField` configured on the related list. -- `searchFields`: The fields used by the Admin UI when searching by this relationship on the list view and in relationship fields. Nominated fields need to support the `contains` filter. -- `displayMode === 'cards'` options: - - `cardFields`: A list of field paths from the related list to render in the card component. Defaults to `'id'` and the `labelField` configured on the related list. - - `linkToItem` (default `false`): If `true`, the default card component will render as a link to navigate to the related item. - - `removeMode` (default: `'disconnect'`): Controls whether the `Remove` button is present in the card. If `'disconnect'`, the button will be present. If `'none'`, the button will not be present. - - `inlineCreate` (default: `null`): If not `null`, an object of the form `{ fields: [...] }`, where `fields` is a list of field paths from the related list should be provided. An inline `Create` button will be included in the cards allowing a new related item to be created based on the configured field paths. - - `inlineEdit` (default: `null`): If not `null`, an object of the form `{ fields: [...] }`, where `fields` is a list of field paths from the related list should be provided. An `Edit` button will be included in each card, allowing the configured fields to be edited for each related item. - - `inlineConnect` (default: `false`): If `true`, an inline `Link existing item` button will be present, allowing existing items of the related list to be connected in this field. -Alternatively this can be an object with the properties: - - `labelField`: The field path from the related list to use for item labels in select. Defaults to the `labelField` configured on the related list. - - `searchFields`: The fields used by the UI to search for this item, in context of this relationship field. Defaults to `searchFields` configured on the related list. -- `displayMode === 'count'` only supports `many` relationships + - `searchFields`: The fields used by the Admin UI when searching by this relationship on the list view and in relationship fields. Nominated fields need to support the `contains` filter. Defaults to `ui.listView.searchFieldon the related list. + - `hideCreate` (default: `false`). If `true`, the "Create related item" button is not shown in the item view. ```typescript import { config, list } from '@keystone-6/core'; diff --git a/docs/content/docs/guides/custom-admin-ui-navigation.md b/docs/content/docs/guides/custom-admin-ui-navigation.md index 928a4024f18..df51c64ded5 100644 --- a/docs/content/docs/guides/custom-admin-ui-navigation.md +++ b/docs/content/docs/guides/custom-admin-ui-navigation.md @@ -18,8 +18,8 @@ Reference your custom Navigation component in the export as so. ```ts // /admin/config.ts -import type { AdminConfig } from '@keystone-6/core/types'; -import { CustomNavigation } from './components/CustomNavigation'; +import type { AdminConfig } from '@keystone-6/core/types' +import { CustomNavigation } from './components/CustomNavigation' export const components: AdminConfig['components'] = { Navigation: CustomNavigation @@ -36,8 +36,8 @@ Here we'll export our `CustomNavigation` component as a named export. ```tsx // admin/components/CustomNavigation.tsx -import type { NavigationProps } from '@keystone-6/core/admin-ui/components'; -export function CustomNavigation({ authenticatedItem, lists }: NavigationProps) { +import type { NavigationProps } from '@keystone-6/core/admin-ui/components' +export function CustomNavigation({ lists }: NavigationProps) { return ( {/* ... */} ) @@ -48,8 +48,7 @@ Keystone will pass the following props to this component. ```tsx type NavigationProps = { - lists: ListMeta[]; - authenticatedItem: AuthenticatedItem + lists: ListMeta[] } ``` @@ -62,21 +61,20 @@ For more information on the props, please see the [Navigation Props](#navigation Next we'll want to import some components that Keystone provides to help us build our custom Navigation. ```tsx -import { NavigationContainer, NavItem, ListNavItems } from '@keystone-6/core/admin-ui/components'; +import { NavigationContainer, NavItem, ListNavItems } from '@keystone-6/core/admin-ui/components' ``` -The `NavigationContainer` component provides a container around your navigation links, as well as the different states of the `authenticatedItem` prop. We'll need this to: +The `NavigationContainer` component provides a container around your navigation links. - Make our `CustomNavigation` component look and feel like the default Admin UI Navigation component. -- Render out the hamburger menu with additional options should a user session be in progress via the `authenticatedItem` prop. ```tsx -import { NavigationContainer, NavItem, ListNavItems } from '@keystone-6/core/admin-ui/components'; -import type { NavigationProps } from '@keystone-6/core/admin-ui/components'; +import { NavigationContainer, NavItem, ListNavItems } from '@keystone-6/core/admin-ui/components' +import type { NavigationProps } from '@keystone-6/core/admin-ui/components' -export function CustomNavigation({ authenticatedItem, lists }: NavigationProps) { +export function CustomNavigation({ lists }: NavigationProps) { return ( - + {/* ... */} ) @@ -92,12 +90,12 @@ For more information on the `NavigationContainer` see the [NavigationContainer]( The `ListNavItems` component takes the provided Array of `lists` and renders a list of NavItems. We'll use this component to help us easily create NavItems from Keystone lists. ```tsx -import { NavigationContainer, NavItem, ListNavItems } from '@keystone-6/core/admin-ui/components'; -import type { NavigationProps } from '@keystone-6/core/admin-ui/components'; +import { NavigationContainer, NavItem, ListNavItems } from '@keystone-6/core/admin-ui/components' +import type { NavigationProps } from '@keystone-6/core/admin-ui/components' -export function CustomNavigation({ authenticatedItem, lists }: NavigationProps) { +export function CustomNavigation({ lists }: NavigationProps) { return ( - + {/* ... */} @@ -114,12 +112,12 @@ For more information on the `ListNavItems` component, see the [ListNavItems](#li The `NavItem` component is a thin styling and accessibility wrapper around the `Link` component from Next.js. We'll use this component to render our custom route as well as the `Dashboard` route. ```tsx -import { NavigationContainer, NavItem, ListNavItems } from '@keystone-6/core/admin-ui/components'; -import type { NavigationProps } from '@keystone-6/core/admin-ui/components'; +import { NavigationContainer, NavItem, ListNavItems } from '@keystone-6/core/admin-ui/components' +import type { NavigationProps } from '@keystone-6/core/admin-ui/components' -export function CustomNavigation({ authenticatedItem, lists }: NavigationProps) { +export function CustomNavigation({ lists }: NavigationProps) { return ( - + Dashboard @@ -144,12 +142,12 @@ With all that done, your Custom Navigation component should be good to go, and y ```tsx // admin/components/CustomNavigation.tsx -import { NavigationContainer, NavItem, ListNavItems } from '@keystone-6/core/admin-ui/components'; -import type { NavigationProps } from '@keystone-6/core/admin-ui/components'; +import { NavigationContainer, NavItem, ListNavItems } from '@keystone-6/core/admin-ui/components' +import type { NavigationProps } from '@keystone-6/core/admin-ui/components' -export function CustomNavigation({ authenticatedItem, lists }: NavigationProps) { +export function CustomNavigation({ lists }: NavigationProps) { return ( - + Dashboard @@ -161,11 +159,11 @@ export function CustomNavigation({ authenticatedItem, lists }: NavigationProps) // admin/config.ts -import { AdminConfig } from '@keystone-6/core/types'; -import { CustomNavigation } from './components/CustomNavigation'; +import { AdminConfig } from '@keystone-6/core/types' +import { CustomNavigation } from './components/CustomNavigation' export const components: AdminConfig['components'] = { Navigation: CustomNavigation -}; +} ``` Start up your Keystone project, and you should see Custom Navigation with a route to the KeystoneJS docs in the Admin UI. @@ -180,15 +178,14 @@ The rest of this guide will elaborate on some of the details around the helper c This section is to provide more granular information around the props that Keystone passes to your Custom Navigation component. ```tsx -export const CustomNavigation = ({ lists, authenticatedItem }) => {} +export const CustomNavigation = ({ lists }) => {} ``` Keystone passes the following props to your custom Navigation component: ```ts type NavigationProps { - lists: ListMeta[]; - authenticatedItem: AuthenticatedItem; + lists: ListMeta[] } ``` @@ -197,33 +194,17 @@ type NavigationProps { ```typescript type ListMeta = { /** Used for optimising the generated list of NavItems in React */ - key: string; + key: string /** href to the list route in the Admin UI. */ - path: string; + path: string /** Used as the label for each list generated NavItem */ - label: string; + label: string /** Other properties exists, but these are the ones that are relevant to the Navigation implementation */ -}; - -type Lists = ListMeta[]; -``` - -`authenticatedItem` is a union of potential authentication states, expanded on below: +} -```ts -type AuthenticatedItem = - | { state: 'unauthenticated' } - | { state: 'authenticated'; label: string; id: string; listKey: string } - | { state: 'loading' } - | { state: 'error'; error: Error | readonly [GraphQLError, ...GraphQLError[]] }; +type Lists = ListMeta[] ``` -The `authenticatedItem` props is rendered automatically when you pass it into the `NavigationContainer` component. - -{% hint kind="warn" %} -If you render the `authenticatedItem` yourself, make sure you handle all of the possible states. -{% /hint %} - ## Components Keystone exposes a variety of helper components to make building out your custom Admin UI Navigation component easier. These are: @@ -234,24 +215,14 @@ Keystone exposes a variety of helper components to make building out your custom ### NavigationContainer -This component renders containing markup around your navigation links, as well as as the different states of the `authenticatedItem` prop. - -```typescript -type NavigationContainerProps = { - authenticatedItem?: AuthenticatedItem; -} -``` - -{% hint kind="tip" %} -For the shape of the `authenticatedItem` prop, please see the [Navigation Props](#navigation-props) section above. -{% /hint %} +This component renders containing markup around your navigation links. ```tsx import { NavigationContainer} from '@keystone-6/core/admin-ui/components' -export const CustomNavigation = ({ lists, authenticatedItem }) => { +export function CustomNavigation ({ lists }) { return ( - + {/* ... */} ) diff --git a/docs/content/docs/guides/custom-admin-ui-pages.md b/docs/content/docs/guides/custom-admin-ui-pages.md index 1ae24fd08dd..b5ac2d49645 100644 --- a/docs/content/docs/guides/custom-admin-ui-pages.md +++ b/docs/content/docs/guides/custom-admin-ui-pages.md @@ -14,7 +14,7 @@ The default export of every file in this directory is expected to be a valid Rea ```tsx // admin/pages/custom-page.tsx -import Link from 'next/link'; +import Link from 'next/link' export default function CustomPage () { return ( @@ -43,8 +43,8 @@ This `header` prop is rendered out as the page title at the top of the page. ```tsx // admin/pages/custom-page.tsx -import Link from 'next/link'; -import { PageContainer } from '@keystone-6/core/admin-ui/components'; +import Link from 'next/link' +import { PageContainer } from '@keystone-6/core/admin-ui/components' export default function CustomPage () { return ( @@ -66,8 +66,8 @@ Keystone pages leverage the `Heading` component from the `@keystone-ui/core` pac ```tsx // admin/pages/custom-page.tsx. -import { PageContainer } from '@keystone-6/core/admin-ui/components'; -import { Heading } from '@keystone-ui/core'; +import { PageContainer } from '@keystone-6/core/admin-ui/components' +import { Heading } from '@keystone-ui/core' export default function CustomPage () { return ( @@ -91,20 +91,20 @@ First add the following files to the `/admin` directory in the root of your Keys ```tsx // admin/config.ts -import type { AdminConfig } from '@keystone-6/core/types'; -import { CustomNavigation } from './components/CustomNavigation'; +import type { AdminConfig } from '@keystone-6/core/types' +import { CustomNavigation } from './components/CustomNavigation' export const components: AdminConfig['components']= { Navigation: CustomNavigation -}; +} ``` ```tsx // admin/components/CustomNavigation.tsx -import { NavigationContainer, ListNavItems, NavItem } from '@keystone-6/core/admin-ui/components'; -import type { NavigationProps } from '@keystone-6/core/admin-ui/components'; -export function CustomNavigation({ lists, authenticatedItem }: NavigationProps) { +import { NavigationContainer, ListNavItems, NavItem } from '@keystone-6/core/admin-ui/components' +import type { NavigationProps } from '@keystone-6/core/admin-ui/components' +export function CustomNavigation({ lists }: NavigationProps) { return ( - + Dashboard @@ -124,11 +124,11 @@ Lastly we'll add our new route to the newly created `CustomNavigation` component ```tsx // admin/components/CustomNavigation.tsx -import { NavigationContainer, ListNavItems, NavItem } from '@keystone-6/core/admin-ui/components'; -import type { NavigationProps } from '@keystone-6/core/admin-ui/components'; -export function CustomNavigation({ lists, authenticatedItem }: NavigationProps) { +import { NavigationContainer, ListNavItems, NavItem } from '@keystone-6/core/admin-ui/components' +import type { NavigationProps } from '@keystone-6/core/admin-ui/components' +export function CustomNavigation({ lists }: NavigationProps) { return ( - + Dashboard Custom Page @@ -155,10 +155,10 @@ The snippet below uses the emotion `jsx` runtime exported from `@keystone-ui/cor // admin/pages/custom-page.tsx /** @jsxRuntime classic */ -import Link from 'next/link'; -import { jsx } from '@keystone-ui/core'; -import { PageContainer } from '@keystone-6/core/admin-ui/components'; -import { Heading } from '@keystone-ui/core'; +import Link from 'next/link' +import { jsx } from '@keystone-ui/core' +import { PageContainer } from '@keystone-6/core/admin-ui/components' +import { Heading } from '@keystone-ui/core' export default function CustomPage () { return ( diff --git a/docs/content/docs/guides/custom-fields.md b/docs/content/docs/guides/custom-fields.md index ec8f629917e..b00e2e69cd7 100644 --- a/docs/content/docs/guides/custom-fields.md +++ b/docs/content/docs/guides/custom-fields.md @@ -180,31 +180,9 @@ Note it does not allow modifying the value. import { CellLink, CellContainer } from '@keystone-6/core/admin-ui/components'; import { CellComponent } from '@keystone-6/core/types'; -export const Cell: CellComponent = ({ item, field, linkTo }) => { - let value = item[field.path] + ''; - return linkTo ? {value} : {value}; -}; -Cell.supportsLinkTo = true; -``` - -### CardValue - -The `CardValue` export is a React component which is shown on the **item view** in relationship fields with `displayMode: 'cards'` when the related item is not being edited. -Note it does not allow modifying the value. - -```tsx -// view.tsx - -import { FieldContainer, FieldLabel } from '@keystone-ui/fields'; -import { CardValueComponent } from '@keystone-6/core/types'; - -export const CardValue: CardValueComponent = ({ item, field }) => { - return ( - - {field.label} - {item[field.path]} - - ); +export const Cell: CellComponent = ({ item, field }) => { + const value = item[field.path] + ''; + return {value}; }; ``` diff --git a/docs/package.json b/docs/package.json index 70ee628a34f..1d982080186 100644 --- a/docs/package.json +++ b/docs/package.json @@ -21,7 +21,7 @@ "@emotion/react": "^11.7.1", "@emotion/server": "11.11.0", "@emotion/weak-memoize": "^0.4.0", - "@keystar/ui": "^0.7.6", + "@keystar/ui": "^0.7.13", "@keystatic/core": "^0.5.24", "@keystatic/next": "^5.0.1", "@keystone-6/fields-document": "workspace:^", diff --git a/examples/assets-local/schema.graphql b/examples/assets-local/schema.graphql index 04eb959be33..a8345fd094d 100644 --- a/examples/assets-local/schema.graphql +++ b/examples/assets-local/schema.graphql @@ -157,24 +157,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/assets-s3/schema.graphql b/examples/assets-s3/schema.graphql index 04eb959be33..a8345fd094d 100644 --- a/examples/assets-s3/schema.graphql +++ b/examples/assets-s3/schema.graphql @@ -157,24 +157,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/auth/schema.graphql b/examples/auth/schema.graphql index 246333e2b9e..3660513b490 100644 --- a/examples/auth/schema.graphql +++ b/examples/auth/schema.graphql @@ -105,11 +105,9 @@ type Query { users(where: UserWhereInput! = {}, orderBy: [UserOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: UserWhereUniqueInput): [User!] usersCount(where: UserWhereInput! = {}): Int keystone: KeystoneMeta! - authenticatedItem: AuthenticatedItem + authenticatedItem: User } -union AuthenticatedItem = User - type KeystoneMeta { adminMeta: KeystoneAdminMeta! } @@ -122,24 +120,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/better-list-search/README.md b/examples/better-list-search/README.md deleted file mode 100644 index 479accb2e51..00000000000 --- a/examples/better-list-search/README.md +++ /dev/null @@ -1,32 +0,0 @@ -## Base Project - Blog - -This project implements a basic **Blog**, with `Posts` and `Authors`. - -Use it as a starting place for learning how to use Keystone. - -## Instructions - -To run this project, clone the Keystone repository locally, run `pnpm install` at the root of this repository, then navigate to this directory and run: - -```shell -pnpm dev -``` - -This will start the Admin UI at [localhost:3000](http://localhost:3000). -You can use the Admin UI to create items in your database. - -You can also access a GraphQL Playground at [localhost:3000/api/graphql](http://localhost:3000/api/graphql), which allows you to directly run GraphQL queries and mutations. - -Congratulations, you're now up and running with Keystone! 🚀 - -### Optional: add sample data - -This example includes sample data. To add it to your database: - -1. Ensure you’ve initialised your project with `pnpm dev` at least once. -2. Run `pnpm seed-data`. This will populate your database with sample content. -3. Run `pnpm dev` again to startup Admin UI with sample data in place. - -## Try it out in CodeSandbox 🧪 - -You can play with this example online in a web browser using the free [codesandbox.io](https://codesandbox.io/) service. To launch this example, open the URL . You can also fork this sandbox to make your own changes. diff --git a/examples/better-list-search/package.json b/examples/better-list-search/package.json index 83b3b1cad39..5599f807001 100644 --- a/examples/better-list-search/package.json +++ b/examples/better-list-search/package.json @@ -7,8 +7,7 @@ "dev": "keystone dev", "start": "keystone start", "build": "keystone build", - "postinstall": "keystone postinstall", - "seed-data": "tsx seed-data.ts" + "postinstall": "keystone postinstall" }, "dependencies": { "@keystone-6/core": "^6.3.1", diff --git a/examples/better-list-search/schema.graphql b/examples/better-list-search/schema.graphql index b062d907379..1606b802718 100644 --- a/examples/better-list-search/schema.graphql +++ b/examples/better-list-search/schema.graphql @@ -267,24 +267,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/cloudinary/schema.graphql b/examples/cloudinary/schema.graphql index 6f88003e940..632b27725ff 100644 --- a/examples/cloudinary/schema.graphql +++ b/examples/cloudinary/schema.graphql @@ -172,24 +172,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/custom-admin-ui-logo/schema.graphql b/examples/custom-admin-ui-logo/schema.graphql index 57b33b45d9b..490492d80e3 100644 --- a/examples/custom-admin-ui-logo/schema.graphql +++ b/examples/custom-admin-ui-logo/schema.graphql @@ -241,24 +241,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/custom-admin-ui-navigation/README.md b/examples/custom-admin-ui-navigation/README.md index fa65635f31e..6a60a95e4bd 100644 --- a/examples/custom-admin-ui-navigation/README.md +++ b/examples/custom-admin-ui-navigation/README.md @@ -24,26 +24,26 @@ The project contains an `config.ts` file in the `/admin` directory at its root. export const components = { Logo, Navigation, -}; +} ``` The exported components object is expected to have the following type signature: ```typescript { - Logo?: (props: {}) => ReactElement; - Navigation?: (props: NavigationProps) => ReactElement; + Logo?: (props: {}) => ReactElement + Navigation?: (props: NavigationProps) => ReactElement } ``` Keystone conveniently exports an AdminConfig type for DX. ```typescript -import { AdminConfig } from '@keystone-6/core/types'; +import { AdminConfig } from '@keystone-6/core/types' export const components: AdminConfig['components'] = { Logo, Navigation, -}; +} ``` ## NavigationProps @@ -52,25 +52,10 @@ Keystone passes the following props to your custom Navigation component: ```typescript NavigationProps = { - authenticatedItem: AuthenticatedItem, lists: ListMeta[] } ``` -### authenticatedItem prop - -The authenticatedItem prop is a `Union` representative of the following possible authentication states: - -```typescript -type AuthenticatedItem = - | { state: 'unauthenticated' } - | { state: 'authenticated'; label: string; id: string; listKey: string } - | { state: 'loading' } - | { state: 'error'; error: Error | readonly [GraphQLError, ...GraphQLError[]] }; -``` - -You will need to reasonably account for all of these states if you would like to role your own AuthenticatedItem component but that's out of the scope for this particular example. - ### lists prop The `lists` prop is an array of keystone list objects. @@ -78,56 +63,50 @@ The `lists` prop is an array of keystone list objects. ```typescript type ListMeta = { /** Used for optimising the generated list of NavItems in React */ - key: string; + key: string /** Used as the href for each list generated NavItem */ - path: string; + path: string /** Used as the label for each list generated NavItem */ - label: string; + label: string /** Other properties exists, but these are the ones that are relevant to the Navigation implementation */ -}; +} -type Lists = ListMeta[]; +type Lists = ListMeta[] ``` ## Navigation components Keystone exports the following components to make creating your own custom Navigation component easier. These are: -- NavigationContainer: A generic container element that also includes rendering logic for the passed in authenticatedItem. +- NavigationContainer: A generic container element - ListNavItems: This component renders out a list of Nav Items with preconfigured route matching logic optimised for lists. - NavItem: A thin styling and accessibility wrapper around the Next.js `Link` component. ### NavigationContainer The NavigationContainer is responsible for rendering semantic markup and styles for the containing element around your navigation items. -It's also responsible for rendering out the `authenticatedItem` should it exist. It has the following prop signature: ```typescript type NavigationContainerProps = { - authenticatedItem: AuthenticatedItem; - children: ReactNode; -}; -``` - -Pass it the `authenticatedItem` from the `NavigationProps` for it to handle rendering logic around user signin. + children: ReactNode +} -```tsx -const CustomNavigation = ({ authenticatedItem }) => { +const CustomNavigation = () => { return ( - - {/* The rest of your Nav goes here */} + + {/* the rest goes here */} - ); -}; + ) +} ``` ### ListNavItems ```ts type ListNavItemsProps = { - lists: ListMeta[]; - include?: string[]; -}; + lists: ListMeta[] + include?: string[] +} ``` The ListNavItems component expects `lists` an array of list objects and renders a list of NavItems. It also optionally takes `include`, an array of strings. If this array is passed to this component, only lists with a `key` property that matches an element in the `include` array will be rendered. If this array is not passed into the component, all lists will be rendered. @@ -136,8 +115,8 @@ The ListNavItems component expects `lists` an array of list objects and renders ```typescript type ListNavItemProps = { - list: ListMeta; -}; + list: ListMeta +} ``` The ListNavItem component takes a single `list` object and renders a `NavItem`. This is a thin wrapper around `NavItem`, that automates the association of list properties to NavItem. It also adds a custom `isSelected` expression optimised for keystone lists. @@ -151,10 +130,10 @@ The NavItem component has the following type signature: ```typescript type NavItemProps = { - children: ReactNode; - href: string; - isSelected?: boolean; -}; + children: ReactNode + href: string + isSelected?: boolean +} ``` By default the `isSelected` value if left undefined, will be evaluated by the condition `router.pathname === href`, pass in `isSelected` if you have a custom condition or would like more granular control over the selected state of Navigation items. diff --git a/examples/custom-admin-ui-navigation/admin/components/CustomNavigation.tsx b/examples/custom-admin-ui-navigation/admin/components/CustomNavigation.tsx index 075557fee52..8036e093160 100644 --- a/examples/custom-admin-ui-navigation/admin/components/CustomNavigation.tsx +++ b/examples/custom-admin-ui-navigation/admin/components/CustomNavigation.tsx @@ -1,15 +1,33 @@ import React from 'react' -import { ListNavItems, NavigationContainer, NavItem } from '@keystone-6/core/admin-ui/components' - +import { Divider } from '@keystar/ui/layout' +import { + getHrefFromList, + DeveloperResourcesMenu, + NavContainer, + NavFooter, + NavList, + NavItem, +} from '@keystone-6/core/admin-ui/components' import type { NavigationProps } from '@keystone-6/core/admin-ui/components' -export function CustomNavigation ({ lists, authenticatedItem }: NavigationProps) { +export function CustomNavigation ({ lists }: NavigationProps) { return ( - - Dashboard - - Keystone Docs - + + + Dashboard + Keystone Docs + + {lists.map((list) => ( + + {list.label} + + ))} + + + + + + ) } diff --git a/examples/custom-admin-ui-navigation/package.json b/examples/custom-admin-ui-navigation/package.json index fc414bc6406..88527b76f4b 100644 --- a/examples/custom-admin-ui-navigation/package.json +++ b/examples/custom-admin-ui-navigation/package.json @@ -10,6 +10,7 @@ "postinstall": "keystone postinstall" }, "dependencies": { + "@keystar/ui": "^0.7.13", "@keystone-6/core": "^6.3.1", "@prisma/client": "5.19.0", "react": "^18.3.1" diff --git a/examples/custom-admin-ui-navigation/schema.graphql b/examples/custom-admin-ui-navigation/schema.graphql index 57b33b45d9b..490492d80e3 100644 --- a/examples/custom-admin-ui-navigation/schema.graphql +++ b/examples/custom-admin-ui-navigation/schema.graphql @@ -241,24 +241,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/custom-admin-ui-pages/admin/components/CustomNavigation.tsx b/examples/custom-admin-ui-pages/admin/components/CustomNavigation.tsx index 24d18a89ab7..42ce6904baf 100644 --- a/examples/custom-admin-ui-pages/admin/components/CustomNavigation.tsx +++ b/examples/custom-admin-ui-pages/admin/components/CustomNavigation.tsx @@ -1,13 +1,33 @@ import React from 'react' -import { NavItem, ListNavItems, NavigationContainer } from '@keystone-6/core/admin-ui/components' + +import { Divider } from '@keystar/ui/layout' +import { + getHrefFromList, + DeveloperResourcesMenu, + NavContainer, + NavFooter, + NavList, + NavItem, +} from '@keystone-6/core/admin-ui/components' import type { NavigationProps } from '@keystone-6/core/admin-ui/components' -export function CustomNavigation ({ lists, authenticatedItem }: NavigationProps) { +export function CustomNavigation ({ lists }: NavigationProps) { return ( - - Dashboard - - Custom Page - + + + Dashboard + Custom Page + + {lists.map((list) => ( + + {list.label} + + ))} + + + + + + ) } diff --git a/examples/custom-admin-ui-pages/package.json b/examples/custom-admin-ui-pages/package.json index 47998487fcc..0ef454dec6e 100644 --- a/examples/custom-admin-ui-pages/package.json +++ b/examples/custom-admin-ui-pages/package.json @@ -10,6 +10,7 @@ "postinstall": "keystone postinstall" }, "dependencies": { + "@keystar/ui": "^0.7.13", "@keystone-6/core": "^6.3.1", "@keystone-ui/core": "^5.0.2", "@prisma/client": "5.19.0", diff --git a/examples/custom-admin-ui-pages/schema.graphql b/examples/custom-admin-ui-pages/schema.graphql index 57b33b45d9b..490492d80e3 100644 --- a/examples/custom-admin-ui-pages/schema.graphql +++ b/examples/custom-admin-ui-pages/schema.graphql @@ -241,24 +241,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/custom-esbuild/schema.graphql b/examples/custom-esbuild/schema.graphql index f8b64d9787b..9a5afaa354a 100644 --- a/examples/custom-esbuild/schema.graphql +++ b/examples/custom-esbuild/schema.graphql @@ -118,24 +118,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/custom-field-view/schema.graphql b/examples/custom-field-view/schema.graphql index e9a8913365e..7bbd3b7e18a 100644 --- a/examples/custom-field-view/schema.graphql +++ b/examples/custom-field-view/schema.graphql @@ -244,24 +244,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/custom-field/1-text-field/index.ts b/examples/custom-field/1-text-field/index.ts index ca673877e7f..208343b72d3 100644 --- a/examples/custom-field/1-text-field/index.ts +++ b/examples/custom-field/1-text-field/index.ts @@ -1,8 +1,8 @@ import { type BaseListTypeInfo, - fieldType, - type FieldTypeFunc, type CommonFieldConfig, + type FieldTypeFunc, + fieldType, orderDirectionEnum, } from '@keystone-6/core/types' import { graphql } from '@keystone-6/core' diff --git a/examples/custom-field/1-text-field/views.tsx b/examples/custom-field/1-text-field/views.tsx index 3d0c9a60322..357eb6e71f8 100644 --- a/examples/custom-field/1-text-field/views.tsx +++ b/examples/custom-field/1-text-field/views.tsx @@ -1,9 +1,8 @@ import React from 'react' -import { FieldContainer, FieldDescription, FieldLabel, TextInput } from '@keystone-ui/fields' -import { CellLink, CellContainer } from '@keystone-6/core/admin-ui/components' +import { Cell as CellContainer } from '@keystar/ui/table' +import { TextField } from '@keystar/ui/text-field' import { - type CardValueComponent, type CellComponent, type FieldController, type FieldControllerConfig, @@ -14,42 +13,25 @@ export function Field ({ field, value, onChange, autoFocus }: FieldProps - {field.label} - {field.description} -
- { - onChange?.(event.target.value) - }} - disabled={disabled} - value={value || ''} - autoFocus={autoFocus} - /> -
- + onChange?.(x === '' ? null : x)} + value={value ?? ''} + /> ) } -export const Cell: CellComponent = ({ item, field, linkTo }) => { +export const Cell: CellComponent = ({ item, field }) => { const value = item[field.path] + '' - return linkTo ? {value} : {value} + return {value} } -Cell.supportsLinkTo = true -export const CardValue: CardValueComponent = ({ item, field }) => { - return ( - - {field.label} - {item[field.path]} - - ) -} - -export const controller = ( +export function controller ( config: FieldControllerConfig<{}> -): FieldController => { +): FieldController { return { path: config.path, label: config.label, diff --git a/examples/custom-field/2-stars-field/index.ts b/examples/custom-field/2-stars-field/index.ts index abf8b22df3f..a81ad61e5d1 100644 --- a/examples/custom-field/2-stars-field/index.ts +++ b/examples/custom-field/2-stars-field/index.ts @@ -1,8 +1,8 @@ import { type BaseListTypeInfo, - fieldType, - type FieldTypeFunc, type CommonFieldConfig, + type FieldTypeFunc, + fieldType, orderDirectionEnum, } from '@keystone-6/core/types' import { graphql } from '@keystone-6/core' diff --git a/examples/custom-field/2-stars-field/views.tsx b/examples/custom-field/2-stars-field/views.tsx index a93dac82d5b..e68e5a0e522 100644 --- a/examples/custom-field/2-stars-field/views.tsx +++ b/examples/custom-field/2-stars-field/views.tsx @@ -1,50 +1,48 @@ import React from 'react' -import { FieldContainer, FieldDescription, FieldLabel } from '@keystone-ui/fields' -import { CellLink, CellContainer } from '@keystone-6/core/admin-ui/components' +import { Cell as CellContainer } from '@keystar/ui/table' +import { TextField } from '@keystar/ui/text-field' import { - type CardValueComponent, type CellComponent, type FieldController, type FieldControllerConfig, type FieldProps, } from '@keystone-6/core/types' -import { StarsInput } from './stars-input' +// import { StarsInput } from './stars-input' // this is the component shown in the create modal and item page -export const Field = ({ field, value, onChange, autoFocus }: FieldProps) => ( - - {field.label} - {field.description} - - -) +export function Field ({ field, value, onChange, autoFocus }: FieldProps) { + const disabled = onChange === undefined -// this is shown on the list view in the table -export const Cell: CellComponent = ({ item, field, linkTo }) => { - let value = item[field.path] + '' - return linkTo ? {value} : {value} -} -// setting supportsLinksTo means the cell component allows containing a link to the item -// for example, text fields support it but relationship fields don't because -// their cell component links to the related item so it can't link to the item that the relationship is on -Cell.supportsLinkTo = true - -// this is shown on the item page in relationship fields with `displayMode: 'cards'` -export const CardValue: CardValueComponent = ({ item, field }) => { + // TODO: +// return +// {field.label} +// {field.description} +// +// return ( - - {field.label} - {item[field.path]} - + onChange?.(x === '' ? null : x)} + value={value ?? ''} + /> ) } -export const controller = ( +// this is shown on the list view in the table +export const Cell: CellComponent = ({ item, field }) => { + const value = item[field.path] + '' + return {value} +} + +export function controller ( // the type parameter here needs to align with what is returned from `getAdminMeta` // in the server-side portion of the field type config: FieldControllerConfig<{ maxStars: number }> -): FieldController & { maxStars: number } => { +): FieldController & { maxStars: number } { return { maxStars: config.fieldMeta.maxStars, path: config.path, diff --git a/examples/custom-field/3-pair-field-json/index.ts b/examples/custom-field/3-pair-field-json/index.ts index 6ed0c0b35f5..97d62b11540 100644 --- a/examples/custom-field/3-pair-field-json/index.ts +++ b/examples/custom-field/3-pair-field-json/index.ts @@ -1,8 +1,8 @@ import { type BaseListTypeInfo, - fieldType, - type FieldTypeFunc, type CommonFieldConfig, + type FieldTypeFunc, + fieldType, } from '@keystone-6/core/types' import { graphql } from '@keystone-6/core' @@ -54,12 +54,8 @@ export function pair ( } function resolveWhere (value: null | { equals: PairInput | null | undefined }) { - if (value === null) { - throw new Error('PairFilter cannot be null') - } - if (value.equals === undefined) { - return {} - } + if (value === null) throw new Error('PairFilter cannot be null') + if (value.equals === undefined) return {} const json = resolveInput(value.equals) return { equals: json } } diff --git a/examples/custom-field/3-pair-field-json/views.tsx b/examples/custom-field/3-pair-field-json/views.tsx index 4f464a2fe81..4b1e164c48a 100644 --- a/examples/custom-field/3-pair-field-json/views.tsx +++ b/examples/custom-field/3-pair-field-json/views.tsx @@ -1,9 +1,8 @@ import React from 'react' -import { FieldContainer, FieldDescription, FieldLabel, TextInput } from '@keystone-ui/fields' -import { CellLink, CellContainer } from '@keystone-6/core/admin-ui/components' +import { Cell as CellContainer } from '@keystar/ui/table' +import { TextField } from '@keystar/ui/text-field' import { - type CardValueComponent, type CellComponent, type FieldController, type FieldControllerConfig, @@ -16,57 +15,29 @@ export function Field ({ field, value, onChange, autoFocus }: FieldProps - - {field.label} (Left) - - {field.description} - -
- { - onChange?.({ left: event.target.value, right }) - }} - disabled={disabled} - value={left || ''} - autoFocus={autoFocus} - /> -
-
- - {field.label} (Right) - - {field.description} - -
- { - onChange?.({ left, right: event.target.value }) - }} - disabled={disabled} - value={right || ''} - autoFocus={autoFocus} - /> -
-
+ onChange?.({ left: x === '' ? null : x, right })} + value={left ?? ''} + /> + onChange?.({ right: x === '' ? null : x, left })} + value={right ?? ''} + /> ) } -export const Cell: CellComponent = ({ item, field, linkTo }) => { - let value = item[field.path] + '' - return linkTo ? {value} : {value} -} -Cell.supportsLinkTo = true - -export const CardValue: CardValueComponent = ({ item, field }) => { - return ( - - {field.label} - {item[field.path]} - - ) +export const Cell: CellComponent = ({ item, field }) => { + const value = item[field.path] + '' + return {value} } export const controller = ( diff --git a/examples/custom-field/3-pair-field-nested/index.ts b/examples/custom-field/3-pair-field-nested/index.ts index c5fb98d918a..249ee89c436 100644 --- a/examples/custom-field/3-pair-field-nested/index.ts +++ b/examples/custom-field/3-pair-field-nested/index.ts @@ -1,8 +1,8 @@ import { type BaseListTypeInfo, - fieldType, - type FieldTypeFunc, type CommonFieldConfig, + type FieldTypeFunc, + fieldType, } from '@keystone-6/core/types' import { graphql } from '@keystone-6/core' @@ -53,12 +53,8 @@ export function pair ( } function resolveWhere (value: null | { equals: PairInput | null | undefined }) { - if (value === null) { - throw new Error('PairFilter cannot be null') - } - if (value.equals === undefined) { - return {} - } + if (value === null) throw new Error('PairFilter cannot be null') + if (value.equals === undefined) return {} const { left, right } = resolveInput(value.equals) return { left: { equals: left }, diff --git a/examples/custom-field/3-pair-field-nested/views.tsx b/examples/custom-field/3-pair-field-nested/views.tsx index 4f464a2fe81..f80ce7e972a 100644 --- a/examples/custom-field/3-pair-field-nested/views.tsx +++ b/examples/custom-field/3-pair-field-nested/views.tsx @@ -1,9 +1,8 @@ import React from 'react' -import { FieldContainer, FieldDescription, FieldLabel, TextInput } from '@keystone-ui/fields' -import { CellLink, CellContainer } from '@keystone-6/core/admin-ui/components' +import { Cell as CellContainer } from '@keystar/ui/table' +import { TextField } from '@keystar/ui/text-field' import { - type CardValueComponent, type CellComponent, type FieldController, type FieldControllerConfig, @@ -16,60 +15,32 @@ export function Field ({ field, value, onChange, autoFocus }: FieldProps - - {field.label} (Left) - - {field.description} - -
- { - onChange?.({ left: event.target.value, right }) - }} - disabled={disabled} - value={left || ''} - autoFocus={autoFocus} - /> -
-
- - {field.label} (Right) - - {field.description} - -
- { - onChange?.({ left, right: event.target.value }) - }} - disabled={disabled} - value={right || ''} - autoFocus={autoFocus} - /> -
-
+ onChange?.({ left: x === '' ? null : x, right })} + value={left ?? ''} + /> + onChange?.({ right: x === '' ? null : x, left })} + value={right ?? ''} + /> ) } -export const Cell: CellComponent = ({ item, field, linkTo }) => { - let value = item[field.path] + '' - return linkTo ? {value} : {value} +export const Cell: CellComponent = ({ item, field }) => { + const value = item[field.path] + '' + return {value} } -Cell.supportsLinkTo = true -export const CardValue: CardValueComponent = ({ item, field }) => { - return ( - - {field.label} - {item[field.path]} - - ) -} - -export const controller = ( +export function controller ( config: FieldControllerConfig<{}> ): FieldController< { @@ -77,7 +48,7 @@ export const controller = ( right: string | null } | null, string -> => { +> { return { path: config.path, label: config.label, diff --git a/examples/custom-field/3-pair-field/index.ts b/examples/custom-field/3-pair-field/index.ts index 86a683b757e..3ff8cc8cec2 100644 --- a/examples/custom-field/3-pair-field/index.ts +++ b/examples/custom-field/3-pair-field/index.ts @@ -1,8 +1,8 @@ import { type BaseListTypeInfo, - fieldType, - type FieldTypeFunc, type CommonFieldConfig, + type FieldTypeFunc, + fieldType, } from '@keystone-6/core/types' import { graphql } from '@keystone-6/core' @@ -40,12 +40,8 @@ export function pair ( } function resolveWhere (value: null | { equals: string | null | undefined }) { - if (value === null) { - throw new Error('PairFilter cannot be null') - } - if (value.equals === undefined) { - return {} - } + if (value === null) throw new Error('PairFilter cannot be null') + if (value.equals === undefined) return {} const { left, right } = resolveInput(value.equals) return { left: { equals: left }, diff --git a/examples/custom-field/3-pair-field/views.tsx b/examples/custom-field/3-pair-field/views.tsx index 4ad1ec86f02..357eb6e71f8 100644 --- a/examples/custom-field/3-pair-field/views.tsx +++ b/examples/custom-field/3-pair-field/views.tsx @@ -1,9 +1,8 @@ import React from 'react' -import { FieldContainer, FieldDescription, FieldLabel, TextInput } from '@keystone-ui/fields' -import { CellLink, CellContainer } from '@keystone-6/core/admin-ui/components' +import { Cell as CellContainer } from '@keystar/ui/table' +import { TextField } from '@keystar/ui/text-field' import { - type CardValueComponent, type CellComponent, type FieldController, type FieldControllerConfig, @@ -14,42 +13,25 @@ export function Field ({ field, value, onChange, autoFocus }: FieldProps - {field.label} - {field.description} -
- { - onChange?.(event.target.value) - }} - disabled={disabled} - value={value || ''} - autoFocus={autoFocus} - /> -
- + onChange?.(x === '' ? null : x)} + value={value ?? ''} + /> ) } -export const Cell: CellComponent = ({ item, field, linkTo }) => { - let value = item[field.path] + '' - return linkTo ? {value} : {value} +export const Cell: CellComponent = ({ item, field }) => { + const value = item[field.path] + '' + return {value} } -Cell.supportsLinkTo = true -export const CardValue: CardValueComponent = ({ item, field }) => { - return ( - - {field.label} - {item[field.path]} - - ) -} - -export const controller = ( +export function controller ( config: FieldControllerConfig<{}> -): FieldController => { +): FieldController { return { path: config.path, label: config.label, diff --git a/examples/custom-field/4-conditional-field/index.tsx b/examples/custom-field/4-conditional-field/index.tsx index 07318c19e99..336c08cc01e 100644 --- a/examples/custom-field/4-conditional-field/index.tsx +++ b/examples/custom-field/4-conditional-field/index.tsx @@ -1,8 +1,8 @@ import { type BaseListTypeInfo, - fieldType, - type FieldTypeFunc, type CommonFieldConfig, + type FieldTypeFunc, + fieldType, orderDirectionEnum, } from '@keystone-6/core/types' import { graphql } from '@keystone-6/core' diff --git a/examples/custom-field/4-conditional-field/views.tsx b/examples/custom-field/4-conditional-field/views.tsx index 0c50f1b5f93..0745d73435f 100644 --- a/examples/custom-field/4-conditional-field/views.tsx +++ b/examples/custom-field/4-conditional-field/views.tsx @@ -1,9 +1,8 @@ import React, { useEffect } from 'react' -import { FieldContainer, FieldDescription, FieldLabel, TextInput } from '@keystone-ui/fields' -import { CellLink, CellContainer } from '@keystone-6/core/admin-ui/components' +import { Cell as CellContainer } from '@keystar/ui/table' +import { TextField } from '@keystar/ui/text-field' import { - type CardValueComponent, type CellComponent, type FieldController, type FieldControllerConfig, @@ -28,40 +27,23 @@ export function Field ({ const disabled = onChange === undefined return ( - - {field.label} - {field.description} -
- { - onChange?.(event.target.value) - }} - disabled={disabled} - value={value || ''} - autoFocus={autoFocus} - /> -
-
+ onChange?.(x === '' ? null : x)} + value={value ?? ''} + /> ) } -export const Cell: CellComponent = ({ item, field, linkTo }) => { +export const Cell: CellComponent = ({ item, field }) => { const value = item[field.path] + '' - return linkTo ? {value} : {value} + return {value} } -Cell.supportsLinkTo = true -export const CardValue: CardValueComponent = ({ item, field }) => { - return ( - - {field.label} - {item[field.path]} - - ) -} - -export const controller = ( +export function controller ( config: FieldControllerConfig<{ dependency: { field: string @@ -73,7 +55,7 @@ export const controller = ( field: string minimumValue: number } -} => { +} { return { path: config.path, label: config.label, diff --git a/examples/custom-field/package.json b/examples/custom-field/package.json index c1e98c18fc9..4200ff10278 100644 --- a/examples/custom-field/package.json +++ b/examples/custom-field/package.json @@ -10,6 +10,7 @@ "postinstall": "keystone postinstall" }, "dependencies": { + "@keystar/ui": "^0.7.13", "@keystone-6/core": "^6.3.1", "@keystone-ui/fields": "^7.2.0", "@prisma/client": "5.19.0" diff --git a/examples/custom-field/schema.graphql b/examples/custom-field/schema.graphql index 775a82359f4..5b8a88e1cdc 100644 --- a/examples/custom-field/schema.graphql +++ b/examples/custom-field/schema.graphql @@ -136,24 +136,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/custom-id/schema.graphql b/examples/custom-id/schema.graphql index cf770e33753..5b7077319c0 100644 --- a/examples/custom-id/schema.graphql +++ b/examples/custom-id/schema.graphql @@ -402,24 +402,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/custom-output-paths/my-graphql.graphql b/examples/custom-output-paths/my-graphql.graphql index fec34de3bfd..4901e57abfd 100644 --- a/examples/custom-output-paths/my-graphql.graphql +++ b/examples/custom-output-paths/my-graphql.graphql @@ -136,24 +136,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/custom-session-invalidation/schema.graphql b/examples/custom-session-invalidation/schema.graphql index 28724cc9f8d..ff38064b80c 100644 --- a/examples/custom-session-invalidation/schema.graphql +++ b/examples/custom-session-invalidation/schema.graphql @@ -101,11 +101,9 @@ type Query { users(where: UserWhereInput! = {}, orderBy: [UserOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: UserWhereUniqueInput): [User!] usersCount(where: UserWhereInput! = {}): Int keystone: KeystoneMeta! - authenticatedItem: AuthenticatedItem + authenticatedItem: User } -union AuthenticatedItem = User - type KeystoneMeta { adminMeta: KeystoneAdminMeta! } @@ -118,24 +116,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/custom-session-jwt/schema.graphql b/examples/custom-session-jwt/schema.graphql index 2296d876ec2..f385129f793 100644 --- a/examples/custom-session-jwt/schema.graphql +++ b/examples/custom-session-jwt/schema.graphql @@ -148,7 +148,6 @@ type Mutation { updateUsers(data: [UserUpdateArgs!]!): [User] deleteUser(where: UserWhereUniqueInput!): User deleteUsers(where: [UserWhereUniqueInput!]!): [User] - endSession: Boolean! } type Query { @@ -173,24 +172,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/custom-session-next-auth/schema.graphql b/examples/custom-session-next-auth/schema.graphql index 33d7b305133..070217b2084 100644 --- a/examples/custom-session-next-auth/schema.graphql +++ b/examples/custom-session-next-auth/schema.graphql @@ -182,7 +182,6 @@ type Mutation { updateAuthors(data: [AuthorUpdateArgs!]!): [Author] deleteAuthor(where: AuthorWhereUniqueInput!): Author deleteAuthors(where: [AuthorWhereUniqueInput!]!): [Author] - endSession: Boolean! } type Query { @@ -207,24 +206,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/custom-session-passport/schema.graphql b/examples/custom-session-passport/schema.graphql index 33d7b305133..070217b2084 100644 --- a/examples/custom-session-passport/schema.graphql +++ b/examples/custom-session-passport/schema.graphql @@ -182,7 +182,6 @@ type Mutation { updateAuthors(data: [AuthorUpdateArgs!]!): [Author] deleteAuthor(where: AuthorWhereUniqueInput!): Author deleteAuthors(where: [AuthorWhereUniqueInput!]!): [Author] - endSession: Boolean! } type Query { @@ -207,24 +206,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/custom-session-redis/schema.graphql b/examples/custom-session-redis/schema.graphql index 0ffdd4bfb46..73e9d9e4486 100644 --- a/examples/custom-session-redis/schema.graphql +++ b/examples/custom-session-redis/schema.graphql @@ -95,11 +95,9 @@ type Query { users(where: UserWhereInput! = {}, orderBy: [UserOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: UserWhereUniqueInput): [User!] usersCount(where: UserWhereInput! = {}): Int keystone: KeystoneMeta! - authenticatedItem: AuthenticatedItem + authenticatedItem: User } -union AuthenticatedItem = User - type KeystoneMeta { adminMeta: KeystoneAdminMeta! } @@ -112,24 +110,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/custom-session/schema.graphql b/examples/custom-session/schema.graphql index 2296d876ec2..f385129f793 100644 --- a/examples/custom-session/schema.graphql +++ b/examples/custom-session/schema.graphql @@ -148,7 +148,6 @@ type Mutation { updateUsers(data: [UserUpdateArgs!]!): [User] deleteUser(where: UserWhereUniqueInput!): User deleteUsers(where: [UserWhereUniqueInput!]!): [User] - endSession: Boolean! } type Query { @@ -173,24 +172,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/default-values/schema.graphql b/examples/default-values/schema.graphql index 0b14abaecb0..7c61cbff9be 100644 --- a/examples/default-values/schema.graphql +++ b/examples/default-values/schema.graphql @@ -260,24 +260,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/document-field-customisation/keystone-server/schema.graphql b/examples/document-field-customisation/keystone-server/schema.graphql index 6a099d9995d..0bf8b8c0197 100644 --- a/examples/document-field-customisation/keystone-server/schema.graphql +++ b/examples/document-field-customisation/keystone-server/schema.graphql @@ -232,24 +232,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/document-field/schema.graphql b/examples/document-field/schema.graphql index dcf50dbe7e3..7911bebfe18 100644 --- a/examples/document-field/schema.graphql +++ b/examples/document-field/schema.graphql @@ -256,24 +256,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/empty-lists/schema.graphql b/examples/empty-lists/schema.graphql index 87cebf6105c..2253c198dce 100644 --- a/examples/empty-lists/schema.graphql +++ b/examples/empty-lists/schema.graphql @@ -381,24 +381,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/extend-express-app/schema.graphql b/examples/extend-express-app/schema.graphql index 190f6739ade..2bcf8117ed2 100644 --- a/examples/extend-express-app/schema.graphql +++ b/examples/extend-express-app/schema.graphql @@ -128,24 +128,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/extend-graphql-schema-graphql-tools/schema.graphql b/examples/extend-graphql-schema-graphql-tools/schema.graphql index eca1913cd52..9a932332e57 100644 --- a/examples/extend-graphql-schema-graphql-tools/schema.graphql +++ b/examples/extend-graphql-schema-graphql-tools/schema.graphql @@ -253,24 +253,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/extend-graphql-schema-graphql-ts/schema.graphql b/examples/extend-graphql-schema-graphql-ts/schema.graphql index 0ba58eb6efe..fc7bbc58641 100644 --- a/examples/extend-graphql-schema-graphql-ts/schema.graphql +++ b/examples/extend-graphql-schema-graphql-ts/schema.graphql @@ -251,24 +251,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/extend-graphql-schema-nexus/nexus-types.ts b/examples/extend-graphql-schema-nexus/nexus-types.ts index ed5c71d7c23..a94250fff9c 100644 --- a/examples/extend-graphql-schema-nexus/nexus-types.ts +++ b/examples/extend-graphql-schema-nexus/nexus-types.ts @@ -15,151 +15,146 @@ declare global { export interface NexusGenInputs { AuthorCreateInput: { // input type - email?: string | null // String - name?: string | null // String - posts?: NexusGenInputs['PostRelateToManyForCreateInput'] | null // PostRelateToManyForCreateInput + name?: string | null; // String + posts?: NexusGenInputs['PostRelateToManyForCreateInput'] | null; // PostRelateToManyForCreateInput } AuthorOrderByInput: { // input type - email?: NexusGenEnums['OrderDirection'] | null // OrderDirection - id?: NexusGenEnums['OrderDirection'] | null // OrderDirection - name?: NexusGenEnums['OrderDirection'] | null // OrderDirection + id?: NexusGenEnums['OrderDirection'] | null; // OrderDirection + name?: NexusGenEnums['OrderDirection'] | null; // OrderDirection } AuthorRelateToOneForCreateInput: { // input type - connect?: NexusGenInputs['AuthorWhereUniqueInput'] | null // AuthorWhereUniqueInput - create?: NexusGenInputs['AuthorCreateInput'] | null // AuthorCreateInput + connect?: NexusGenInputs['AuthorWhereUniqueInput'] | null; // AuthorWhereUniqueInput + create?: NexusGenInputs['AuthorCreateInput'] | null; // AuthorCreateInput } AuthorRelateToOneForUpdateInput: { // input type - connect?: NexusGenInputs['AuthorWhereUniqueInput'] | null // AuthorWhereUniqueInput - create?: NexusGenInputs['AuthorCreateInput'] | null // AuthorCreateInput - disconnect?: boolean | null // Boolean + connect?: NexusGenInputs['AuthorWhereUniqueInput'] | null; // AuthorWhereUniqueInput + create?: NexusGenInputs['AuthorCreateInput'] | null; // AuthorCreateInput + disconnect?: boolean | null; // Boolean } AuthorUpdateArgs: { // input type - data: NexusGenInputs['AuthorUpdateInput'] // AuthorUpdateInput! - where: NexusGenInputs['AuthorWhereUniqueInput'] // AuthorWhereUniqueInput! + data: NexusGenInputs['AuthorUpdateInput']; // AuthorUpdateInput! + where: NexusGenInputs['AuthorWhereUniqueInput']; // AuthorWhereUniqueInput! } AuthorUpdateInput: { // input type - email?: string | null // String - name?: string | null // String - posts?: NexusGenInputs['PostRelateToManyForUpdateInput'] | null // PostRelateToManyForUpdateInput + name?: string | null; // String + posts?: NexusGenInputs['PostRelateToManyForUpdateInput'] | null; // PostRelateToManyForUpdateInput } AuthorWhereInput: { // input type - AND?: NexusGenInputs['AuthorWhereInput'][] | null // [AuthorWhereInput!] - NOT?: NexusGenInputs['AuthorWhereInput'][] | null // [AuthorWhereInput!] - OR?: NexusGenInputs['AuthorWhereInput'][] | null // [AuthorWhereInput!] - email?: NexusGenInputs['StringFilter'] | null // StringFilter - id?: NexusGenInputs['IDFilter'] | null // IDFilter - name?: NexusGenInputs['StringFilter'] | null // StringFilter - posts?: NexusGenInputs['PostManyRelationFilter'] | null // PostManyRelationFilter + AND?: NexusGenInputs['AuthorWhereInput'][] | null; // [AuthorWhereInput!] + NOT?: NexusGenInputs['AuthorWhereInput'][] | null; // [AuthorWhereInput!] + OR?: NexusGenInputs['AuthorWhereInput'][] | null; // [AuthorWhereInput!] + id?: NexusGenInputs['IDFilter'] | null; // IDFilter + name?: NexusGenInputs['StringFilter'] | null; // StringFilter + posts?: NexusGenInputs['PostManyRelationFilter'] | null; // PostManyRelationFilter } AuthorWhereUniqueInput: { // input type - email?: string | null // String - id?: string | null // ID + id?: string | null; // ID } DateTimeNullableFilter: { // input type - equals?: NexusGenScalars['DateTime'] | null // DateTime - gt?: NexusGenScalars['DateTime'] | null // DateTime - gte?: NexusGenScalars['DateTime'] | null // DateTime - in?: NexusGenScalars['DateTime'][] | null // [DateTime!] - lt?: NexusGenScalars['DateTime'] | null // DateTime - lte?: NexusGenScalars['DateTime'] | null // DateTime - not?: NexusGenInputs['DateTimeNullableFilter'] | null // DateTimeNullableFilter - notIn?: NexusGenScalars['DateTime'][] | null // [DateTime!] + equals?: NexusGenScalars['DateTime'] | null; // DateTime + gt?: NexusGenScalars['DateTime'] | null; // DateTime + gte?: NexusGenScalars['DateTime'] | null; // DateTime + in?: NexusGenScalars['DateTime'][] | null; // [DateTime!] + lt?: NexusGenScalars['DateTime'] | null; // DateTime + lte?: NexusGenScalars['DateTime'] | null; // DateTime + not?: NexusGenInputs['DateTimeNullableFilter'] | null; // DateTimeNullableFilter + notIn?: NexusGenScalars['DateTime'][] | null; // [DateTime!] } IDFilter: { // input type - equals?: string | null // ID - gt?: string | null // ID - gte?: string | null // ID - in?: string[] | null // [ID!] - lt?: string | null // ID - lte?: string | null // ID - not?: NexusGenInputs['IDFilter'] | null // IDFilter - notIn?: string[] | null // [ID!] + equals?: string | null; // ID + gt?: string | null; // ID + gte?: string | null; // ID + in?: string[] | null; // [ID!] + lt?: string | null; // ID + lte?: string | null; // ID + not?: NexusGenInputs['IDFilter'] | null; // IDFilter + notIn?: string[] | null; // [ID!] } NestedStringFilter: { // input type - contains?: string | null // String - endsWith?: string | null // String - equals?: string | null // String - gt?: string | null // String - gte?: string | null // String - in?: string[] | null // [String!] - lt?: string | null // String - lte?: string | null // String - not?: NexusGenInputs['NestedStringFilter'] | null // NestedStringFilter - notIn?: string[] | null // [String!] - startsWith?: string | null // String + contains?: string | null; // String + endsWith?: string | null; // String + equals?: string | null; // String + gt?: string | null; // String + gte?: string | null; // String + in?: string[] | null; // [String!] + lt?: string | null; // String + lte?: string | null; // String + not?: NexusGenInputs['NestedStringFilter'] | null; // NestedStringFilter + notIn?: string[] | null; // [String!] + startsWith?: string | null; // String } PostCreateInput: { // input type - author?: NexusGenInputs['AuthorRelateToOneForCreateInput'] | null // AuthorRelateToOneForCreateInput - content?: string | null // String - publishDate?: NexusGenScalars['DateTime'] | null // DateTime - status?: NexusGenEnums['PostStatusType'] | null // PostStatusType - title?: string | null // String + author?: NexusGenInputs['AuthorRelateToOneForCreateInput'] | null; // AuthorRelateToOneForCreateInput + content?: string | null; // String + publishDate?: NexusGenScalars['DateTime'] | null; // DateTime + status?: NexusGenEnums['PostStatusType'] | null; // PostStatusType + title?: string | null; // String } PostManyRelationFilter: { // input type - every?: NexusGenInputs['PostWhereInput'] | null // PostWhereInput - none?: NexusGenInputs['PostWhereInput'] | null // PostWhereInput - some?: NexusGenInputs['PostWhereInput'] | null // PostWhereInput + every?: NexusGenInputs['PostWhereInput'] | null; // PostWhereInput + none?: NexusGenInputs['PostWhereInput'] | null; // PostWhereInput + some?: NexusGenInputs['PostWhereInput'] | null; // PostWhereInput } PostOrderByInput: { // input type - content?: NexusGenEnums['OrderDirection'] | null // OrderDirection - id?: NexusGenEnums['OrderDirection'] | null // OrderDirection - publishDate?: NexusGenEnums['OrderDirection'] | null // OrderDirection - status?: NexusGenEnums['OrderDirection'] | null // OrderDirection - title?: NexusGenEnums['OrderDirection'] | null // OrderDirection + content?: NexusGenEnums['OrderDirection'] | null; // OrderDirection + id?: NexusGenEnums['OrderDirection'] | null; // OrderDirection + publishDate?: NexusGenEnums['OrderDirection'] | null; // OrderDirection + status?: NexusGenEnums['OrderDirection'] | null; // OrderDirection + title?: NexusGenEnums['OrderDirection'] | null; // OrderDirection } PostRelateToManyForCreateInput: { // input type - connect?: NexusGenInputs['PostWhereUniqueInput'][] | null // [PostWhereUniqueInput!] - create?: NexusGenInputs['PostCreateInput'][] | null // [PostCreateInput!] + connect?: NexusGenInputs['PostWhereUniqueInput'][] | null; // [PostWhereUniqueInput!] + create?: NexusGenInputs['PostCreateInput'][] | null; // [PostCreateInput!] } PostRelateToManyForUpdateInput: { // input type - connect?: NexusGenInputs['PostWhereUniqueInput'][] | null // [PostWhereUniqueInput!] - create?: NexusGenInputs['PostCreateInput'][] | null // [PostCreateInput!] - disconnect?: NexusGenInputs['PostWhereUniqueInput'][] | null // [PostWhereUniqueInput!] - set?: NexusGenInputs['PostWhereUniqueInput'][] | null // [PostWhereUniqueInput!] + connect?: NexusGenInputs['PostWhereUniqueInput'][] | null; // [PostWhereUniqueInput!] + create?: NexusGenInputs['PostCreateInput'][] | null; // [PostCreateInput!] + disconnect?: NexusGenInputs['PostWhereUniqueInput'][] | null; // [PostWhereUniqueInput!] + set?: NexusGenInputs['PostWhereUniqueInput'][] | null; // [PostWhereUniqueInput!] } PostStatusTypeNullableFilter: { // input type - equals?: NexusGenEnums['PostStatusType'] | null // PostStatusType - in?: NexusGenEnums['PostStatusType'][] | null // [PostStatusType!] - not?: NexusGenInputs['PostStatusTypeNullableFilter'] | null // PostStatusTypeNullableFilter - notIn?: NexusGenEnums['PostStatusType'][] | null // [PostStatusType!] + equals?: NexusGenEnums['PostStatusType'] | null; // PostStatusType + in?: NexusGenEnums['PostStatusType'][] | null; // [PostStatusType!] + not?: NexusGenInputs['PostStatusTypeNullableFilter'] | null; // PostStatusTypeNullableFilter + notIn?: NexusGenEnums['PostStatusType'][] | null; // [PostStatusType!] } PostUpdateArgs: { // input type - data: NexusGenInputs['PostUpdateInput'] // PostUpdateInput! - where: NexusGenInputs['PostWhereUniqueInput'] // PostWhereUniqueInput! + data: NexusGenInputs['PostUpdateInput']; // PostUpdateInput! + where: NexusGenInputs['PostWhereUniqueInput']; // PostWhereUniqueInput! } PostUpdateInput: { // input type - author?: NexusGenInputs['AuthorRelateToOneForUpdateInput'] | null // AuthorRelateToOneForUpdateInput - content?: string | null // String - publishDate?: NexusGenScalars['DateTime'] | null // DateTime - status?: NexusGenEnums['PostStatusType'] | null // PostStatusType - title?: string | null // String + author?: NexusGenInputs['AuthorRelateToOneForUpdateInput'] | null; // AuthorRelateToOneForUpdateInput + content?: string | null; // String + publishDate?: NexusGenScalars['DateTime'] | null; // DateTime + status?: NexusGenEnums['PostStatusType'] | null; // PostStatusType + title?: string | null; // String } PostWhereInput: { // input type - AND?: NexusGenInputs['PostWhereInput'][] | null // [PostWhereInput!] - NOT?: NexusGenInputs['PostWhereInput'][] | null // [PostWhereInput!] - OR?: NexusGenInputs['PostWhereInput'][] | null // [PostWhereInput!] - author?: NexusGenInputs['AuthorWhereInput'] | null // AuthorWhereInput - content?: NexusGenInputs['StringFilter'] | null // StringFilter - id?: NexusGenInputs['IDFilter'] | null // IDFilter - publishDate?: NexusGenInputs['DateTimeNullableFilter'] | null // DateTimeNullableFilter - status?: NexusGenInputs['PostStatusTypeNullableFilter'] | null // PostStatusTypeNullableFilter - title?: NexusGenInputs['StringFilter'] | null // StringFilter + AND?: NexusGenInputs['PostWhereInput'][] | null; // [PostWhereInput!] + NOT?: NexusGenInputs['PostWhereInput'][] | null; // [PostWhereInput!] + OR?: NexusGenInputs['PostWhereInput'][] | null; // [PostWhereInput!] + author?: NexusGenInputs['AuthorWhereInput'] | null; // AuthorWhereInput + content?: NexusGenInputs['StringFilter'] | null; // StringFilter + id?: NexusGenInputs['IDFilter'] | null; // IDFilter + publishDate?: NexusGenInputs['DateTimeNullableFilter'] | null; // DateTimeNullableFilter + status?: NexusGenInputs['PostStatusTypeNullableFilter'] | null; // PostStatusTypeNullableFilter + title?: NexusGenInputs['StringFilter'] | null; // StringFilter } PostWhereUniqueInput: { // input type - id?: string | null // ID + id?: string | null; // ID } StringFilter: { // input type - contains?: string | null // String - endsWith?: string | null // String - equals?: string | null // String - gt?: string | null // String - gte?: string | null // String - in?: string[] | null // [String!] - lt?: string | null // String - lte?: string | null // String - not?: NexusGenInputs['NestedStringFilter'] | null // NestedStringFilter - notIn?: string[] | null // [String!] - startsWith?: string | null // String + contains?: string | null; // String + endsWith?: string | null; // String + equals?: string | null; // String + gt?: string | null; // String + gte?: string | null; // String + in?: string[] | null; // [String!] + lt?: string | null; // String + lte?: string | null; // String + not?: NexusGenInputs['NestedStringFilter'] | null; // NestedStringFilter + notIn?: string[] | null; // [String!] + startsWith?: string | null; // String } } @@ -186,59 +181,83 @@ export interface NexusGenScalars { } export interface NexusGenObjects { - Author: {} + Author: {}; KeystoneAdminMeta: { // root type - lists: NexusGenRootTypes['KeystoneAdminUIListMeta'][] // [KeystoneAdminUIListMeta!]! + lists: NexusGenRootTypes['KeystoneAdminUIListMeta'][]; // [KeystoneAdminUIListMeta!]! } KeystoneAdminUIFieldGroupMeta: { // root type - description?: string | null // String - fields: NexusGenRootTypes['KeystoneAdminUIFieldMeta'][] // [KeystoneAdminUIFieldMeta!]! - label: string // String! + description?: string | null; // String + fields: NexusGenRootTypes['KeystoneAdminUIFieldMeta'][]; // [KeystoneAdminUIFieldMeta!]! + label: string; // String! } KeystoneAdminUIFieldMeta: { // root type - createView: NexusGenRootTypes['KeystoneAdminUIFieldMetaCreateView'] // KeystoneAdminUIFieldMetaCreateView! - customViewsIndex?: number | null // Int - description?: string | null // String - fieldMeta?: NexusGenScalars['JSON'] | null // JSON - isNonNull?: NexusGenEnums['KeystoneAdminUIFieldMetaIsNonNull'][] | null // [KeystoneAdminUIFieldMetaIsNonNull!] - label: string // String! - listView: NexusGenRootTypes['KeystoneAdminUIFieldMetaListView'] // KeystoneAdminUIFieldMetaListView! - path: string // String! - search?: NexusGenEnums['QueryMode'] | null // QueryMode - viewsIndex: number // Int! - } - KeystoneAdminUIFieldMetaCreateView: {} - KeystoneAdminUIFieldMetaItemView: {} - KeystoneAdminUIFieldMetaListView: {} + createView: NexusGenRootTypes['KeystoneAdminUIFieldMetaCreateView']; // KeystoneAdminUIFieldMetaCreateView! + customViewsIndex?: number | null; // Int + description?: string | null; // String + fieldMeta?: NexusGenScalars['JSON'] | null; // JSON + isNonNull?: NexusGenEnums['KeystoneAdminUIFieldMetaIsNonNull'][] | null; // [KeystoneAdminUIFieldMetaIsNonNull!] + label: string; // String! + listView: NexusGenRootTypes['KeystoneAdminUIFieldMetaListView']; // KeystoneAdminUIFieldMetaListView! + path: string; // String! + search?: NexusGenEnums['QueryMode'] | null; // QueryMode + viewsIndex: number; // Int! + } + KeystoneAdminUIFieldMetaCreateView: {}; + KeystoneAdminUIFieldMetaItemView: {}; + KeystoneAdminUIFieldMetaListView: {}; + KeystoneAdminUIGraphQL: { // root type + names: NexusGenRootTypes['KeystoneAdminUIGraphQLNames']; // KeystoneAdminUIGraphQLNames! + } + KeystoneAdminUIGraphQLNames: { // root type + createInputName: string; // String! + createManyMutationName: string; // String! + createMutationName: string; // String! + deleteManyMutationName: string; // String! + deleteMutationName: string; // String! + itemQueryName: string; // String! + listOrderName: string; // String! + listQueryCountName: string; // String! + listQueryName: string; // String! + outputTypeName: string; // String! + relateToManyForCreateInputName: string; // String! + relateToManyForUpdateInputName: string; // String! + relateToOneForCreateInputName: string; // String! + relateToOneForUpdateInputName: string; // String! + updateInputName: string; // String! + updateManyInputName: string; // String! + updateManyMutationName: string; // String! + updateMutationName: string; // String! + whereInputName: string; // String! + whereUniqueInputName: string; // String! + } KeystoneAdminUIListMeta: { // root type - description?: string | null // String - fields: NexusGenRootTypes['KeystoneAdminUIFieldMeta'][] // [KeystoneAdminUIFieldMeta!]! - groups: NexusGenRootTypes['KeystoneAdminUIFieldGroupMeta'][] // [KeystoneAdminUIFieldGroupMeta!]! - initialColumns: string[] // [String!]! - initialSort?: NexusGenRootTypes['KeystoneAdminUISort'] | null // KeystoneAdminUISort - isSingleton: boolean // Boolean! - itemQueryName: string // String! - key: string // String! - label: string // String! - labelField: string // String! - listQueryName: string // String! - pageSize: number // Int! - path: string // String! - plural: string // String! - singular: string // String! + description?: string | null; // String + fields: NexusGenRootTypes['KeystoneAdminUIFieldMeta'][]; // [KeystoneAdminUIFieldMeta!]! + graphql: NexusGenRootTypes['KeystoneAdminUIGraphQL']; // KeystoneAdminUIGraphQL! + groups: NexusGenRootTypes['KeystoneAdminUIFieldGroupMeta'][]; // [KeystoneAdminUIFieldGroupMeta!]! + initialColumns: string[]; // [String!]! + initialSort?: NexusGenRootTypes['KeystoneAdminUISort'] | null; // KeystoneAdminUISort + isSingleton: boolean; // Boolean! + key: string; // String! + label: string; // String! + labelField: string; // String! + pageSize: number; // Int! + path: string; // String! + plural: string; // String! + singular: string; // String! } KeystoneAdminUISort: { // root type - direction: NexusGenEnums['KeystoneAdminUISortDirection'] // KeystoneAdminUISortDirection! - field: string // String! + direction: NexusGenEnums['KeystoneAdminUISortDirection']; // KeystoneAdminUISortDirection! + field: string; // String! } - KeystoneMeta: {} - Mutation: {} + KeystoneMeta: {}; + Mutation: {}; NexusThing: { // root type - id?: number | null // Int - title?: string | null // String + id?: number | null; // Int + title?: string | null; // String } - Post: {} - Query: {} + Post: {}; + Query: {}; } export interface NexusGenInterfaces { @@ -253,115 +272,137 @@ export type NexusGenAllTypes = NexusGenRootTypes & NexusGenScalars & NexusGenEnu export interface NexusGenFieldTypes { Author: { // field return type - email: string | null // String - id: string // ID! - name: string | null // String - posts: NexusGenRootTypes['Post'][] | null // [Post!] - postsCount: number | null // Int + id: string; // ID! + name: string | null; // String + posts: NexusGenRootTypes['Post'][] | null; // [Post!] + postsCount: number | null; // Int } KeystoneAdminMeta: { // field return type - list: NexusGenRootTypes['KeystoneAdminUIListMeta'] | null // KeystoneAdminUIListMeta - lists: NexusGenRootTypes['KeystoneAdminUIListMeta'][] // [KeystoneAdminUIListMeta!]! + list: NexusGenRootTypes['KeystoneAdminUIListMeta'] | null; // KeystoneAdminUIListMeta + lists: NexusGenRootTypes['KeystoneAdminUIListMeta'][]; // [KeystoneAdminUIListMeta!]! } KeystoneAdminUIFieldGroupMeta: { // field return type - description: string | null // String - fields: NexusGenRootTypes['KeystoneAdminUIFieldMeta'][] // [KeystoneAdminUIFieldMeta!]! - label: string // String! + description: string | null; // String + fields: NexusGenRootTypes['KeystoneAdminUIFieldMeta'][]; // [KeystoneAdminUIFieldMeta!]! + label: string; // String! } KeystoneAdminUIFieldMeta: { // field return type - createView: NexusGenRootTypes['KeystoneAdminUIFieldMetaCreateView'] // KeystoneAdminUIFieldMetaCreateView! - customViewsIndex: number | null // Int - description: string | null // String - fieldMeta: NexusGenScalars['JSON'] | null // JSON - isFilterable: boolean // Boolean! - isNonNull: NexusGenEnums['KeystoneAdminUIFieldMetaIsNonNull'][] | null // [KeystoneAdminUIFieldMetaIsNonNull!] - isOrderable: boolean // Boolean! - itemView: NexusGenRootTypes['KeystoneAdminUIFieldMetaItemView'] | null // KeystoneAdminUIFieldMetaItemView - label: string // String! - listView: NexusGenRootTypes['KeystoneAdminUIFieldMetaListView'] // KeystoneAdminUIFieldMetaListView! - path: string // String! - search: NexusGenEnums['QueryMode'] | null // QueryMode - viewsIndex: number // Int! + createView: NexusGenRootTypes['KeystoneAdminUIFieldMetaCreateView']; // KeystoneAdminUIFieldMetaCreateView! + customViewsIndex: number | null; // Int + description: string | null; // String + fieldMeta: NexusGenScalars['JSON'] | null; // JSON + isFilterable: boolean; // Boolean! + isNonNull: NexusGenEnums['KeystoneAdminUIFieldMetaIsNonNull'][] | null; // [KeystoneAdminUIFieldMetaIsNonNull!] + isOrderable: boolean; // Boolean! + itemView: NexusGenRootTypes['KeystoneAdminUIFieldMetaItemView'] | null; // KeystoneAdminUIFieldMetaItemView + label: string; // String! + listView: NexusGenRootTypes['KeystoneAdminUIFieldMetaListView']; // KeystoneAdminUIFieldMetaListView! + path: string; // String! + search: NexusGenEnums['QueryMode'] | null; // QueryMode + viewsIndex: number; // Int! } KeystoneAdminUIFieldMetaCreateView: { // field return type - fieldMode: NexusGenEnums['KeystoneAdminUIFieldMetaCreateViewFieldMode'] // KeystoneAdminUIFieldMetaCreateViewFieldMode! + fieldMode: NexusGenEnums['KeystoneAdminUIFieldMetaCreateViewFieldMode']; // KeystoneAdminUIFieldMetaCreateViewFieldMode! } KeystoneAdminUIFieldMetaItemView: { // field return type - fieldMode: NexusGenEnums['KeystoneAdminUIFieldMetaItemViewFieldMode'] | null // KeystoneAdminUIFieldMetaItemViewFieldMode - fieldPosition: NexusGenEnums['KeystoneAdminUIFieldMetaItemViewFieldPosition'] | null // KeystoneAdminUIFieldMetaItemViewFieldPosition + fieldMode: NexusGenEnums['KeystoneAdminUIFieldMetaItemViewFieldMode'] | null; // KeystoneAdminUIFieldMetaItemViewFieldMode + fieldPosition: NexusGenEnums['KeystoneAdminUIFieldMetaItemViewFieldPosition'] | null; // KeystoneAdminUIFieldMetaItemViewFieldPosition } KeystoneAdminUIFieldMetaListView: { // field return type - fieldMode: NexusGenEnums['KeystoneAdminUIFieldMetaListViewFieldMode'] // KeystoneAdminUIFieldMetaListViewFieldMode! + fieldMode: NexusGenEnums['KeystoneAdminUIFieldMetaListViewFieldMode']; // KeystoneAdminUIFieldMetaListViewFieldMode! + } + KeystoneAdminUIGraphQL: { // field return type + names: NexusGenRootTypes['KeystoneAdminUIGraphQLNames']; // KeystoneAdminUIGraphQLNames! + } + KeystoneAdminUIGraphQLNames: { // field return type + createInputName: string; // String! + createManyMutationName: string; // String! + createMutationName: string; // String! + deleteManyMutationName: string; // String! + deleteMutationName: string; // String! + itemQueryName: string; // String! + listOrderName: string; // String! + listQueryCountName: string; // String! + listQueryName: string; // String! + outputTypeName: string; // String! + relateToManyForCreateInputName: string; // String! + relateToManyForUpdateInputName: string; // String! + relateToOneForCreateInputName: string; // String! + relateToOneForUpdateInputName: string; // String! + updateInputName: string; // String! + updateManyInputName: string; // String! + updateManyMutationName: string; // String! + updateMutationName: string; // String! + whereInputName: string; // String! + whereUniqueInputName: string; // String! } KeystoneAdminUIListMeta: { // field return type - description: string | null // String - fields: NexusGenRootTypes['KeystoneAdminUIFieldMeta'][] // [KeystoneAdminUIFieldMeta!]! - groups: NexusGenRootTypes['KeystoneAdminUIFieldGroupMeta'][] // [KeystoneAdminUIFieldGroupMeta!]! - hideCreate: boolean // Boolean! - hideDelete: boolean // Boolean! - initialColumns: string[] // [String!]! - initialSort: NexusGenRootTypes['KeystoneAdminUISort'] | null // KeystoneAdminUISort - isHidden: boolean // Boolean! - isSingleton: boolean // Boolean! - itemQueryName: string // String! - key: string // String! - label: string // String! - labelField: string // String! - listQueryName: string // String! - pageSize: number // Int! - path: string // String! - plural: string // String! - singular: string // String! + description: string | null; // String + fields: NexusGenRootTypes['KeystoneAdminUIFieldMeta'][]; // [KeystoneAdminUIFieldMeta!]! + graphql: NexusGenRootTypes['KeystoneAdminUIGraphQL']; // KeystoneAdminUIGraphQL! + groups: NexusGenRootTypes['KeystoneAdminUIFieldGroupMeta'][]; // [KeystoneAdminUIFieldGroupMeta!]! + hideCreate: boolean; // Boolean! + hideDelete: boolean; // Boolean! + hideNavigation: boolean; // Boolean! + initialColumns: string[]; // [String!]! + initialSort: NexusGenRootTypes['KeystoneAdminUISort'] | null; // KeystoneAdminUISort + isSingleton: boolean; // Boolean! + key: string; // String! + label: string; // String! + labelField: string; // String! + pageSize: number; // Int! + path: string; // String! + plural: string; // String! + singular: string; // String! } KeystoneAdminUISort: { // field return type - direction: NexusGenEnums['KeystoneAdminUISortDirection'] // KeystoneAdminUISortDirection! - field: string // String! + direction: NexusGenEnums['KeystoneAdminUISortDirection']; // KeystoneAdminUISortDirection! + field: string; // String! } KeystoneMeta: { // field return type - adminMeta: NexusGenRootTypes['KeystoneAdminMeta'] // KeystoneAdminMeta! + adminMeta: NexusGenRootTypes['KeystoneAdminMeta']; // KeystoneAdminMeta! } Mutation: { // field return type - createAuthor: NexusGenRootTypes['Author'] | null // Author - createAuthors: Array | null // [Author] - createPost: NexusGenRootTypes['Post'] | null // Post - createPosts: Array | null // [Post] - deleteAuthor: NexusGenRootTypes['Author'] | null // Author - deleteAuthors: Array | null // [Author] - deletePost: NexusGenRootTypes['Post'] | null // Post - deletePosts: Array | null // [Post] - updateAuthor: NexusGenRootTypes['Author'] | null // Author - updateAuthors: Array | null // [Author] - updatePost: NexusGenRootTypes['Post'] | null // Post - updatePosts: Array | null // [Post] + createAuthor: NexusGenRootTypes['Author'] | null; // Author + createAuthors: Array | null; // [Author] + createPost: NexusGenRootTypes['Post'] | null; // Post + createPosts: Array | null; // [Post] + deleteAuthor: NexusGenRootTypes['Author'] | null; // Author + deleteAuthors: Array | null; // [Author] + deletePost: NexusGenRootTypes['Post'] | null; // Post + deletePosts: Array | null; // [Post] + updateAuthor: NexusGenRootTypes['Author'] | null; // Author + updateAuthors: Array | null; // [Author] + updatePost: NexusGenRootTypes['Post'] | null; // Post + updatePosts: Array | null; // [Post] } NexusThing: { // field return type - id: number | null // Int - title: string | null // String + id: number | null; // Int + title: string | null; // String } Post: { // field return type - author: NexusGenRootTypes['Author'] | null // Author - content: string | null // String - id: string // ID! - publishDate: NexusGenScalars['DateTime'] | null // DateTime - status: NexusGenEnums['PostStatusType'] | null // PostStatusType - title: string | null // String + author: NexusGenRootTypes['Author'] | null; // Author + content: string | null; // String + id: string; // ID! + publishDate: NexusGenScalars['DateTime'] | null; // DateTime + status: NexusGenEnums['PostStatusType'] | null; // PostStatusType + title: string | null; // String } Query: { // field return type - author: NexusGenRootTypes['Author'] | null // Author - authors: NexusGenRootTypes['Author'][] | null // [Author!] - authorsCount: number | null // Int - keystone: NexusGenRootTypes['KeystoneMeta'] // KeystoneMeta! - nexusPosts: Array // [Post]! - post: NexusGenRootTypes['Post'] | null // Post - posts: NexusGenRootTypes['Post'][] | null // [Post!] - postsCount: number | null // Int - things: Array // [NexusThing]! + author: NexusGenRootTypes['Author'] | null; // Author + authors: NexusGenRootTypes['Author'][] | null; // [Author!] + authorsCount: number | null; // Int + keystone: NexusGenRootTypes['KeystoneMeta']; // KeystoneMeta! + nexusPosts: Array; // [Post]! + post: NexusGenRootTypes['Post'] | null; // Post + posts: NexusGenRootTypes['Post'][] | null; // [Post!] + postsCount: number | null; // Int + things: Array; // [NexusThing]! } } export interface NexusGenFieldTypeNames { Author: { // field return type name - email: 'String' id: 'ID' name: 'String' posts: 'Post' @@ -401,21 +442,45 @@ export interface NexusGenFieldTypeNames { KeystoneAdminUIFieldMetaListView: { // field return type name fieldMode: 'KeystoneAdminUIFieldMetaListViewFieldMode' } + KeystoneAdminUIGraphQL: { // field return type name + names: 'KeystoneAdminUIGraphQLNames' + } + KeystoneAdminUIGraphQLNames: { // field return type name + createInputName: 'String' + createManyMutationName: 'String' + createMutationName: 'String' + deleteManyMutationName: 'String' + deleteMutationName: 'String' + itemQueryName: 'String' + listOrderName: 'String' + listQueryCountName: 'String' + listQueryName: 'String' + outputTypeName: 'String' + relateToManyForCreateInputName: 'String' + relateToManyForUpdateInputName: 'String' + relateToOneForCreateInputName: 'String' + relateToOneForUpdateInputName: 'String' + updateInputName: 'String' + updateManyInputName: 'String' + updateManyMutationName: 'String' + updateMutationName: 'String' + whereInputName: 'String' + whereUniqueInputName: 'String' + } KeystoneAdminUIListMeta: { // field return type name description: 'String' fields: 'KeystoneAdminUIFieldMeta' + graphql: 'KeystoneAdminUIGraphQL' groups: 'KeystoneAdminUIFieldGroupMeta' hideCreate: 'Boolean' hideDelete: 'Boolean' + hideNavigation: 'Boolean' initialColumns: 'String' initialSort: 'KeystoneAdminUISort' - isHidden: 'Boolean' isSingleton: 'Boolean' - itemQueryName: 'String' key: 'String' label: 'String' labelField: 'String' - listQueryName: 'String' pageSize: 'Int' path: 'String' plural: 'String' @@ -470,96 +535,96 @@ export interface NexusGenFieldTypeNames { export interface NexusGenArgTypes { Author: { posts: { // args - cursor?: NexusGenInputs['PostWhereUniqueInput'] | null // PostWhereUniqueInput - orderBy: NexusGenInputs['PostOrderByInput'][] // [PostOrderByInput!]! - skip: number // Int! - take?: number | null // Int - where: NexusGenInputs['PostWhereInput'] // PostWhereInput! + cursor?: NexusGenInputs['PostWhereUniqueInput'] | null; // PostWhereUniqueInput + orderBy: NexusGenInputs['PostOrderByInput'][]; // [PostOrderByInput!]! + skip: number; // Int! + take?: number | null; // Int + where: NexusGenInputs['PostWhereInput']; // PostWhereInput! } postsCount: { // args - where: NexusGenInputs['PostWhereInput'] // PostWhereInput! + where: NexusGenInputs['PostWhereInput']; // PostWhereInput! } } KeystoneAdminMeta: { list: { // args - key: string // String! + key: string; // String! } } KeystoneAdminUIFieldMeta: { itemView: { // args - id?: string | null // ID + id?: string | null; // ID } } Mutation: { createAuthor: { // args - data: NexusGenInputs['AuthorCreateInput'] // AuthorCreateInput! + data: NexusGenInputs['AuthorCreateInput']; // AuthorCreateInput! } createAuthors: { // args - data: NexusGenInputs['AuthorCreateInput'][] // [AuthorCreateInput!]! + data: NexusGenInputs['AuthorCreateInput'][]; // [AuthorCreateInput!]! } createPost: { // args - data: NexusGenInputs['PostCreateInput'] // PostCreateInput! + data: NexusGenInputs['PostCreateInput']; // PostCreateInput! } createPosts: { // args - data: NexusGenInputs['PostCreateInput'][] // [PostCreateInput!]! + data: NexusGenInputs['PostCreateInput'][]; // [PostCreateInput!]! } deleteAuthor: { // args - where: NexusGenInputs['AuthorWhereUniqueInput'] // AuthorWhereUniqueInput! + where: NexusGenInputs['AuthorWhereUniqueInput']; // AuthorWhereUniqueInput! } deleteAuthors: { // args - where: NexusGenInputs['AuthorWhereUniqueInput'][] // [AuthorWhereUniqueInput!]! + where: NexusGenInputs['AuthorWhereUniqueInput'][]; // [AuthorWhereUniqueInput!]! } deletePost: { // args - where: NexusGenInputs['PostWhereUniqueInput'] // PostWhereUniqueInput! + where: NexusGenInputs['PostWhereUniqueInput']; // PostWhereUniqueInput! } deletePosts: { // args - where: NexusGenInputs['PostWhereUniqueInput'][] // [PostWhereUniqueInput!]! + where: NexusGenInputs['PostWhereUniqueInput'][]; // [PostWhereUniqueInput!]! } updateAuthor: { // args - data: NexusGenInputs['AuthorUpdateInput'] // AuthorUpdateInput! - where: NexusGenInputs['AuthorWhereUniqueInput'] // AuthorWhereUniqueInput! + data: NexusGenInputs['AuthorUpdateInput']; // AuthorUpdateInput! + where: NexusGenInputs['AuthorWhereUniqueInput']; // AuthorWhereUniqueInput! } updateAuthors: { // args - data: NexusGenInputs['AuthorUpdateArgs'][] // [AuthorUpdateArgs!]! + data: NexusGenInputs['AuthorUpdateArgs'][]; // [AuthorUpdateArgs!]! } updatePost: { // args - data: NexusGenInputs['PostUpdateInput'] // PostUpdateInput! - where: NexusGenInputs['PostWhereUniqueInput'] // PostWhereUniqueInput! + data: NexusGenInputs['PostUpdateInput']; // PostUpdateInput! + where: NexusGenInputs['PostWhereUniqueInput']; // PostWhereUniqueInput! } updatePosts: { // args - data: NexusGenInputs['PostUpdateArgs'][] // [PostUpdateArgs!]! + data: NexusGenInputs['PostUpdateArgs'][]; // [PostUpdateArgs!]! } } Query: { author: { // args - where: NexusGenInputs['AuthorWhereUniqueInput'] // AuthorWhereUniqueInput! + where: NexusGenInputs['AuthorWhereUniqueInput']; // AuthorWhereUniqueInput! } authors: { // args - cursor?: NexusGenInputs['AuthorWhereUniqueInput'] | null // AuthorWhereUniqueInput - orderBy: NexusGenInputs['AuthorOrderByInput'][] // [AuthorOrderByInput!]! - skip: number // Int! - take?: number | null // Int - where: NexusGenInputs['AuthorWhereInput'] // AuthorWhereInput! + cursor?: NexusGenInputs['AuthorWhereUniqueInput'] | null; // AuthorWhereUniqueInput + orderBy: NexusGenInputs['AuthorOrderByInput'][]; // [AuthorOrderByInput!]! + skip: number; // Int! + take?: number | null; // Int + where: NexusGenInputs['AuthorWhereInput']; // AuthorWhereInput! } authorsCount: { // args - where: NexusGenInputs['AuthorWhereInput'] // AuthorWhereInput! + where: NexusGenInputs['AuthorWhereInput']; // AuthorWhereInput! } nexusPosts: { // args - id: string // String! - seconds: number // Int! + id: string; // String! + seconds: number; // Int! } post: { // args - where: NexusGenInputs['PostWhereUniqueInput'] // PostWhereUniqueInput! + where: NexusGenInputs['PostWhereUniqueInput']; // PostWhereUniqueInput! } posts: { // args - cursor?: NexusGenInputs['PostWhereUniqueInput'] | null // PostWhereUniqueInput - orderBy: NexusGenInputs['PostOrderByInput'][] // [PostOrderByInput!]! - skip: number // Int! - take?: number | null // Int - where: NexusGenInputs['PostWhereInput'] // PostWhereInput! + cursor?: NexusGenInputs['PostWhereUniqueInput'] | null; // PostWhereUniqueInput + orderBy: NexusGenInputs['PostOrderByInput'][]; // [PostOrderByInput!]! + skip: number; // Int! + take?: number | null; // Int + where: NexusGenInputs['PostWhereInput']; // PostWhereInput! } postsCount: { // args - where: NexusGenInputs['PostWhereInput'] // PostWhereInput! + where: NexusGenInputs['PostWhereInput']; // PostWhereInput! } } } @@ -570,21 +635,21 @@ export interface NexusGenAbstractTypeMembers { export interface NexusGenTypeInterfaces { } -export type NexusGenObjectNames = keyof NexusGenObjects +export type NexusGenObjectNames = keyof NexusGenObjects; -export type NexusGenInputNames = keyof NexusGenInputs +export type NexusGenInputNames = keyof NexusGenInputs; -export type NexusGenEnumNames = keyof NexusGenEnums +export type NexusGenEnumNames = keyof NexusGenEnums; -export type NexusGenInterfaceNames = never +export type NexusGenInterfaceNames = never; -export type NexusGenScalarNames = keyof NexusGenScalars +export type NexusGenScalarNames = keyof NexusGenScalars; -export type NexusGenUnionNames = never +export type NexusGenUnionNames = never; -export type NexusGenObjectsUsingAbstractStrategyIsTypeOf = never +export type NexusGenObjectsUsingAbstractStrategyIsTypeOf = never; -export type NexusGenAbstractsUsingStrategyResolveType = never +export type NexusGenAbstractsUsingStrategyResolveType = never; export type NexusGenFeaturesConfig = { abstractTypeStrategies: { @@ -595,29 +660,29 @@ export type NexusGenFeaturesConfig = { } export interface NexusGenTypes { - context: Context - inputTypes: NexusGenInputs - rootTypes: NexusGenRootTypes - inputTypeShapes: NexusGenInputs & NexusGenEnums & NexusGenScalars - argTypes: NexusGenArgTypes - fieldTypes: NexusGenFieldTypes - fieldTypeNames: NexusGenFieldTypeNames - allTypes: NexusGenAllTypes - typeInterfaces: NexusGenTypeInterfaces - objectNames: NexusGenObjectNames - inputNames: NexusGenInputNames - enumNames: NexusGenEnumNames - interfaceNames: NexusGenInterfaceNames - scalarNames: NexusGenScalarNames - unionNames: NexusGenUnionNames - allInputTypes: NexusGenTypes['inputNames'] | NexusGenTypes['enumNames'] | NexusGenTypes['scalarNames'] - allOutputTypes: NexusGenTypes['objectNames'] | NexusGenTypes['enumNames'] | NexusGenTypes['unionNames'] | NexusGenTypes['interfaceNames'] | NexusGenTypes['scalarNames'] + context: Context; + inputTypes: NexusGenInputs; + rootTypes: NexusGenRootTypes; + inputTypeShapes: NexusGenInputs & NexusGenEnums & NexusGenScalars; + argTypes: NexusGenArgTypes; + fieldTypes: NexusGenFieldTypes; + fieldTypeNames: NexusGenFieldTypeNames; + allTypes: NexusGenAllTypes; + typeInterfaces: NexusGenTypeInterfaces; + objectNames: NexusGenObjectNames; + inputNames: NexusGenInputNames; + enumNames: NexusGenEnumNames; + interfaceNames: NexusGenInterfaceNames; + scalarNames: NexusGenScalarNames; + unionNames: NexusGenUnionNames; + allInputTypes: NexusGenTypes['inputNames'] | NexusGenTypes['enumNames'] | NexusGenTypes['scalarNames']; + allOutputTypes: NexusGenTypes['objectNames'] | NexusGenTypes['enumNames'] | NexusGenTypes['unionNames'] | NexusGenTypes['interfaceNames'] | NexusGenTypes['scalarNames']; allNamedTypes: NexusGenTypes['allInputTypes'] | NexusGenTypes['allOutputTypes'] - abstractTypes: NexusGenTypes['interfaceNames'] | NexusGenTypes['unionNames'] - abstractTypeMembers: NexusGenAbstractTypeMembers - objectsUsingAbstractStrategyIsTypeOf: NexusGenObjectsUsingAbstractStrategyIsTypeOf - abstractsUsingStrategyResolveType: NexusGenAbstractsUsingStrategyResolveType - features: NexusGenFeaturesConfig + abstractTypes: NexusGenTypes['interfaceNames'] | NexusGenTypes['unionNames']; + abstractTypeMembers: NexusGenAbstractTypeMembers; + objectsUsingAbstractStrategyIsTypeOf: NexusGenObjectsUsingAbstractStrategyIsTypeOf; + abstractsUsingStrategyResolveType: NexusGenAbstractsUsingStrategyResolveType; + features: NexusGenFeaturesConfig; } @@ -634,4 +699,4 @@ declare global { } interface NexusGenPluginArgConfig { } -} +} \ No newline at end of file diff --git a/examples/extend-graphql-schema-nexus/schema.graphql b/examples/extend-graphql-schema-nexus/schema.graphql index fbd1b21dab6..6f55540d142 100644 --- a/examples/extend-graphql-schema-nexus/schema.graphql +++ b/examples/extend-graphql-schema-nexus/schema.graphql @@ -237,24 +237,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/extend-graphql-subscriptions/admin/components/CustomNavigation.tsx b/examples/extend-graphql-subscriptions/admin/components/CustomNavigation.tsx index 937432d09d1..04674e1ccf4 100644 --- a/examples/extend-graphql-subscriptions/admin/components/CustomNavigation.tsx +++ b/examples/extend-graphql-subscriptions/admin/components/CustomNavigation.tsx @@ -1,15 +1,33 @@ import React from 'react' -import { ListNavItems, NavigationContainer, NavItem } from '@keystone-6/core/admin-ui/components' - +import { Divider } from '@keystar/ui/layout' +import { + getHrefFromList, + DeveloperResourcesMenu, + NavContainer, + NavFooter, + NavList, + NavItem, +} from '@keystone-6/core/admin-ui/components' import type { NavigationProps } from '@keystone-6/core/admin-ui/components' -export function CustomNavigation ({ lists, authenticatedItem }: NavigationProps) { +export function CustomNavigation ({ lists }: NavigationProps) { return ( - - Dashboard - - Subscriptions Page - + + + Dashboard + Subscriptions Page + + {lists.map((list) => ( + + {list.label} + + ))} + + + + + + ) } diff --git a/examples/extend-graphql-subscriptions/package.json b/examples/extend-graphql-subscriptions/package.json index 12f4a930d3e..6b66f65c1f8 100644 --- a/examples/extend-graphql-subscriptions/package.json +++ b/examples/extend-graphql-subscriptions/package.json @@ -13,6 +13,7 @@ "@apollo/client": "^3.7.0", "@emotion/css": "^11.7.1", "@graphql-tools/schema": "^9.0.0", + "@keystar/ui": "^0.7.13", "@keystone-6/core": "^6.3.1", "@keystone-ui/button": "^7.0.2", "@keystone-ui/core": "^5.0.2", diff --git a/examples/extend-graphql-subscriptions/schema.graphql b/examples/extend-graphql-subscriptions/schema.graphql index f211869315b..fd6c2b570b4 100644 --- a/examples/extend-graphql-subscriptions/schema.graphql +++ b/examples/extend-graphql-subscriptions/schema.graphql @@ -244,24 +244,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/extend-prisma-schema/schema.graphql b/examples/extend-prisma-schema/schema.graphql index 87484264c7a..c50cb03f384 100644 --- a/examples/extend-prisma-schema/schema.graphql +++ b/examples/extend-prisma-schema/schema.graphql @@ -264,24 +264,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/field-groups/schema.graphql b/examples/field-groups/schema.graphql index 6bfbac8b383..25fdd678314 100644 --- a/examples/field-groups/schema.graphql +++ b/examples/field-groups/schema.graphql @@ -137,24 +137,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/framework-astro/package.json b/examples/framework-astro/package.json index 28a008f390d..913d4d0dce1 100644 --- a/examples/framework-astro/package.json +++ b/examples/framework-astro/package.json @@ -6,7 +6,9 @@ "scripts": { "astro": "astro", "build": "astro build", + "build:keystone": "keystone build", "dev": "astro dev", + "dev:keystone": "keystone dev", "postinstall": "keystone postinstall", "preview": "astro preview", "start": "astro dev" diff --git a/examples/framework-astro/schema.graphql b/examples/framework-astro/schema.graphql index ae21a418fb6..6baecb7210b 100644 --- a/examples/framework-astro/schema.graphql +++ b/examples/framework-astro/schema.graphql @@ -118,24 +118,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/framework-nextjs-app-directory/schema.graphql b/examples/framework-nextjs-app-directory/schema.graphql index e0a6a85eea9..40b31970f24 100644 --- a/examples/framework-nextjs-app-directory/schema.graphql +++ b/examples/framework-nextjs-app-directory/schema.graphql @@ -138,24 +138,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/framework-nextjs-pages-directory/schema.graphql b/examples/framework-nextjs-pages-directory/schema.graphql index 84691091ccc..e99121b0e4e 100644 --- a/examples/framework-nextjs-pages-directory/schema.graphql +++ b/examples/framework-nextjs-pages-directory/schema.graphql @@ -136,24 +136,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/framework-nextjs-two-servers/keystone-server/schema.graphql b/examples/framework-nextjs-two-servers/keystone-server/schema.graphql index 6a099d9995d..0bf8b8c0197 100644 --- a/examples/framework-nextjs-two-servers/keystone-server/schema.graphql +++ b/examples/framework-nextjs-two-servers/keystone-server/schema.graphql @@ -232,24 +232,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/framework-remix/schema.graphql b/examples/framework-remix/schema.graphql index f8b64d9787b..9a5afaa354a 100644 --- a/examples/framework-remix/schema.graphql +++ b/examples/framework-remix/schema.graphql @@ -118,24 +118,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/graphql-ts-gql/schema.graphql b/examples/graphql-ts-gql/schema.graphql index b505dbc88ea..fdd8c0b7919 100644 --- a/examples/graphql-ts-gql/schema.graphql +++ b/examples/graphql-ts-gql/schema.graphql @@ -252,24 +252,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/hooks/schema.graphql b/examples/hooks/schema.graphql index 2ba8fcbfb52..81af4b17729 100644 --- a/examples/hooks/schema.graphql +++ b/examples/hooks/schema.graphql @@ -157,24 +157,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/limits/schema.graphql b/examples/limits/schema.graphql index 969e54d8046..f1984e58e5f 100644 --- a/examples/limits/schema.graphql +++ b/examples/limits/schema.graphql @@ -113,24 +113,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/number-fields/keystone.ts b/examples/number-fields/keystone.ts new file mode 100644 index 00000000000..a59cdcd5436 --- /dev/null +++ b/examples/number-fields/keystone.ts @@ -0,0 +1,13 @@ +import { config } from '@keystone-6/core' +import { lists } from './schema' + +export default config({ + db: { + provider: 'sqlite', + url: process.env.DATABASE_URL || 'file:./keystone-example.db', + + // WARNING: this is only needed for our monorepo examples, dont do this + prismaClientPath: 'node_modules/myprisma', + }, + lists, +}) diff --git a/examples/number-fields/package.json b/examples/number-fields/package.json new file mode 100644 index 00000000000..1fd48119a27 --- /dev/null +++ b/examples/number-fields/package.json @@ -0,0 +1,20 @@ +{ + "name": "@keystone-6/example-number-fields", + "version": null, + "private": true, + "license": "MIT", + "scripts": { + "dev": "keystone dev", + "start": "keystone start", + "build": "keystone build", + "postinstall": "keystone postinstall" + }, + "dependencies": { + "@keystone-6/core": "^6.3.1", + "@prisma/client": "5.19.0" + }, + "devDependencies": { + "prisma": "5.19.0", + "typescript": "^5.5.0" + } +} diff --git a/examples/number-fields/sandbox.config.json b/examples/number-fields/sandbox.config.json new file mode 100644 index 00000000000..c5d3215212f --- /dev/null +++ b/examples/number-fields/sandbox.config.json @@ -0,0 +1,7 @@ +{ + "template": "node", + "container": { + "startScript": "keystone dev", + "node": "20" + } +} diff --git a/examples/number-fields/schema.graphql b/examples/number-fields/schema.graphql new file mode 100644 index 00000000000..2c6b253821e --- /dev/null +++ b/examples/number-fields/schema.graphql @@ -0,0 +1,269 @@ +# This file is automatically generated by Keystone, do not modify it manually. +# Modify your Keystone config when you want to change this. + +type Example { + id: ID! + bigInt: BigInt + bigIntDefaulted: BigInt + float: Float + floatDefaulted: Float + integer: Int + integerDefaulted: Int + maximum: Int +} + +scalar BigInt + +input ExampleWhereUniqueInput { + id: ID +} + +input ExampleWhereInput { + AND: [ExampleWhereInput!] + OR: [ExampleWhereInput!] + NOT: [ExampleWhereInput!] + id: IDFilter + bigInt: BigIntNullableFilter + bigIntDefaulted: BigIntNullableFilter + float: FloatNullableFilter + floatDefaulted: FloatNullableFilter + integer: IntNullableFilter + integerDefaulted: IntNullableFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input BigIntNullableFilter { + equals: BigInt + in: [BigInt!] + notIn: [BigInt!] + lt: BigInt + lte: BigInt + gt: BigInt + gte: BigInt + not: BigIntNullableFilter +} + +input FloatNullableFilter { + equals: Float + in: [Float!] + notIn: [Float!] + lt: Float + lte: Float + gt: Float + gte: Float + not: FloatNullableFilter +} + +input IntNullableFilter { + equals: Int + in: [Int!] + notIn: [Int!] + lt: Int + lte: Int + gt: Int + gte: Int + not: IntNullableFilter +} + +input ExampleOrderByInput { + id: OrderDirection + bigInt: OrderDirection + bigIntDefaulted: OrderDirection + float: OrderDirection + floatDefaulted: OrderDirection + integer: OrderDirection + integerDefaulted: OrderDirection +} + +enum OrderDirection { + asc + desc +} + +input ExampleUpdateInput { + bigInt: BigInt + bigIntDefaulted: BigInt + float: Float + floatDefaulted: Float + integer: Int + integerDefaulted: Int +} + +input ExampleUpdateArgs { + where: ExampleWhereUniqueInput! + data: ExampleUpdateInput! +} + +input ExampleCreateInput { + bigInt: BigInt + bigIntDefaulted: BigInt + float: Float + floatDefaulted: Float + integer: Int + integerDefaulted: Int +} + +""" +The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). +""" +scalar JSON @specifiedBy(url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf") + +type Mutation { + createExample(data: ExampleCreateInput!): Example + createExamples(data: [ExampleCreateInput!]!): [Example] + updateExample(where: ExampleWhereUniqueInput!, data: ExampleUpdateInput!): Example + updateExamples(data: [ExampleUpdateArgs!]!): [Example] + deleteExample(where: ExampleWhereUniqueInput!): Example + deleteExamples(where: [ExampleWhereUniqueInput!]!): [Example] +} + +type Query { + example(where: ExampleWhereUniqueInput!): Example + examples(where: ExampleWhereInput! = {}, orderBy: [ExampleOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: ExampleWhereUniqueInput): [Example!] + examplesCount(where: ExampleWhereInput! = {}): Int + keystone: KeystoneMeta! +} + +type KeystoneMeta { + adminMeta: KeystoneAdminMeta! +} + +type KeystoneAdminMeta { + lists: [KeystoneAdminUIListMeta!]! + list(key: String!): KeystoneAdminUIListMeta +} + +type KeystoneAdminUIListMeta { + key: String! + path: String! + description: String + label: String! + labelField: String! + singular: String! + plural: String! + fields: [KeystoneAdminUIFieldMeta!]! + groups: [KeystoneAdminUIFieldGroupMeta!]! + graphql: KeystoneAdminUIGraphQL! + pageSize: Int! + initialColumns: [String!]! + initialSearchFields: [String!]! + initialSort: KeystoneAdminUISort + isSingleton: Boolean! + hideNavigation: Boolean! + hideCreate: Boolean! + hideDelete: Boolean! +} + +type KeystoneAdminUIFieldMeta { + path: String! + label: String! + description: String + isOrderable: Boolean! + isFilterable: Boolean! + isNonNull: [KeystoneAdminUIFieldMetaIsNonNull!] + fieldMeta: JSON + viewsIndex: Int! + customViewsIndex: Int + createView: KeystoneAdminUIFieldMetaCreateView! + listView: KeystoneAdminUIFieldMetaListView! + itemView(id: ID): KeystoneAdminUIFieldMetaItemView + search: QueryMode +} + +enum KeystoneAdminUIFieldMetaIsNonNull { + read + create + update +} + +type KeystoneAdminUIFieldMetaCreateView { + fieldMode: KeystoneAdminUIFieldMetaCreateViewFieldMode! +} + +enum KeystoneAdminUIFieldMetaCreateViewFieldMode { + edit + hidden +} + +type KeystoneAdminUIFieldMetaListView { + fieldMode: KeystoneAdminUIFieldMetaListViewFieldMode! +} + +enum KeystoneAdminUIFieldMetaListViewFieldMode { + read + hidden +} + +type KeystoneAdminUIFieldMetaItemView { + fieldMode: KeystoneAdminUIFieldMetaItemViewFieldMode + fieldPosition: KeystoneAdminUIFieldMetaItemViewFieldPosition +} + +enum KeystoneAdminUIFieldMetaItemViewFieldMode { + edit + read + hidden +} + +enum KeystoneAdminUIFieldMetaItemViewFieldPosition { + form + sidebar +} + +enum QueryMode { + default + insensitive +} + +type KeystoneAdminUIFieldGroupMeta { + label: String! + description: String + fields: [KeystoneAdminUIFieldMeta!]! +} + +type KeystoneAdminUIGraphQL { + names: KeystoneAdminUIGraphQLNames! +} + +type KeystoneAdminUIGraphQLNames { + outputTypeName: String! + whereInputName: String! + whereUniqueInputName: String! + createInputName: String! + createMutationName: String! + createManyMutationName: String! + relateToOneForCreateInputName: String! + relateToManyForCreateInputName: String! + itemQueryName: String! + listOrderName: String! + listQueryCountName: String! + listQueryName: String! + updateInputName: String! + updateMutationName: String! + updateManyInputName: String! + updateManyMutationName: String! + relateToOneForUpdateInputName: String! + relateToManyForUpdateInputName: String! + deleteMutationName: String! + deleteManyMutationName: String! +} + +type KeystoneAdminUISort { + field: String! + direction: KeystoneAdminUISortDirection! +} + +enum KeystoneAdminUISortDirection { + ASC + DESC +} diff --git a/examples/number-fields/schema.prisma b/examples/number-fields/schema.prisma new file mode 100644 index 00000000000..2c0d305045e --- /dev/null +++ b/examples/number-fields/schema.prisma @@ -0,0 +1,23 @@ +// This file is automatically generated by Keystone, do not modify it manually. +// Modify your Keystone config when you want to change this. + +datasource sqlite { + url = env("DATABASE_URL") + shadowDatabaseUrl = env("SHADOW_DATABASE_URL") + provider = "sqlite" +} + +generator client { + provider = "prisma-client-js" + output = "node_modules/myprisma" +} + +model Example { + id String @id @default(cuid()) + bigInt BigInt? + bigIntDefaulted BigInt? @default(123) + float Float? + floatDefaulted Float? @default(456.321) + integer Int? + integerDefaulted Int? @default(789) +} diff --git a/examples/number-fields/schema.ts b/examples/number-fields/schema.ts new file mode 100644 index 00000000000..207845562b4 --- /dev/null +++ b/examples/number-fields/schema.ts @@ -0,0 +1,36 @@ +import { list, graphql } from '@keystone-6/core' +import { + bigInt, + float, + integer, + virtual +} from '@keystone-6/core/fields' +import { allowAll } from '@keystone-6/core/access' +import type { Lists } from '.keystone/types' + +export const lists = { + Example: list({ + access: allowAll, + fields: { + bigInt: bigInt(), + bigIntDefaulted: bigInt({ defaultValue: 123n }), + float: float(), + floatDefaulted: float({ defaultValue: 456.321 }), + integer: integer(), + integerDefaulted: integer({ defaultValue: 789 }), + maximum: virtual({ + field: graphql.field({ + type: graphql.Int, + resolve (x) { + return Math.max( + x.float ?? 0, + x.floatDefaulted ?? 0, + x.integer ?? 0, + x.integerDefaulted ?? 0, + ) + }, + }), + }), + }, + }), +} satisfies Lists diff --git a/examples/omit/schema.graphql b/examples/omit/schema.graphql index c75debec719..7518e05ce1d 100644 --- a/examples/omit/schema.graphql +++ b/examples/omit/schema.graphql @@ -182,24 +182,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/reuse/schema.graphql b/examples/reuse/schema.graphql index 859bd729feb..df1426c1bfc 100644 --- a/examples/reuse/schema.graphql +++ b/examples/reuse/schema.graphql @@ -253,24 +253,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/script/schema.graphql b/examples/script/schema.graphql index 94680c2e767..f46114e6247 100644 --- a/examples/script/schema.graphql +++ b/examples/script/schema.graphql @@ -131,24 +131,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/singleton/schema.graphql b/examples/singleton/schema.graphql index 542217e25ad..7f6f4a3201d 100644 --- a/examples/singleton/schema.graphql +++ b/examples/singleton/schema.graphql @@ -208,24 +208,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/testing/schema.graphql b/examples/testing/schema.graphql index a3de9384ab2..e286d79d267 100644 --- a/examples/testing/schema.graphql +++ b/examples/testing/schema.graphql @@ -254,11 +254,9 @@ type Query { users(where: UserWhereInput! = {}, orderBy: [UserOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: UserWhereUniqueInput): [User!] usersCount(where: UserWhereInput! = {}): Int keystone: KeystoneMeta! - authenticatedItem: AuthenticatedItem + authenticatedItem: User } -union AuthenticatedItem = User - type KeystoneMeta { adminMeta: KeystoneAdminMeta! } @@ -271,24 +269,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/transactions/schema.graphql b/examples/transactions/schema.graphql index 6104243e771..c9dad067073 100644 --- a/examples/transactions/schema.graphql +++ b/examples/transactions/schema.graphql @@ -295,24 +295,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/usecase-blog-moderated/schema.graphql b/examples/usecase-blog-moderated/schema.graphql index b0d2a3483f2..8cfd0764c6d 100644 --- a/examples/usecase-blog-moderated/schema.graphql +++ b/examples/usecase-blog-moderated/schema.graphql @@ -324,7 +324,6 @@ type Mutation { updateUsers(data: [UserUpdateArgs!]!): [User] deleteUser(where: UserWhereUniqueInput!): User deleteUsers(where: [UserWhereUniqueInput!]!): [User] - endSession: Boolean! } type Query { @@ -355,24 +354,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/usecase-blog/schema.graphql b/examples/usecase-blog/schema.graphql index b72e595735b..5e24c2e998d 100644 --- a/examples/usecase-blog/schema.graphql +++ b/examples/usecase-blog/schema.graphql @@ -298,24 +298,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/usecase-blog/schema.ts b/examples/usecase-blog/schema.ts index 0f278ce9127..05e7fcc9dc8 100644 --- a/examples/usecase-blog/schema.ts +++ b/examples/usecase-blog/schema.ts @@ -72,16 +72,6 @@ export const lists = { author: relationship({ // we could have used 'Author', but then the relationship would only be 1-way ref: 'Author.posts', - - // we customise how this will look in the AdminUI, for fun - ui: { - displayMode: 'cards', - cardFields: ['name', 'email'], - inlineEdit: { fields: ['name', 'email'] }, - linkToItem: true, - inlineConnect: true, - }, - many: false, // only 1 author for each Post (the default) }), @@ -90,27 +80,28 @@ export const lists = { ref: 'Tag.posts', many: true, // a Post can have many Tags, not just one - // we customise how this will look in the AdminUI, for fun - ui: { - displayMode: 'cards', - cardFields: ['name'], - inlineEdit: { fields: ['name'] }, - linkToItem: true, - inlineConnect: true, - inlineCreate: { fields: ['name'] }, - }, + // TODO: restore after breaking change +// ui: { +// inlineCreate: { +// fields: ['name'] +// } +// } }), }, }), // this last list is our Tag list, it only has a name field for now Tag: list({ - // WARNING - for this example, anyone can create, query, update and delete anything + // WARNING + // for this starter project, anyone can create, query, update and delete anything + // + // if you want to prevent random people on the internet from accessing your data, + // you can find out more at https://keystonejs.com/docs/guides/auth-and-access-control access: allowAll, - // we want to hide this list in the AdminUI + // dont show this list in the AdminUI ui: { - isHidden: true, + hideNavigation: true, }, fields: { diff --git a/examples/usecase-relationship-union/schema.graphql b/examples/usecase-relationship-union/schema.graphql index eb7b2294970..5ccebecc754 100644 --- a/examples/usecase-relationship-union/schema.graphql +++ b/examples/usecase-relationship-union/schema.graphql @@ -242,24 +242,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/usecase-roles/schema.graphql b/examples/usecase-roles/schema.graphql index 921166991cd..c8c61463665 100644 --- a/examples/usecase-roles/schema.graphql +++ b/examples/usecase-roles/schema.graphql @@ -337,11 +337,9 @@ type Query { roles(where: RoleWhereInput! = {}, orderBy: [RoleOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: RoleWhereUniqueInput): [Role!] rolesCount(where: RoleWhereInput! = {}): Int keystone: KeystoneMeta! - authenticatedItem: AuthenticatedItem + authenticatedItem: User } -union AuthenticatedItem = User - type KeystoneMeta { adminMeta: KeystoneAdminMeta! } @@ -354,24 +352,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/usecase-todo/schema.graphql b/examples/usecase-todo/schema.graphql index 000874fde47..8655bd0c572 100644 --- a/examples/usecase-todo/schema.graphql +++ b/examples/usecase-todo/schema.graphql @@ -242,24 +242,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/usecase-versioning/schema.graphql b/examples/usecase-versioning/schema.graphql index 262ec5cad3c..7e750c6cf02 100644 --- a/examples/usecase-versioning/schema.graphql +++ b/examples/usecase-versioning/schema.graphql @@ -134,24 +134,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/examples/virtual-field/schema.graphql b/examples/virtual-field/schema.graphql index 3a628eda09a..74f2a0777f4 100644 --- a/examples/virtual-field/schema.graphql +++ b/examples/virtual-field/schema.graphql @@ -143,24 +143,22 @@ type KeystoneAdminMeta { type KeystoneAdminUIListMeta { key: String! path: String! + description: String label: String! + labelField: String! singular: String! plural: String! - description: String - pageSize: Int! - labelField: String! fields: [KeystoneAdminUIFieldMeta!]! groups: [KeystoneAdminUIFieldGroupMeta!]! graphql: KeystoneAdminUIGraphQL! + pageSize: Int! initialColumns: [String!]! initialSearchFields: [String!]! initialSort: KeystoneAdminUISort isSingleton: Boolean! + hideNavigation: Boolean! hideCreate: Boolean! hideDelete: Boolean! - isHidden: Boolean! - itemQueryName: String! - listQueryName: String! } type KeystoneAdminUIFieldMeta { diff --git a/package.json b/package.json index 10744fa1f15..e53c7f2ebf0 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,8 @@ ], "exports": true, "___experimentalFlags_WILL_CHANGE_IN_PATCH": { - "keepDynamicImportAsDynamicImportInCommonJS": true + "keepDynamicImportAsDynamicImportInCommonJS": true, + "importsConditions": true } }, "jest": { diff --git a/packages/auth/components/Navigation/package.json b/packages/auth/components/Navigation/package.json new file mode 100644 index 00000000000..7d28b00c53f --- /dev/null +++ b/packages/auth/components/Navigation/package.json @@ -0,0 +1,4 @@ +{ + "main": "dist/keystone-6-auth-components-Navigation.cjs.js", + "module": "dist/keystone-6-auth-components-Navigation.esm.js" +} diff --git a/packages/auth/package.json b/packages/auth/package.json index 0acfdca1972..3943206bae6 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -6,21 +6,30 @@ "module": "dist/keystone-6-auth.esm.js", "exports": { ".": { + "types": "./dist/keystone-6-auth.cjs.js", "module": "./dist/keystone-6-auth.esm.js", "default": "./dist/keystone-6-auth.cjs.js" }, "./pages/InitPage": { + "types": "./pages/InitPage/dist/keystone-6-auth-pages-InitPage.cjs.js", "module": "./pages/InitPage/dist/keystone-6-auth-pages-InitPage.esm.js", "default": "./pages/InitPage/dist/keystone-6-auth-pages-InitPage.cjs.js" }, "./pages/SigninPage": { + "types": "./pages/SigninPage/dist/keystone-6-auth-pages-SigninPage.cjs.js", "module": "./pages/SigninPage/dist/keystone-6-auth-pages-SigninPage.esm.js", "default": "./pages/SigninPage/dist/keystone-6-auth-pages-SigninPage.cjs.js" }, + "./components/Navigation": { + "types": "./components/Navigation/dist/keystone-6-auth-components-Navigation.cjs.js", + "module": "./components/Navigation/dist/keystone-6-auth-components-Navigation.esm.js", + "default": "./components/Navigation/dist/keystone-6-auth-components-Navigation.cjs.js" + }, "./package.json": "./package.json" }, "dependencies": { "@babel/runtime": "^7.24.7", + "@keystar/ui": "^0.7.13", "@keystone-ui/button": "workspace:^", "@keystone-ui/core": "workspace:^", "@keystone-ui/fields": "workspace:^", @@ -41,6 +50,7 @@ "preconstruct": { "entrypoints": [ "index.ts", + "components/Navigation.tsx", "pages/*.tsx" ] }, diff --git a/packages/auth/src/components/Navigation.tsx b/packages/auth/src/components/Navigation.tsx new file mode 100644 index 00000000000..9bad086db1e --- /dev/null +++ b/packages/auth/src/components/Navigation.tsx @@ -0,0 +1,105 @@ +import React, { useEffect } from 'react' + +import { ActionButton } from '@keystar/ui/button' +import { Divider } from '@keystar/ui/layout' +import { TooltipTrigger, Tooltip } from '@keystar/ui/tooltip' +import { Text } from '@keystar/ui/typography' + +import { + useQuery, + useMutation, + gql, +} from '@keystone-6/core/admin-ui/apollo' +import { + DeveloperResourcesMenu, + NavList, + NavContainer, + NavFooter, + NavItem, + getHrefFromList +} from '@keystone-6/core/admin-ui/components' +import type { NavigationProps } from '@keystone-6/core/admin-ui/components' + +type AuthenticatedItem = { + label: string + id: string +} + +export default ({ labelField }: { labelField: string }) => (props: NavigationProps) => + +function Navigation ({ + labelField, + lists +}: { + labelField: string +} & NavigationProps) { + const { data } = useQuery<{ + authenticatedItem: AuthenticatedItem | null + }>(gql` + query Session { + authenticatedItem { + id + label: ${labelField} + } + } + `) + + return ( + + + Dashboard + + {lists.map((list) => ( + + {list.label} + + ))} + + + + {data?.authenticatedItem && ( + + )} + + + + ) +} + +function SignoutButton (props: { authItemLabel: string, children?: React.ReactNode }) { + const { authItemLabel, children = 'Sign out' } = props + const { signout } = useSignout() + + return ( + + signout()}> + {children} + + + Signed in as {authItemLabel} + + + ) +} + +const END_SESSION = gql` + mutation EndSession { + endSession + } +` + +function useSignout () { + const [signout, result] = useMutation(END_SESSION) + + // TODO: handle errors + useEffect(() => { + if (result.data?.endSession) { + window.location.reload() + } + }, [result.data]) + + return { + signout, + loading: result.loading, + } +} diff --git a/packages/auth/src/components/SigninContainer.tsx b/packages/auth/src/components/SigninContainer.tsx index 759004aaf97..09370147a89 100644 --- a/packages/auth/src/components/SigninContainer.tsx +++ b/packages/auth/src/components/SigninContainer.tsx @@ -3,7 +3,12 @@ import { type ReactNode } from 'react' -import { jsx, Box, Center, useTheme } from '@keystone-ui/core' +import { + jsx, + Box, + Center, + useTheme +} from '@keystone-ui/core' import { Head } from '@keystone-6/core/admin-ui/router' type SigninContainerProps = { @@ -11,12 +16,12 @@ type SigninContainerProps = { title?: string } -export const SigninContainer = ({ children, title }: SigninContainerProps) => { +export function SigninContainer ({ children, title }: SigninContainerProps) { const { colors, shadow } = useTheme() return (
- {title || 'Keystone'} + {title ?? 'Keystone'}
({ name: gqlNames.ItemAuthenticationWithPasswordResult, types: [ItemAuthenticationWithPasswordSuccess, ItemAuthenticationWithPasswordFailure], resolveType (val) { - if ('sessionToken' in val) { - return gqlNames.ItemAuthenticationWithPasswordSuccess - } + if ('sessionToken' in val) return gqlNames.ItemAuthenticationWithPasswordSuccess return gqlNames.ItemAuthenticationWithPasswordFailure }, }) @@ -57,16 +55,10 @@ export function getBaseAuthSchema ({ const extension = { query: { authenticatedItem: graphql.field({ - type: graphql.union({ - name: 'AuthenticatedItem', - types: [base.object(listKey) as graphql.ObjectType], - resolveType: (root, context: KeystoneContext) => context.session?.listKey, - }), + type: base.object(listKey), resolve (root, args, context: KeystoneContext) { const { session } = context - if (!session) return null - if (!session.itemId) return null - if (session.listKey !== listKey) return null + if (!session?.itemId) return null return context.db[listKey].findOne({ where: { @@ -77,6 +69,13 @@ export function getBaseAuthSchema ({ }), }, mutation: { + endSession: graphql.field({ + type: graphql.nonNull(graphql.Boolean), + async resolve (rootVal, args, context) { + await context.sessionStrategy?.end({ context }) + return true + }, + }), [gqlNames.authenticateItemWithPassword]: graphql.field({ type: AuthenticationResult, args: { @@ -103,7 +102,6 @@ export function getBaseAuthSchema ({ // Update system state const sessionToken = await context.sessionStrategy.start({ data: { - listKey, itemId: result.item.id, }, context, diff --git a/packages/auth/src/index.ts b/packages/auth/src/index.ts index 68448486a4c..dba1c8fe01d 100644 --- a/packages/auth/src/index.ts +++ b/packages/auth/src/index.ts @@ -10,11 +10,11 @@ import { password, timestamp } from '@keystone-6/core/fields' import type { AuthConfig, AuthGqlNames } from './types' import { getSchemaExtension } from './schema' -import { signinTemplate } from './templates/signin' -import { initTemplate } from './templates/init' +import configTemplate from './templates/config' +import signinTemplate from './templates/signin' +import initTemplate from './templates/init' export type AuthSession = { - listKey: string // TODO: use ListTypeInfo itemId: string | number // TODO: use ListTypeInfo data: unknown // TODO: use ListTypeInfo } @@ -99,13 +99,30 @@ export function createAuth ({ * * The signin page is always included, and the init page is included when initFirstItem is set */ - const authGetAdditionalFiles = () => { + const authGetAdditionalFiles = (config: KeystoneConfig) => { + // TODO: FIXME: this is a duplication of initialise-lists:747 + const listConfig = config.lists[listKey] + const labelField = + listConfig.ui?.labelField ?? + (listConfig.fields.label + ? 'label' + : listConfig.fields.name + ? 'name' + : listConfig.fields.title + ? 'title' + : 'id') + const filesToWrite: AdminFileToWrite[] = [ { mode: 'write', - src: signinTemplate({ gqlNames, identityField, secretField }), + src: signinTemplate({ listKey, gqlNames, identityField, secretField }), outputPath: 'pages/signin.js', }, + { + mode: 'write', + src: configTemplate({ labelField }), + outputPath: 'config.ts', + }, ] if (initFirstItem) { filesToWrite.push({ @@ -169,9 +186,10 @@ export function createAuth ({ get: async ({ context }) => { const session = await get({ context }) const sudoContext = context.sudo() - if (!session) return - if (!session.itemId) return - if (session.listKey !== listKey) return + if (!session?.itemId) return + + // TODO: replace with SessionSecret: HMAC({ listKey, identityField, secretField }, SessionSecretVar) + // if (session.listKey !== listKey) return null try { const data = await sudoContext.query[listKey].findOne({ @@ -180,11 +198,14 @@ export function createAuth ({ }) if (!data) return - return { ...session, itemId: session.itemId, listKey, data } + return { + ...session, + itemId: session.itemId, + data + } } catch (e) { console.error(e) - // TODO: the assumption is this could only be from an invalid sessionData configuration - // it could be something else though, either way, result is a bad session + // WARNING: this is probably an invalid configuration return } }, @@ -262,7 +283,7 @@ export function createAuth ({ ui = { ...ui, publicPages: [...publicPages, ...authPublicPages], - getAdditionalFiles: [...getAdditionalFiles, authGetAdditionalFiles], + getAdditionalFiles: [...getAdditionalFiles, () => authGetAdditionalFiles(config)], isAccessAllowed: async (context: KeystoneContext) => { if (await hasInitFirstItemConditions(context)) return true diff --git a/packages/auth/src/pages/InitPage.tsx b/packages/auth/src/pages/InitPage.tsx index 2b8a3317760..a4df122517a 100644 --- a/packages/auth/src/pages/InitPage.tsx +++ b/packages/auth/src/pages/InitPage.tsx @@ -3,12 +3,12 @@ import { useMemo, useState } from 'react' import fetch from 'cross-fetch' +import isDeepEqual from 'fast-deep-equal' import { jsx, H1, Stack, Inline } from '@keystone-ui/core' import { Button } from '@keystone-ui/button' import { Checkbox, FieldLabel, TextInput } from '@keystone-ui/fields' import { type FieldMeta } from '@keystone-6/core/types' -import isDeepEqual from 'fast-deep-equal' import { gql, useMutation } from '@keystone-6/core/admin-ui/apollo' import { useReinitContext, useKeystone } from '@keystone-6/core/admin-ui/context' @@ -26,6 +26,8 @@ import { useRedirect } from '../lib/useFromRedirect' const signupURL = 'https://endpoints.thinkmill.com.au/newsletter' +export default (props: Parameters[0]) => () => + function Welcome ({ value, onContinue }: { value: any, onContinue: () => void }) { const [subscribe, setSubscribe] = useState<{ keystone: boolean, thinkmill: boolean}>( { @@ -300,5 +302,3 @@ function InitPage ({ ) } - -export const getInitPage = (props: Parameters[0]) => () => diff --git a/packages/auth/src/pages/SigninPage.tsx b/packages/auth/src/pages/SigninPage.tsx index d0b5b7536d7..1540c1065e5 100644 --- a/packages/auth/src/pages/SigninPage.tsx +++ b/packages/auth/src/pages/SigninPage.tsx @@ -1,28 +1,30 @@ /** @jsxRuntime classic */ /** @jsx jsx */ -import { useState, Fragment, type FormEvent, useRef, useEffect } from 'react' +import { + type FormEvent, + Fragment, + useEffect, + useRef, + useState, +} from 'react' import { jsx, H1, Stack, VisuallyHidden } from '@keystone-ui/core' import { Button } from '@keystone-ui/button' import { TextInput } from '@keystone-ui/fields' import { Notice } from '@keystone-ui/notice' -import { useMutation, gql } from '@keystone-6/core/admin-ui/apollo' +import { + useQuery, + useMutation, + gql +} from '@keystone-6/core/admin-ui/apollo' import { useRawKeystone, useReinitContext } from '@keystone-6/core/admin-ui/context' import { useRouter } from '@keystone-6/core/admin-ui/router' import { SigninContainer } from '../components/SigninContainer' import { useRedirect } from '../lib/useFromRedirect' -type SigninPageProps = { - identityField: string - secretField: string - mutationName: string - successTypename: string - failureTypename: string -} - -export const getSigninPage = (props: SigninPageProps) => () => +export default (props: Parameters[0]) => () => export function SigninPage ({ identityField, @@ -30,9 +32,15 @@ export function SigninPage ({ mutationName, successTypename, failureTypename, -}: SigninPageProps) { +}: { + identityField: string + secretField: string + mutationName: string + successTypename: string + failureTypename: string +}) { const mutation = gql` - mutation($identity: String!, $secret: String!) { + mutation ($identity: String!, $secret: String!) { authenticate: ${mutationName}(${identityField}: $identity, ${secretField}: $secret) { ... on ${successTypename} { item { @@ -60,14 +68,23 @@ export function SigninPage ({ const router = useRouter() const rawKeystone = useRawKeystone() const redirect = useRedirect() + const { data: whoamiResult } = useQuery<{ + authenticatedItem: { id: unknown } | null + }>(gql` + query Session { + authenticatedItem { + id + } + } + `) // if we are signed in, redirect immediately useEffect(() => { if (submitted) return - if (rawKeystone.authenticatedItem.state === 'authenticated') { + if (whoamiResult?.authenticatedItem?.id) { router.push(redirect) } - }, [rawKeystone.authenticatedItem, router, redirect, submitted]) + }, [whoamiResult?.authenticatedItem?.id, router, redirect, submitted]) useEffect(() => { if (!submitted) return diff --git a/packages/auth/src/templates/config.ts b/packages/auth/src/templates/config.ts new file mode 100644 index 00000000000..87f2551c454 --- /dev/null +++ b/packages/auth/src/templates/config.ts @@ -0,0 +1,13 @@ +export default function ({ + labelField +}: { + labelField: string +}) { + return `import { type AdminConfig } from '@keystone-6/core/types' +import makeNavigation from '@keystone-6/auth/components/Navigation' + +export const components: AdminConfig['components'] = { + Navigation: makeNavigation({ labelField: '${labelField}' }), +} +` +} diff --git a/packages/auth/src/templates/init.ts b/packages/auth/src/templates/init.ts index 03e69aa977d..8ea89ef7035 100644 --- a/packages/auth/src/templates/init.ts +++ b/packages/auth/src/templates/init.ts @@ -1,22 +1,19 @@ import type { BaseListTypeInfo } from '@keystone-6/core/types' import type { AuthConfig } from '../types' -type InitTemplateArgs = { +export default function ({ + listKey, + initFirstItem +}: { listKey: string initFirstItem: NonNullable['initFirstItem']> -} - -export const initTemplate = ({ listKey, initFirstItem }: InitTemplateArgs) => { - // -- TEMPLATE START - return `import { getInitPage } from '@keystone-6/auth/pages/InitPage'; - -const fieldPaths = ${JSON.stringify(initFirstItem.fields)}; +}) { + return `import makeSigninPage from '@keystone-6/auth/pages/InitPage' -export default getInitPage(${JSON.stringify({ - listKey, - fieldPaths: initFirstItem.fields, - enableWelcome: !initFirstItem.skipKeystoneWelcome, - })}); +export default makeSigninPage(${JSON.stringify({ + listKey, + fieldPaths: initFirstItem.fields, + enableWelcome: !initFirstItem.skipKeystoneWelcome, +})}) ` - // -- TEMPLATE END } diff --git a/packages/auth/src/templates/signin.ts b/packages/auth/src/templates/signin.ts index 6e333babc59..e43c47a22d6 100644 --- a/packages/auth/src/templates/signin.ts +++ b/packages/auth/src/templates/signin.ts @@ -1,24 +1,25 @@ import { type AuthGqlNames } from '../types' -export const signinTemplate = ({ +export default ({ + listKey, gqlNames, identityField, secretField, }: { + listKey: string gqlNames: AuthGqlNames identityField: string secretField: string }) => { - // -- TEMPLATE START - return `import { getSigninPage } from '@keystone-6/auth/pages/SigninPage' + return `import makeSigninPage from '@keystone-6/auth/pages/SigninPage' -export default getSigninPage(${JSON.stringify({ - identityField: identityField, - secretField: secretField, - mutationName: gqlNames.authenticateItemWithPassword, - successTypename: gqlNames.ItemAuthenticationWithPasswordSuccess, - failureTypename: gqlNames.ItemAuthenticationWithPasswordFailure, - })}); +export default makeSigninPage(${JSON.stringify({ + listKey, + identityField, + secretField, + mutationName: gqlNames.authenticateItemWithPassword, + successTypename: gqlNames.ItemAuthenticationWithPasswordSuccess, + failureTypename: gqlNames.ItemAuthenticationWithPasswordFailure, +})}) ` - // -- TEMPLATE END } diff --git a/packages/cloudinary/package.json b/packages/cloudinary/package.json index b065b25e434..4ad5d6bce09 100644 --- a/packages/cloudinary/package.json +++ b/packages/cloudinary/package.json @@ -6,10 +6,12 @@ "module": "dist/keystone-6-cloudinary.esm.js", "exports": { ".": { + "types": "./dist/keystone-6-cloudinary.cjs.js", "module": "./dist/keystone-6-cloudinary.esm.js", "default": "./dist/keystone-6-cloudinary.cjs.js" }, "./views": { + "types": "./views/dist/keystone-6-cloudinary-views.cjs.js", "module": "./views/dist/keystone-6-cloudinary-views.esm.js", "default": "./views/dist/keystone-6-cloudinary-views.cjs.js" }, diff --git a/packages/cloudinary/src/views/index.tsx b/packages/cloudinary/src/views/index.tsx index 0f2ee54266f..5e236e38015 100644 --- a/packages/cloudinary/src/views/index.tsx +++ b/packages/cloudinary/src/views/index.tsx @@ -3,12 +3,10 @@ import { jsx } from '@keystone-ui/core' import { - type CardValueComponent, type CellComponent, type FieldController, type FieldControllerConfig, } from '@keystone-6/core/types' -import { FieldContainer, FieldLabel } from '@keystone-ui/fields' import { validateImage } from './Field' export { Field } from './Field' @@ -35,16 +33,6 @@ export const Cell: CellComponent = ({ item, field }) => { ) } -export const CardValue: CardValueComponent = ({ item, field }) => { - const data = item[field.path] - return ( - - {field.label} - {data && {data.filename}} - - ) -} - type ImageData = { id: string filename: string @@ -69,7 +57,7 @@ type CloudinaryImageValue = type CloudinaryImageController = FieldController -export const controller = (config: FieldControllerConfig): CloudinaryImageController => { +export function controller (config: FieldControllerConfig): CloudinaryImageController { return { path: config.path, label: config.label, diff --git a/packages/core/package.json b/packages/core/package.json index ff8a944d065..bf9714e54a9 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -7,166 +7,207 @@ "module": "dist/keystone-6-core.esm.js", "exports": { ".": { + "types": "./dist/keystone-6-core.cjs.js", "module": "./dist/keystone-6-core.esm.js", "default": "./dist/keystone-6-core.cjs.js" }, "./access": { + "types": "./access/dist/keystone-6-core-access.cjs.js", "module": "./access/dist/keystone-6-core-access.esm.js", "default": "./access/dist/keystone-6-core-access.cjs.js" }, "./context": { + "types": "./context/dist/keystone-6-core-context.cjs.js", "module": "./context/dist/keystone-6-core-context.esm.js", "default": "./context/dist/keystone-6-core-context.cjs.js" }, "./session": { + "types": "./session/dist/keystone-6-core-session.cjs.js", "module": "./session/dist/keystone-6-core-session.esm.js", "default": "./session/dist/keystone-6-core-session.cjs.js" }, "./testing": { + "types": "./testing/dist/keystone-6-core-testing.cjs.js", "module": "./testing/dist/keystone-6-core-testing.esm.js", "default": "./testing/dist/keystone-6-core-testing.cjs.js" }, "./scripts/cli": { + "types": "./scripts/cli/dist/keystone-6-core-scripts-cli.cjs.js", "module": "./scripts/cli/dist/keystone-6-core-scripts-cli.esm.js", "default": "./scripts/cli/dist/keystone-6-core-scripts-cli.cjs.js" }, "./types": { + "types": "./types/dist/keystone-6-core-types.cjs.js", "module": "./types/dist/keystone-6-core-types.esm.js", "default": "./types/dist/keystone-6-core-types.cjs.js" }, "./fields": { + "types": "./fields/dist/keystone-6-core-fields.cjs.js", "module": "./fields/dist/keystone-6-core-fields.esm.js", "default": "./fields/dist/keystone-6-core-fields.cjs.js" }, "./scripts": { + "types": "./scripts/dist/keystone-6-core-scripts.cjs.js", "module": "./scripts/dist/keystone-6-core-scripts.esm.js", "default": "./scripts/dist/keystone-6-core-scripts.cjs.js" }, "./admin-ui/image": { + "types": "./admin-ui/image/dist/keystone-6-core-admin-ui-image.cjs.js", "module": "./admin-ui/image/dist/keystone-6-core-admin-ui-image.esm.js", "default": "./admin-ui/image/dist/keystone-6-core-admin-ui-image.cjs.js" }, "./admin-ui/apollo": { + "types": "./admin-ui/apollo/dist/keystone-6-core-admin-ui-apollo.cjs.js", "module": "./admin-ui/apollo/dist/keystone-6-core-admin-ui-apollo.esm.js", "default": "./admin-ui/apollo/dist/keystone-6-core-admin-ui-apollo.cjs.js" }, "./admin-ui/router": { + "types": "./admin-ui/router/dist/keystone-6-core-admin-ui-router.cjs.js", "module": "./admin-ui/router/dist/keystone-6-core-admin-ui-router.esm.js", "default": "./admin-ui/router/dist/keystone-6-core-admin-ui-router.cjs.js" }, "./admin-ui/context": { + "types": "./admin-ui/context/dist/keystone-6-core-admin-ui-context.cjs.js", "module": "./admin-ui/context/dist/keystone-6-core-admin-ui-context.esm.js", "default": "./admin-ui/context/dist/keystone-6-core-admin-ui-context.cjs.js" }, "./admin-ui/utils": { + "types": "./admin-ui/utils/dist/keystone-6-core-admin-ui-utils.cjs.js", "module": "./admin-ui/utils/dist/keystone-6-core-admin-ui-utils.esm.js", "default": "./admin-ui/utils/dist/keystone-6-core-admin-ui-utils.cjs.js" }, "./fields/types/image/utils": { + "types": "./fields/types/image/utils/dist/keystone-6-core-fields-types-image-utils.cjs.js", "module": "./fields/types/image/utils/dist/keystone-6-core-fields-types-image-utils.esm.js", "default": "./fields/types/image/utils/dist/keystone-6-core-fields-types-image-utils.cjs.js" }, "./admin-ui/components": { + "types": "./admin-ui/components/dist/keystone-6-core-admin-ui-components.cjs.js", "module": "./admin-ui/components/dist/keystone-6-core-admin-ui-components.esm.js", "default": "./admin-ui/components/dist/keystone-6-core-admin-ui-components.cjs.js" }, "./fields/types/file/views": { + "types": "./fields/types/file/views/dist/keystone-6-core-fields-types-file-views.cjs.js", "module": "./fields/types/file/views/dist/keystone-6-core-fields-types-file-views.esm.js", "default": "./fields/types/file/views/dist/keystone-6-core-fields-types-file-views.cjs.js" }, "./fields/types/json/views": { + "types": "./fields/types/json/views/dist/keystone-6-core-fields-types-json-views.cjs.js", "module": "./fields/types/json/views/dist/keystone-6-core-fields-types-json-views.esm.js", "default": "./fields/types/json/views/dist/keystone-6-core-fields-types-json-views.cjs.js" }, "./fields/types/text/views": { + "types": "./fields/types/text/views/dist/keystone-6-core-fields-types-text-views.cjs.js", "module": "./fields/types/text/views/dist/keystone-6-core-fields-types-text-views.esm.js", "default": "./fields/types/text/views/dist/keystone-6-core-fields-types-text-views.cjs.js" }, "./fields/types/float/views": { + "types": "./fields/types/float/views/dist/keystone-6-core-fields-types-float-views.cjs.js", "module": "./fields/types/float/views/dist/keystone-6-core-fields-types-float-views.esm.js", "default": "./fields/types/float/views/dist/keystone-6-core-fields-types-float-views.cjs.js" }, "./fields/types/image/views": { + "types": "./fields/types/image/views/dist/keystone-6-core-fields-types-image-views.cjs.js", "module": "./fields/types/image/views/dist/keystone-6-core-fields-types-image-views.esm.js", "default": "./fields/types/image/views/dist/keystone-6-core-fields-types-image-views.cjs.js" }, "./fields/types/bigInt/views": { + "types": "./fields/types/bigInt/views/dist/keystone-6-core-fields-types-bigInt-views.cjs.js", "module": "./fields/types/bigInt/views/dist/keystone-6-core-fields-types-bigInt-views.esm.js", "default": "./fields/types/bigInt/views/dist/keystone-6-core-fields-types-bigInt-views.cjs.js" }, "./fields/types/select/views": { + "types": "./fields/types/select/views/dist/keystone-6-core-fields-types-select-views.cjs.js", "module": "./fields/types/select/views/dist/keystone-6-core-fields-types-select-views.esm.js", "default": "./fields/types/select/views/dist/keystone-6-core-fields-types-select-views.cjs.js" }, "./fields/types/decimal/views": { + "types": "./fields/types/decimal/views/dist/keystone-6-core-fields-types-decimal-views.cjs.js", "module": "./fields/types/decimal/views/dist/keystone-6-core-fields-types-decimal-views.esm.js", "default": "./fields/types/decimal/views/dist/keystone-6-core-fields-types-decimal-views.cjs.js" }, "./fields/types/integer/views": { + "types": "./fields/types/integer/views/dist/keystone-6-core-fields-types-integer-views.cjs.js", "module": "./fields/types/integer/views/dist/keystone-6-core-fields-types-integer-views.esm.js", "default": "./fields/types/integer/views/dist/keystone-6-core-fields-types-integer-views.cjs.js" }, "./fields/types/virtual/views": { + "types": "./fields/types/virtual/views/dist/keystone-6-core-fields-types-virtual-views.cjs.js", "module": "./fields/types/virtual/views/dist/keystone-6-core-fields-types-virtual-views.esm.js", "default": "./fields/types/virtual/views/dist/keystone-6-core-fields-types-virtual-views.cjs.js" }, "./fields/types/checkbox/views": { + "types": "./fields/types/checkbox/views/dist/keystone-6-core-fields-types-checkbox-views.cjs.js", "module": "./fields/types/checkbox/views/dist/keystone-6-core-fields-types-checkbox-views.esm.js", "default": "./fields/types/checkbox/views/dist/keystone-6-core-fields-types-checkbox-views.cjs.js" }, "./fields/types/password/views": { + "types": "./fields/types/password/views/dist/keystone-6-core-fields-types-password-views.cjs.js", "module": "./fields/types/password/views/dist/keystone-6-core-fields-types-password-views.esm.js", "default": "./fields/types/password/views/dist/keystone-6-core-fields-types-password-views.cjs.js" }, "./fields/types/timestamp/views": { + "types": "./fields/types/timestamp/views/dist/keystone-6-core-fields-types-timestamp-views.cjs.js", "module": "./fields/types/timestamp/views/dist/keystone-6-core-fields-types-timestamp-views.esm.js", "default": "./fields/types/timestamp/views/dist/keystone-6-core-fields-types-timestamp-views.cjs.js" }, "./fields/types/calendarDay/views": { + "types": "./fields/types/calendarDay/views/dist/keystone-6-core-fields-types-calendarDay-views.cjs.js", "module": "./fields/types/calendarDay/views/dist/keystone-6-core-fields-types-calendarDay-views.esm.js", "default": "./fields/types/calendarDay/views/dist/keystone-6-core-fields-types-calendarDay-views.cjs.js" }, "./fields/types/multiselect/views": { + "types": "./fields/types/multiselect/views/dist/keystone-6-core-fields-types-multiselect-views.cjs.js", "module": "./fields/types/multiselect/views/dist/keystone-6-core-fields-types-multiselect-views.esm.js", "default": "./fields/types/multiselect/views/dist/keystone-6-core-fields-types-multiselect-views.cjs.js" }, "./fields/types/relationship/views": { + "types": "./fields/types/relationship/views/dist/keystone-6-core-fields-types-relationship-views.cjs.js", "module": "./fields/types/relationship/views/dist/keystone-6-core-fields-types-relationship-views.esm.js", "default": "./fields/types/relationship/views/dist/keystone-6-core-fields-types-relationship-views.cjs.js" }, "./fields/types/relationship/views/RelationshipSelect": { + "types": "./fields/types/relationship/views/RelationshipSelect/dist/keystone-6-core-fields-types-relationship-views-RelationshipSelect.cjs.js", "module": "./fields/types/relationship/views/RelationshipSelect/dist/keystone-6-core-fields-types-relationship-views-RelationshipSelect.esm.js", "default": "./fields/types/relationship/views/RelationshipSelect/dist/keystone-6-core-fields-types-relationship-views-RelationshipSelect.cjs.js" }, "./___internal-do-not-use-will-break-in-patch/artifacts": { + "types": "./___internal-do-not-use-will-break-in-patch/artifacts/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-artifacts.cjs.js", "module": "./___internal-do-not-use-will-break-in-patch/artifacts/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-artifacts.esm.js", "default": "./___internal-do-not-use-will-break-in-patch/artifacts/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-artifacts.cjs.js" }, "./___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view": { + "types": "./___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-id-field-view.cjs.js", "module": "./___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-id-field-view.esm.js", "default": "./___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-id-field-view.cjs.js" }, "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/App": { + "types": "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/App/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-App.cjs.js", "module": "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/App/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-App.esm.js", "default": "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/App/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-App.cjs.js" }, "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/HomePage": { + "types": "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/HomePage/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-HomePage.cjs.js", "module": "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/HomePage/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-HomePage.esm.js", "default": "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/HomePage/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-HomePage.cjs.js" }, "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage": { + "types": "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-ItemPage.cjs.js", "module": "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-ItemPage.esm.js", "default": "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-ItemPage.cjs.js" }, "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage": { + "types": "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-ListPage.cjs.js", "module": "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-ListPage.esm.js", "default": "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-ListPage.cjs.js" }, "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/NoAccessPage": { + "types": "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/NoAccessPage/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-NoAccessPage.cjs.js", "module": "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/NoAccessPage/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-NoAccessPage.esm.js", "default": "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/NoAccessPage/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-NoAccessPage.cjs.js" }, "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/CreateItemPage": { + "types": "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/CreateItemPage/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-CreateItemPage.cjs.js", "module": "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/CreateItemPage/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-CreateItemPage.esm.js", "default": "./___internal-do-not-use-will-break-in-patch/admin-ui/pages/CreateItemPage/dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-CreateItemPage.cjs.js" }, @@ -190,6 +231,8 @@ "@graphql-ts/schema": "^0.6.0", "@graphql-typed-document-node/core": "^3.1.2", "@hapi/iron": "^7.0.0", + "@internationalized/date": "^3.5.5", + "@keystar/ui": "^0.7.13", "@keystone-ui/button": "workspace:^", "@keystone-ui/core": "workspace:^", "@keystone-ui/fields": "workspace:^", @@ -207,6 +250,10 @@ "@prisma/client": "5.22.0", "@prisma/internals": "5.22.0", "@prisma/migrate": "5.22.0", + "@react-aria/i18n": "^3.12.1", + "@react-aria/label": "^3.7.10", + "@react-aria/utils": "^3.25.1", + "@react-stately/data": "^3.11.6", "@sindresorhus/slugify": "^1.1.2", "apollo-upload-client": "^17.0.0", "bcryptjs": "^2.4.3", diff --git a/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view.tsx b/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view.tsx index dae6dfe031c..80b7373843d 100644 --- a/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view.tsx +++ b/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view.tsx @@ -1,34 +1,24 @@ -/** @jsxRuntime classic */ -/** @jsx jsx */ +import React from 'react' + +import { TextField } from '@keystar/ui/text-field' +import { Text } from '@keystar/ui/typography' -import { jsx } from '@keystone-ui/core' -import { FieldContainer, FieldLabel, TextInput } from '@keystone-ui/fields' import type { - CardValueComponent, CellComponent, FieldController, FieldControllerConfig, IdFieldConfig, } from '../../types' -import { CellLink, CellContainer } from '../../admin-ui/components' export function Field () { return null } -export const Cell: CellComponent = ({ item, field, linkTo }) => { - const value = item[field.path] + '' - return linkTo ? {value} : {value} -} -Cell.supportsLinkTo = true - -export const CardValue: CardValueComponent = ({ item, field }) => { - return ( - - {field.label} - {item[field.path]} - - ) +export const Cell: CellComponent = ({ field, item }) => { + let value = item[field.path] + return value != null + ? {value.toString()} + : null } export function controller ( @@ -44,13 +34,19 @@ export function controller ( serialize: () => ({}), filter: { Filter (props) { + const { autoFocus, context, onChange, type, typeLabel, value, ...otherProps } = props + + const labelProps = context === 'add' + ? { label: config.label, description: typeLabel } + : { label: typeLabel } + return ( - { - props.onChange(event.target.value) - }} - value={props.value} - autoFocus={props.autoFocus} + ) }, diff --git a/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/App/index.tsx b/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/App/index.tsx index 49d0da8982c..583e0614c48 100644 --- a/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/App/index.tsx +++ b/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/App/index.tsx @@ -1,7 +1,7 @@ import React from 'react' -import { Core } from '@keystone-ui/core' import { type AppProps } from 'next/app' import { type DocumentNode } from 'graphql' + import { type AdminConfig, type FieldViews } from '../../../../types' import { ErrorBoundary } from '../../../../admin-ui/components' import { KeystoneProvider } from '../../../../admin-ui/context' @@ -18,12 +18,10 @@ export const getApp = (props: AppConfig) => ({ Component, pageProps }: AppProps) => { return ( - - - - - - - + + + + + ) } diff --git a/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/CreateItemPage/index.tsx b/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/CreateItemPage/index.tsx index e437e65af8f..bb83d2111f3 100644 --- a/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/CreateItemPage/index.tsx +++ b/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/CreateItemPage/index.tsx @@ -1,80 +1,93 @@ -/** @jsxRuntime classic */ -/** @jsx jsx */ +import React from 'react' +import { useRouter } from 'next/router' -import { Box, jsx } from '@keystone-ui/core' +import { Button } from '@keystar/ui/button' +import { VStack } from '@keystar/ui/layout' import { LoadingDots } from '@keystone-ui/loading' -import { Button } from '@keystone-ui/button' -import { useRouter } from 'next/router' + import { Fields } from '../../../../admin-ui/utils' import { PageContainer } from '../../../../admin-ui/components/PageContainer' import { useKeystone, useList } from '../../../../admin-ui' import { GraphQLErrorNotice } from '../../../../admin-ui/components' -import { type ListMeta } from '../../../../types' import { useCreateItem } from '../../../../admin-ui/utils/useCreateItem' import { BaseToolbar, ColumnLayout, ItemPageHeader } from '../ItemPage/common' -function CreatePageForm (props: { list: ListMeta }) { - const createItem = useCreateItem(props.list) - const router = useRouter() - return ( - - {createItem.error && ( - - )} - - - - - - - ) -} - type CreateItemPageProps = { listKey: string } -export const getCreateItemPage = (props: CreateItemPageProps) => () => - +export const getCreateItemPage = (props: CreateItemPageProps) => () => function CreateItemPage (props: CreateItemPageProps) { + const router = useRouter() const list = useList(props.listKey) + const createItem = useCreateItem(list) const { createViewFieldModes } = useKeystone() return ( } + header={} > - - - {createViewFieldModes.state === 'error' && ( - + ) : ( + +
{ + e.preventDefault() + const item = await createItem.create() + if (item) { + router.push(`/${list.path}/${item.id}`) } - /> - )} - {createViewFieldModes.state === 'loading' && } - - - + }} + style={{ + display: 'contents', + }} + > + {/* + Workaround for react-aria "bug" where pressing enter in a form field + moves focus to the submit button. + See: https://github.com/adobe/react-spectrum/issues/5940 + */} + + + +
+ )}
) } diff --git a/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/HomePage/index.tsx b/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/HomePage/index.tsx index fa8cc5bda5b..71dab2d7f13 100644 --- a/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/HomePage/index.tsx +++ b/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/HomePage/index.tsx @@ -1,23 +1,27 @@ -/** @jsxRuntime classic */ -/** @jsx jsx */ +import React, { + type PropsWithChildren, + useId, + useMemo, + useRef +} from 'react' -import { useMemo } from 'react' - -import { Center, Inline, Heading, VisuallyHidden, jsx, useTheme } from '@keystone-ui/core' -import { PlusIcon } from '@keystone-ui/icons/icons/PlusIcon' -import { LoadingDots } from '@keystone-ui/loading' +import { ActionButton } from '@keystar/ui/button' +import { Icon } from '@keystar/ui/icon' +import { plusIcon } from '@keystar/ui/icon/icons/plusIcon' +import { Grid, VStack } from '@keystar/ui/layout' +import { useLink } from '@keystar/ui/link' +import { Notice } from '@keystar/ui/notice' +import { ProgressCircle } from '@keystar/ui/progress' +import { css, FocusRing, tokenSchema, transition } from '@keystar/ui/style' +import { TooltipTrigger, Tooltip } from '@keystar/ui/tooltip' +import { Heading, Text } from '@keystar/ui/typography' import { makeDataGetter } from '../../../../admin-ui/utils' -import { PageContainer, HEADER_HEIGHT } from '../../../../admin-ui/components/PageContainer' +import { PageContainer } from '../../../../admin-ui/components/PageContainer' import { gql, useQuery } from '../../../../admin-ui/apollo' import { useKeystone, useList } from '../../../../admin-ui/context' -import { Link, type LinkProps } from '../../../../admin-ui/router' -function ListCard ({ - listKey, - count, - hideCreate -}: { +type ListCardProps = { listKey: string hideCreate: boolean count: @@ -25,83 +29,6 @@ function ListCard ({ | { type: 'no-access' } | { type: 'error', message: string } | { type: 'loading' } -}) { - const { colors, palette, radii, spacing } = useTheme() - const list = useList(listKey) - return ( -
- -

{list.label}

- {list.isSingleton ? null : count.type === 'success' ? ( - - {count.count} item{count.count !== 1 ? 's' : ''} - - ) : count.type === 'error' ? ( - count.message - ) : count.type === 'loading' ? ( - - ) : ( - 'No access' - )} - - {hideCreate === false && !list.isSingleton && ( - - - Create {list.singular} - - )} -
- ) -} - -function CreateButton (props: LinkProps) { - const theme = useTheme() - return ( - - ) } export function HomePage () { @@ -122,7 +49,7 @@ export function HomePage () { } ${Object.values(lists) .filter(list => !list.isSingleton) - .map(list => `${list.key}: ${list.gqlNames.listQueryCountName}`) + .map(list => `${list.key}: ${list.graphql.names.listQueryCountName}`) .join('\n')} }`, [lists] @@ -131,58 +58,170 @@ export function HomePage () { const dataGetter = makeDataGetter(data, error?.graphQLErrors) - return ( - Dashboard}> - {visibleLists.state === 'loading' ? ( -
- -
- ) : ( - - {(() => { - if (visibleLists.state === 'error') { - return ( - - {visibleLists.error instanceof Error - ? visibleLists.error.message - : visibleLists.error[0].message} - - ) - } - return Object.keys(lists).map(key => { - if (!visibleLists.lists.has(key)) { - return null + const stateAwareElement = (() => { + if (visibleLists.state === 'error') { + return ( + + {visibleLists.error instanceof Error + ? visibleLists.error.message + : visibleLists.error[0].message} + + ) + } + if (visibleLists.state === 'loading') { + return ( + + + + ) + } + + return ( + + {Object.keys(lists).map(key => { + if (!visibleLists.lists.has(key)) { + return null + } + const result = dataGetter.get(key) + return ( + list.key === key) - ?.hideCreate ?? false - } - key={key} - listKey={key} - /> - ) - }) - })()} - - )} + hideCreate={ + data?.keystone.adminMeta.lists.find((list: any) => list.key === key) + ?.hideCreate ?? false + } + key={key} + listKey={key} + /> + ) + })} + + ) + })() + + return ( + Dashboard}> + Lists + + {stateAwareElement} + ) } + +function ListCard ({ listKey, count, hideCreate }: ListCardProps) { + const list = useList(listKey) + const countElementId = useId() + const countElement = (() => { + if (list.isSingleton) { + return null + } + + switch (count.type) { + case 'success': + return ( + + {count.count} item{count.count !== 1 ? 's' : ''} + + ) + case 'error': + return {count.message} + case 'loading': + return -- + default: + return No access + } + })() + + return ( + + + + + {list.label} + + + + {countElement} + + + {hideCreate === false && !list.isSingleton && ( + + + + + Add {list.singular.toLowerCase()} + + )} + + ) +} + +function CardLink (props: PropsWithChildren<{ href: string }>) { + const ref = useRef(null) + const { isPressed, linkProps } = useLink(props, ref) + return ( + + + + ) +} diff --git a/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/common.tsx b/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/common.tsx index e8aba4cd939..127829af6a2 100644 --- a/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/common.tsx +++ b/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/common.tsx @@ -1,115 +1,133 @@ -/** @jsxRuntime classic */ -/** @jsx jsx */ +import React, { + type HTMLAttributes, + type ReactNode, + Fragment, +} from 'react' +import { useRouter } from 'next/router' + +import { Breadcrumbs, Item } from '@keystar/ui/breadcrumbs' +import { Grid, HStack } from '@keystar/ui/layout' +import { breakpointQueries, css, tokenSchema } from '@keystar/ui/style' +import { Heading, Text } from '@keystar/ui/typography' -import { Heading, jsx, useTheme } from '@keystone-ui/core' -import { ChevronRightIcon } from '@keystone-ui/icons/icons/ChevronRightIcon' -import { Fragment, type HTMLAttributes, type ReactNode } from 'react' import { Container } from '../../../../admin-ui/components/Container' -import { Link } from '../../../../admin-ui/router' -import { type ListMeta } from '../../../../types' +import type { ListMeta } from '../../../../types' + +type ItemPageHeaderProps = { + label: string + list: ListMeta + title: string +} -export function ItemPageHeader (props: { list: ListMeta, label: string }) { - const { palette, spacing } = useTheme() +export function ItemPageHeader (props: ItemPageHeaderProps) { + const { label, list, title = label } = props + const router = useRouter() return ( - -
- {props.list.isSingleton ? ( - {props.list.label} - ) : ( - - - - {props.list.label} - - -
- -
- - {props.label} - -
- )} -
+ + {list.isSingleton ? ( + {list.label} + ) : ( + + + + {list.label} + + + {label} + + + + {/* Every page must have an H1 for accessibility. */} + + {title} + + + )} ) } export function ColumnLayout (props: HTMLAttributes) { - const { spacing } = useTheme() - return ( // this container must be relative to catch absolute children // particularly the "expanded" document-field, which needs a height of 100% - +
) } -export function BaseToolbar (props: { children: ReactNode }) { - const { colors, spacing } = useTheme() - +export function StickySidebar (props: HTMLAttributes) { return (
+ ) +} + +export function BaseToolbar (props: { children: ReactNode }) { + return ( + - {props.children} -
+ + {props.children} + + ) } diff --git a/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/index.tsx b/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/index.tsx index bdb67adf269..34d3f3d8055 100644 --- a/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/index.tsx +++ b/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/index.tsx @@ -1,48 +1,50 @@ -/** @jsxRuntime classic */ -/** @jsx jsx */ - -import copyToClipboard from 'clipboard-copy' -import { useRouter } from 'next/router' -import { +import React, { + type PropsWithChildren, + type ReactElement, Fragment, - type HTMLAttributes, memo, - type ReactElement, useCallback, useEffect, useMemo, useRef, useState, } from 'react' +import copyToClipboard from 'clipboard-copy' +import { useRouter } from 'next/router' + +import { ActionButton, Button } from '@keystar/ui/button' +import { Icon } from '@keystar/ui/icon' +import { fileWarningIcon } from '@keystar/ui/icon/icons/fileWarningIcon' +import { clipboardIcon } from '@keystar/ui/icon/icons/clipboardIcon' +import { AlertDialog, DialogContainer, DialogTrigger } from '@keystar/ui/dialog' +import { Box, Grid, VStack } from '@keystar/ui/layout' +import { Notice } from '@keystar/ui/notice' +import { ProgressCircle } from '@keystar/ui/progress' +import { SlotProvider } from '@keystar/ui/slots' +import { TextField } from '@keystar/ui/text-field' +import { toastQueue } from '@keystar/ui/toast' +import { TooltipTrigger, Tooltip } from '@keystar/ui/tooltip' +import { Heading, Text } from '@keystar/ui/typography' -import { Button } from '@keystone-ui/button' -import { Box, Center, Stack, Text, jsx, useTheme } from '@keystone-ui/core' -import { LoadingDots } from '@keystone-ui/loading' -import { ClipboardIcon } from '@keystone-ui/icons/icons/ClipboardIcon' -import { AlertDialog } from '@keystone-ui/modals' -import { Notice } from '@keystone-ui/notice' -import { useToasts } from '@keystone-ui/toast' -import { Tooltip } from '@keystone-ui/tooltip' -import { FieldLabel, TextInput } from '@keystone-ui/fields' import type { ListMeta, FieldMeta } from '../../../../types' import { type DataGetter, type DeepNullable, - makeDataGetter, - deserializeValue, type ItemData, - useInvalidFields, Fields, + deserializeValue, + makeDataGetter, useChangedFieldsAndDataForUpdate, + useInvalidFields, } from '../../../../admin-ui/utils' - import { gql, useMutation, useQuery } from '../../../../admin-ui/apollo' import { useList } from '../../../../admin-ui/context' -import { PageContainer, HEADER_HEIGHT } from '../../../../admin-ui/components/PageContainer' +import { PageContainer } from '../../../../admin-ui/components/PageContainer' import { GraphQLErrorNotice } from '../../../../admin-ui/components/GraphQLErrorNotice' import { usePreventNavigation } from '../../../../admin-ui/utils/usePreventNavigation' import { CreateButtonLink } from '../../../../admin-ui/components/CreateButtonLink' -import { BaseToolbar, ColumnLayout, ItemPageHeader } from './common' +import { ErrorDetailsDialog } from '../../../../admin-ui/components/Errors' +import { BaseToolbar, ColumnLayout, ItemPageHeader, StickySidebar } from './common' type ItemPageProps = { listKey: string @@ -80,11 +82,11 @@ function ItemForm ({ item: ItemData }) { const list = useList(listKey) - const { spacing, typography } = useTheme() + const [errorDialogValue, setErrorDialogValue] = useState(null) const [update, { loading, error, data }] = useMutation( - gql`mutation ($data: ${list.gqlNames.updateInputName}!, $id: ID!) { - item: ${list.gqlNames.updateMutationName}(where: { id: $id }, data: $data) { + gql`mutation ($data: ${list.graphql.names.updateInputName}!, $id: ID!) { + item: ${list.graphql.names.updateMutationName}(where: { id: $id }, data: $data) { ${selectedFields} } }`, @@ -119,8 +121,8 @@ function ItemForm ({ const invalidFields = useInvalidFields(list.fields, state.value) const [forceValidation, setForceValidation] = useState(false) - const toasts = useToasts() - const onSave = useEventCallback(() => { + const onSave = useEventCallback((e) => { + e.preventDefault() const newForceValidation = invalidFields.size !== 0 setForceValidation(newForceValidation) if (newForceValidation) return @@ -134,55 +136,89 @@ function ItemForm ({ // update the item, path being undefined generally indicates a failure in the graphql mutation itself - ie a type error const error = errors?.find(x => x.path === undefined || x.path?.length === 1) if (error) { - toasts.addToast({ - title: 'Failed to update item', - tone: 'negative', - message: error.message, + toastQueue.critical('Unable to save item', { + actionLabel: 'Details', + onAction: () => setErrorDialogValue(new Error(error.message)), + shouldCloseOnAction: true, }) } else { - toasts.addToast({ - // title: data.item[list.labelField] || data.item.id, - tone: 'positive', - title: 'Saved successfully', - // message: 'Saved successfully', + // do we really need a toast for this? the item _should_ save… + toastQueue.positive(`Saved changes to ${list.singular.toLocaleLowerCase()}`, { + timeout: 5000, }) } }) .catch(err => { - toasts.addToast({ title: 'Failed to update item', tone: 'negative', message: err.message }) + toastQueue.critical('Unable to save item', { + actionLabel: 'Details', + onAction: () => setErrorDialogValue(err), + shouldCloseOnAction: true, + }) }) }) const labelFieldValue = list.isSingleton ? list.label : state.item.data?.[list.labelField] const itemId = state.item.data?.id const hasChangedFields = !!changedFields.size usePreventNavigation(useMemo(() => ({ current: hasChangedFields }), [hasChangedFields])) + return ( - - x.path?.length === 1)} - /> - { - setValue(state => ({ item: state.item, value: value(state.value) })) - }, - [setValue] - )} - value={state.value} - /> +
+ {/* + Workaround for react-aria "bug" where pressing enter in a form field + moves focus to the submit button. + See: https://github.com/adobe/react-spectrum/issues/5940 + */} + - )} - -
- - { - setValue(state => ({ item: state.item, value: value(state.value) })) - }, - [setValue] - )} - value={state.value} - /> - - + + + setErrorDialogValue(null)} isDismissable> + {errorDialogValue && } + ) } +const COPY_TOOLTIP_CONTENT = { + neutral: 'Copy ID', + positive: 'Copied to clipboard', + critical: 'Unable to copy', +} +type TooltipState = { isOpen?: boolean; tone: keyof typeof COPY_TOOLTIP_CONTENT } +function IdField ({ itemId }: { itemId: string }) { + const [tooltipState, setTooltipState] = useState({ tone:'neutral' }) + + const onCopy = useCallback(async () => { + try { + await copyToClipboard(itemId) + setTooltipState({ isOpen: true, tone: 'positive' }) + } catch (err: any) { + setTooltipState({ isOpen: true, tone: 'critical' }) + } + + // close, then reset the tooltip state after a delay + setTimeout(() => { + setTooltipState(state => ({ ...state, isOpen: false })) + }, 2000) + setTimeout(() => { + setTooltipState({ isOpen: undefined, tone: 'neutral' }) + }, 2300) + }, [itemId]) + + return ( + + { + if (target instanceof HTMLInputElement) { + target.select() + } + }} + /> + + + + + + {COPY_TOOLTIP_CONTENT[tooltipState.tone]} + + + + ) +} + function DeleteButton ({ itemLabel, itemId, @@ -267,65 +307,58 @@ function DeleteButton ({ itemId: string list: ListMeta }) { - const toasts = useToasts() - const [deleteItem, { loading }] = useMutation( + const [errorDialogValue, setErrorDialogValue] = useState(null) + const router = useRouter() + const [deleteItem] = useMutation( gql`mutation ($id: ID!) { - ${list.gqlNames.deleteMutationName}(where: { id: $id }) { + ${list.graphql.names.deleteMutationName}(where: { id: $id }) { id } }`, { variables: { id: itemId } } ) - const [isOpen, setIsOpen] = useState(false) - const router = useRouter() return ( - - { - try { - await deleteItem() - } catch (err: any) { - return toasts.addToast({ - title: `Failed to delete ${list.singular} item: ${itemLabel}`, - message: err.message, - tone: 'negative', - }) - } - router.push(list.isSingleton ? '/' : `/${list.path}`) - return toasts.addToast({ - title: itemLabel, - message: `Deleted ${list.singular} item successfully`, - tone: 'positive', + + + { + try { + await deleteItem() + } catch (err: any) { + toastQueue.critical('Unable to delete item.', { + actionLabel: 'Details', + onAction: () => { + setErrorDialogValue(err) + }, + shouldCloseOnAction: true, }) - }, - loading, - }, - cancel: { - label: 'Cancel', - action: () => { - setIsOpen(false) - }, - }, - }} - > - Are you sure you want to delete {itemLabel}? - + return + } + + toastQueue.neutral(`${list.singular} deleted.`, { + timeout: 5000, + }) + router.push(list.isSingleton ? '/' : `/${list.path}`) + }} + > + + Are you sure you want to delete “{itemLabel}”? + This action cannot be undone. + + + + + setErrorDialogValue(null)} isDismissable> + {errorDialogValue && } + ) } @@ -351,7 +384,7 @@ function ItemPage ({ listKey }: ItemPageProps) { selectedFields, query: gql` query ItemPage($id: ID!, $listKey: String!) { - item: ${list.gqlNames.itemQueryName}(where: {id: $id}) { + item: ${list.graphql.names.itemQueryName}(where: {id: $id}) { ${selectedFields} } keystone { @@ -419,11 +452,8 @@ function ItemPage ({ listKey }: ItemPageProps) { const pageLoading = loading || id === undefined const metaQueryErrors = dataGetter.get('keystone').errors - const pageTitle: string = list.isSingleton - ? list.label - : pageLoading - ? undefined - : (data && data.item && (data.item[list.labelField] || data.item.id)) || id + const pageLabel = (data && data.item && (data.item[list.labelField] || data.item.id)) || id + const pageTitle: string = list.isSingleton ? list.label : pageLoading ? undefined : pageLabel return ( } > {pageLoading ? ( -
- -
+ + + ) : metaQueryErrors ? ( - {metaQueryErrors[0].message} + {metaQueryErrors[0].message} ) : ( @@ -458,19 +485,19 @@ function ItemPage ({ listKey }: ItemPageProps) { /> ) : list.isSingleton ? ( id === '1' ? ( - - - {list.label} doesn't exist or you don't have access to it. - + + “{list.label}” doesn’t exist, or you don’t have access to it. {!data.keystone.adminMeta.list!.hideCreate && } - + ) : ( - The item with id "{id}" does not exist + + An item with ID “{id}” does not exist. + ) ) : ( - - The item with id "{id}" could not be found or you don't have access to it. - + + The item with ID “{id}” doesn’t exist, or you don’t have access to it. + )} ) : ( @@ -495,89 +522,68 @@ function ItemPage ({ listKey }: ItemPageProps) { // Styled Components // ------------------------------ +function ItemNotFound (props: PropsWithChildren<{}>) { + return ( + + + Not found + + {props.children} + + + ) +} + const Toolbar = memo(function Toolbar ({ hasChangedFields, loading, - onSave, onReset, deleteButton, }: { hasChangedFields: boolean loading: boolean - onSave: () => void onReset: () => void deleteButton?: ReactElement }) { return ( - - {hasChangedFields ? ( - - ) : ( - - No changes - - )} - {deleteButton} - + + + {deleteButton} ) }) -function ResetChangesButton (props: { onReset: () => void }) { - const [isConfirmModalOpen, setConfirmModalOpen] = useState(false) - +function ResetButton (props: { onReset: () => void, hasChanges?: boolean }) { return ( - - props.onReset(), - label: 'Reset changes', - }, - cancel: { - action: () => setConfirmModalOpen(false), - label: 'Cancel', - }, - }} - isOpen={isConfirmModalOpen} - title="Are you sure you want to reset changes?" - tone="negative" + title="Reset changes" + cancelLabel="Cancel" + primaryActionLabel="Yes, reset" + autoFocusButton="primary" + onPrimaryAction={props.onReset} > - {null} + Are you sure? Lost changes cannot be recovered. - - ) -} - -function StickySidebar (props: HTMLAttributes) { - const { spacing } = useTheme() - return ( -
+ ) } diff --git a/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/FieldSelection.tsx b/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/FieldSelection.tsx index fcfd317dfba..12a4437085c 100644 --- a/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/FieldSelection.tsx +++ b/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/FieldSelection.tsx @@ -1,53 +1,32 @@ -/** @jsxRuntime classic */ -/** @jsx jsx */ -import { Button } from '@keystone-ui/button' -import { Box, jsx } from '@keystone-ui/core' -import { ChevronDownIcon } from '@keystone-ui/icons/icons/ChevronDownIcon' -import { Options, OptionPrimitive, CheckMark } from '@keystone-ui/options' -import { Popover } from '@keystone-ui/popover' +import React, { + type Key, + useMemo, +} from 'react' import { useRouter } from 'next/router' -import { type ListMeta } from '../../../../types' -import { useSelectedFields } from './useSelectedFields' -function isArrayEqual (arrA: string[], arrB: string[]) { - if (arrA.length !== arrB.length) return false - for (let i = 0; i < arrA.length; i++) { - if (arrA[i] !== arrB[i]) { - return false - } - } - return true -} +import { ActionButton } from '@keystar/ui/button' +import { Icon } from '@keystar/ui/icon' +import { chevronDownIcon } from '@keystar/ui/icon/icons/chevronDownIcon' +import { MenuTrigger, Menu, Item } from '@keystar/ui/menu' +import { Text } from '@keystar/ui/typography' -const Option: typeof OptionPrimitive = props => { - return ( - - {props.children} - - - ) -} - -// TODO: return type required by pnpm :( -export const fieldSelectionOptionsComponents: Parameters[0]['components'] = { - Option, -} +import type { ListMeta } from '../../../../types' +import { useSelectedFields } from './useSelectedFields' export function FieldSelection ({ - list, fieldModesByFieldPath, + isDisabled, + list, }: { - list: ListMeta fieldModesByFieldPath: Record + isDisabled?: boolean + list: ListMeta }) { const router = useRouter() const selectedFields = useSelectedFields(list, fieldModesByFieldPath) - const setNewSelectedFields = (selectedFields: string[]) => { + const setNewSelectedFields = (selectedFields: Key[]) => { + // Clear the `fields` query param when selection matches initial columns if (isArrayEqual(selectedFields, list.initialColumns)) { const { fields: _ignore, ...otherQueryFields } = router.query router.push({ query: otherQueryFields }) @@ -55,45 +34,50 @@ export function FieldSelection ({ router.push({ query: { ...router.query, fields: selectedFields.join(',') } }) } } - const fields: { value: string, label: string, isDisabled: boolean }[] = [] - Object.keys(fieldModesByFieldPath).forEach(fieldPath => { - if (fieldModesByFieldPath[fieldPath] === 'read') { - fields.push({ + + const fields = useMemo(() => { + return Object.keys(fieldModesByFieldPath) + .filter(fieldPath => fieldModesByFieldPath[fieldPath] === 'read') + .map(fieldPath => ({ value: fieldPath, label: list.fields[fieldPath].label, isDisabled: selectedFields.size === 1 && selectedFields.has(fieldPath), - }) - } - }) + })) + }, [fieldModesByFieldPath, list.fields, selectedFields]) return ( - { - return ( - - ) - }} - > -
- - { - if (!Array.isArray(options)) return - setNewSelectedFields(options.map(x => x.value)) - }} - isMulti - value={fields.filter(option => selectedFields.has(option.value))} - options={fields} - components={fieldSelectionOptionsComponents} - /> - -
-
+ + + Columns + + + { + if (selection === 'all') { + setNewSelectedFields(fields.map(field => field.value)) + } else { + setNewSelectedFields(Array.from(selection)) + } + }} + selectionMode="multiple" + selectedKeys={selectedFields} + > + {item => ( + {item.label} + )} + + ) } + +function isArrayEqual (arrA: Key[], arrB: Key[]) { + if (arrA.length !== arrB.length) return false + for (let i = 0; i < arrA.length; i++) { + if (arrA[i] !== arrB[i]) { + return false + } + } + return true +} diff --git a/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/FilterAdd.tsx b/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/FilterAdd.tsx index 6fb2180210e..8d36f91ddf6 100644 --- a/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/FilterAdd.tsx +++ b/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/FilterAdd.tsx @@ -1,93 +1,178 @@ -/** @jsxRuntime classic */ -/** @jsx jsx */ +import { useRouter } from 'next/router' +import React, { type FormEvent, Fragment, useMemo, useState, useRef } from 'react' -import { type ComponentProps, Fragment, type FormEvent, useMemo, useState } from 'react' -import { Button } from '@keystone-ui/button' -import { Box, Divider, Heading, Stack, VisuallyHidden, jsx, useTheme } from '@keystone-ui/core' -import { Select } from '@keystone-ui/fields' -import { ChevronLeftIcon } from '@keystone-ui/icons/icons/ChevronLeftIcon' -import { ChevronRightIcon } from '@keystone-ui/icons/icons/ChevronRightIcon' -import { ChevronDownIcon } from '@keystone-ui/icons/icons/ChevronDownIcon' -import { OptionPrimitive, Options } from '@keystone-ui/options' -import { PopoverDialog, usePopover } from '@keystone-ui/popover' +import { ActionButton, ButtonGroup, Button } from '@keystar/ui/button' +import { Dialog, DialogTrigger } from '@keystar/ui/dialog' +import { Icon } from '@keystar/ui/icon' +import { chevronDownIcon } from '@keystar/ui/icon/icons/chevronDownIcon' +import { Grid } from '@keystar/ui/layout' +import { MenuTrigger, Menu, Item } from '@keystar/ui/menu' +import { Picker } from '@keystar/ui/picker' +import { Content } from '@keystar/ui/slots' +import { Heading, Text } from '@keystar/ui/typography' import { type FieldMeta, type JSONValue } from '../../../../types' import { useList } from '../../../../admin-ui/context' -import { useRouter } from '../../../../admin-ui/router' type State = | { kind: 'selecting-field' } | { kind: 'filter-value', fieldPath: string, filterType: string, filterValue: JSONValue } -const fieldSelectComponents: ComponentProps['components'] = { - Option: ({ children, ...props }) => { - const theme = useTheme() - const iconColor = props.isFocused ? theme.colors.foreground : theme.colors.foregroundDim - return ( - - {children} -
- -
-
- ) - }, -} -export function FilterAdd ({ - listKey, - filterableFields, -}: { - listKey: string +export function FilterAdd (props: { filterableFields: Set + isDisabled?: boolean + listKey: string }) { - const { isOpen, setOpen, trigger, dialog, arrow } = usePopover({ - placement: 'bottom', - modifiers: [{ name: 'offset', options: { offset: [0, 8] } }], - }) + const triggerRef = useRef(null) + const [state, setState] = useState({ kind: 'selecting-field' }) + const [forceValidation, setForceValidation] = useState(false) + const router = useRouter() + + const { fieldsWithFilters, filtersByFieldThenType, list } = useFilterFields(props) + const resetState = () => { + setState({ kind: 'selecting-field' }) + setForceValidation(false) + // This is a bit of a hack to ensure the trigger button is focused after the + // dialog closes, since we're forking the render + setTimeout(() => { + triggerRef?.current?.focus() + }, 200) + } + const onSubmit = (event: FormEvent) => { + event.preventDefault() + setForceValidation(true) + + if (state.kind !== 'filter-value') return + if ((state.filterType !== 'empty' && state.filterType !== 'not_empty') && state.filterValue == null) { + return + } + + router.push({ + query: { + ...router.query, + [`!${state.fieldPath}_${state.filterType}`]: JSON.stringify(state.filterValue), + }, + }) + resetState() + } + + if (state.kind === 'filter-value') { + const { Filter } = fieldsWithFilters[state.fieldPath].controller.filter + const fieldLabel = list.fields[state.fieldPath].label + const filterTypes = filtersByFieldThenType[state.fieldPath] + const typeLabel = filterTypes[state.filterType] + return ( + !isOpen && resetState()}> + + Filter + + + +
+ {/* + Workaround for react-aria "bug" where pressing enter in a form field + moves focus to the submit button. + See: https://github.com/adobe/react-spectrum/issues/5940 + */} + + + +
+
+
+ ) + } return ( - - - {isOpen && ( - { - setOpen(false) - }} - listKey={listKey} - filterableFields={filterableFields} - /> - )} - + + + Filter + + + ({ + label: fieldsWithFilters[fieldPath].label, + value: fieldPath, + }))} + onAction={fieldPath => { + const filterType = Object.keys(filtersByFieldThenType[fieldPath])[0] + setState({ + kind: 'filter-value', + fieldPath: fieldPath as string, + filterType, + filterValue: + fieldsWithFilters[fieldPath].controller.filter.types[filterType].initialValue, + }) + }} + > + {item => ( + {item.label} + )} + + ) } -function FilterAddPopoverContent ({ - onClose, +function useFilterFields ({ listKey, filterableFields, }: { - onClose: () => void listKey: string filterableFields: Set }) { @@ -124,126 +209,10 @@ function FilterAddPopoverContent ({ }) return filtersByFieldThenType }, [router.query, fieldsWithFilters]) - const [state, setState] = useState({ kind: 'selecting-field' }) - return ( - { - event.preventDefault() - if (state.kind === 'filter-value') { - router.push({ - query: { - ...router.query, - [`!${state.fieldPath}_${state.filterType}`]: JSON.stringify(state.filterValue), - }, - }) - onClose() - } - }} - gap="small" - > -
- {state.kind !== 'selecting-field' && ( - - )} - - {(() => { - switch (state.kind) { - case 'selecting-field': { - return 'Filter' - } - case 'filter-value': { - return list.fields[state.fieldPath].label - } - } - })()} - -
- - {state.kind === 'selecting-field' && ( - { - const fieldPath: string = (newVal as any).value - const filterType = Object.keys(filtersByFieldThenType[fieldPath])[0] - setState({ - kind: 'filter-value', - fieldPath, - filterType, - filterValue: - fieldsWithFilters[fieldPath].controller.filter.types[filterType].initialValue, - }) - }} - options={Object.keys(filtersByFieldThenType).map(fieldPath => ({ - label: fieldsWithFilters[fieldPath].label, - value: fieldPath, - }))} - /> - )} - {state.kind === 'filter-value' && ( - - + + {inputProps => ( + + + + + )} + ) } -function FileView ({ - errorMessage, - value, - onChange, - inputRef, -}: { - errorMessage?: string - value: FileValue +function FileView(props: { + onFileTrigger: () => void + isInvalid?: boolean onChange?: (value: FileValue) => void - inputRef: RefObject + value: FileValue }) { - return value.kind === 'from-server' || value.kind === 'upload' ? ( - - {onChange && ( - - {value.kind === 'from-server' && ( - - -
- {`${value.data.filename}`} - - - Size: {bytes(value.data.filesize)} - - )} - {value.kind === 'upload' && ( - - - File linked, save to complete upload - - Size: {bytes(value.data.file.size)} - + const { isInvalid, onFileTrigger, onChange, value } = props + const fileData = useFileData(value) + + return ( + + {isInvalid || !fileData ? null : ( + + {onChange && ( + + Change + {value.kind === 'from-server' && ( + { + onChange({ kind: 'remove', previous: value }) + }} + > + Remove + + )} + {value.kind === 'upload' && ( + { + onChange(value.previous) + }} + > + Cancel + + )} + )} - - - {value.kind === 'from-server' && ( - - )} - {value.kind === 'upload' && ( - - )} - {errorMessage && ( - - {errorMessage} - - )} - - + Undo removal + + )} + )} - - ) : ( - - - - {value.kind === 'remove' && value.previous && ( - - )} - - + + ) +} + +function FileDetails(props: PropsWithChildren) { + const trimStartStyles = useTrimStartStyles() + + return ( + + + + + + + + {props.name} + + + + {formatBytes(props.size)} + + + {/* field controls dependant on value type */} + {props.children} + + ) } -function createErrorMessage (value: FileValue) { +export function formatBytes(size: number, options = defaultBytesOptions(size)) { + return bytes(size, options) +} + +function defaultBytesOptions(size: number): BytesOptions { + // increase precision for larger files + const GB = 1_000_000_000 + const MB = 1_000_000 + const decimalPlaces = size >= GB ? 2 : size >= MB ? 1 : 0 + + return { decimalPlaces } +} + +type FileData = { + icon: ReactElement + name: string + size: number +} + +function useFileData(value: FileValue): FileData | null { + switch (value.kind) { + case 'from-server': + return { + icon: getFileIcon(value.data.filename), + name: value.data.filename, + size: value.data.filesize, + } + case 'upload': + return { + icon: fileUpIcon, + name: value.data.file.name, + size: value.data.file.size, + } + default: + return null + } +} + +// prettier-ignore +const FILE_TYPES = { + audio: new Set(['aac', 'aiff', 'alac', 'flac', 'm4a', 'mp3', 'ogg', 'wav', 'wma']), + binary: new Set(['bin', 'dat', 'exe', 'iso', 'pkg', 'rar', 'tar', 'zip']), + code: new Set(['c', 'cpp', 'cs', 'css', 'go', 'html', 'java', 'js', 'jsx', 'less', 'php', 'py', 'rb', 'rs', 'scss', 'ts', 'tsx', 'xml']), + image: new Set(['avif', 'bmp', 'gif', 'heic', 'ico', 'jpeg', 'jpg', 'png', 'svg', 'tiff', 'webp']), + json: new Set(['geojson', 'json', 'json5', 'jsonld']), + spreadsheet: new Set(['csv', 'ods', 'tsv', 'xls', 'xlsx']), + text: new Set(['doc', 'docx', 'eml', 'log', 'md', 'msg', 'odt', 'pdf', 'rtf', 'tex', 'txt']), + video: new Set(['avi', 'flv', 'm4v', 'mkv', 'mov', 'mp4', 'ogg', 'webm', 'wmv']), +} + +function getFileIcon(filename: string) { + const extension = getExtension(filename) + + if (FILE_TYPES.audio.has(extension)) return fileAudioIcon + if (FILE_TYPES.binary.has(extension)) return fileDigitIcon + if (FILE_TYPES.code.has(extension)) return fileCodeIcon + if (FILE_TYPES.image.has(extension)) return fileImageIcon + if (FILE_TYPES.json.has(extension)) return fileJsonIcon + if (FILE_TYPES.spreadsheet.has(extension)) return fileSpreadsheetIcon + if (FILE_TYPES.text.has(extension)) return fileTextIcon + if (FILE_TYPES.video.has(extension)) return fileVideoIcon + + return fileIcon +} +function getExtension(filename: string) { + // `extname` returns e.g. ".mov", remove leading dot to match "mov" + return extname(filename).slice(1).toLowerCase() +} + +export function useTrimStartStyles() { + const { direction } = useLocale() + return { + direction: direction === 'rtl' ? 'ltr' : 'rtl', + display: 'block', + overflow: 'hidden', + textAlign: direction === 'rtl' ? 'right' : 'left', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + } as const +} + +function createErrorMessage(value: FileValue) { if (value.kind === 'upload') { return validateFile(value.data) } } -export function validateFile ({ validity }: { validity: ValidityState }): string | undefined { +export function validateFile({ validity }: { validity: ValidityState }) { if (!validity.valid) { return 'Something went wrong, please reload and try again.' } diff --git a/packages/core/src/fields/types/file/views/index.tsx b/packages/core/src/fields/types/file/views/index.tsx index c6854fb85ee..5f0a6d6eacb 100644 --- a/packages/core/src/fields/types/file/views/index.tsx +++ b/packages/core/src/fields/types/file/views/index.tsx @@ -2,9 +2,7 @@ /** @jsx jsx */ import { jsx } from '@keystone-ui/core' -import { FieldContainer, FieldLabel } from '@keystone-ui/fields' import { - type CardValueComponent, type CellComponent, type FieldController, type FieldControllerConfig, @@ -32,16 +30,6 @@ export const Cell: CellComponent = ({ item, field }) => { ) } -export const CardValue: CardValueComponent = ({ item, field }) => { - const data = item[field.path] - return ( - - {field.label} - {data && data.filename} - - ) -} - type FileData = { src: string filesize: number diff --git a/packages/core/src/fields/types/float/index.ts b/packages/core/src/fields/types/float/index.ts index 400d3272cf4..c3d8b341d59 100644 --- a/packages/core/src/fields/types/float/index.ts +++ b/packages/core/src/fields/types/float/index.ts @@ -1,4 +1,3 @@ -// Float in GQL: A signed double-precision floating-point value. import { type BaseListTypeInfo, type FieldTypeFunc, @@ -13,12 +12,12 @@ import { mergeFieldHooks } from '../../resolve-hooks' export type FloatFieldConfig = CommonFieldConfig & { - defaultValue?: number isIndexed?: boolean | 'unique' + defaultValue?: number | null validation?: { + isRequired?: boolean min?: number max?: number - isRequired?: boolean } db?: { isNullable?: boolean @@ -29,7 +28,7 @@ export type FloatFieldConfig = export function float (config: FloatFieldConfig = {}): FieldTypeFunc { const { - defaultValue, + defaultValue: defaultValue_, isIndexed, validation = {}, } = config @@ -39,15 +38,16 @@ export function float (config: FloatFiel min, max } = validation + const defaultValue = defaultValue_ ?? null return (meta) => { - if (defaultValue !== undefined && (typeof defaultValue !== 'number' || !Number.isFinite(defaultValue))) { + if (defaultValue !== null && !Number.isFinite(defaultValue)) { throw new Error(`${meta.listKey}.${meta.fieldKey} specifies a default value of: ${defaultValue} but it must be a valid finite number`) } - if (min !== undefined && (typeof min !== 'number' || !Number.isFinite(min))) { + if (min !== undefined && !Number.isFinite(min)) { throw new Error(`${meta.listKey}.${meta.fieldKey} specifies validation.min: ${min} but it must be a valid finite number`) } - if (max !== undefined && (typeof max !== 'number' || !Number.isFinite(max))) { + if (max !== undefined && !Number.isFinite(max)) { throw new Error(`${meta.listKey}.${meta.fieldKey} specifies validation.max: ${max} but it must be a valid finite number`) } if ( @@ -82,7 +82,10 @@ export function float (config: FloatFiel mode, scalar: 'Float', index: isIndexed === true ? 'index' : isIndexed || undefined, - default: typeof defaultValue === 'number' ? { kind: 'literal', value: defaultValue } : undefined, + default: + typeof defaultValue === 'number' + ? { kind: 'literal', value: defaultValue } + : undefined, map: config.db?.map, extendPrismaSchema: config.db?.extendPrismaSchema, })({ @@ -100,18 +103,14 @@ export function float (config: FloatFiel defaultValue: typeof defaultValue === 'number' ? defaultValue : undefined, }), resolve (value) { - if (value === undefined) { - return defaultValue ?? null - } + if (value === undefined) return defaultValue return value }, }, update: { arg: graphql.arg({ type: graphql.Float }) }, orderBy: { arg: graphql.arg({ type: orderDirectionEnum }) }, }, - output: graphql.field({ - type: graphql.Float, - }), + output: graphql.field({ type: graphql.Float, }), __ksTelemetryFieldTypeName: '@keystone-6/float', views: '@keystone-6/core/fields/types/float/views', getAdminMeta () { @@ -121,7 +120,7 @@ export function float (config: FloatFiel min: min ?? null, max: max ?? null, }, - defaultValue: defaultValue ?? null, + defaultValue } }, }) diff --git a/packages/core/src/fields/types/float/views/index.tsx b/packages/core/src/fields/types/float/views/index.tsx index 1ea8b2137fb..9b36f56e944 100644 --- a/packages/core/src/fields/types/float/views/index.tsx +++ b/packages/core/src/fields/types/float/views/index.tsx @@ -1,271 +1,192 @@ -/** @jsxRuntime classic */ -/** @jsx jsx */ +import React, { useState } from 'react' + +import { TextField } from '@keystar/ui/text-field' +import { Text } from '@keystar/ui/typography' -import { jsx } from '@keystone-ui/core' -import { FieldContainer, FieldDescription, FieldLabel, TextInput } from '@keystone-ui/fields' -import { useState } from 'react' import { - type CardValueComponent, type CellComponent, type FieldController, type FieldControllerConfig, type FieldProps, } from '../../../../types' -import { CellLink, CellContainer } from '../../../../admin-ui/components' -import { useFormattedInput } from '../../integer/views/utils' -type Validation = { - min?: number - max?: number - isRequired?: boolean +const TYPE_OPERATOR_MAP = { + equals: '=', + not: '≠', + gt: '>', + lt: '<', + gte: '≥', + lte: '≤', +} as const + +export const Cell: CellComponent = ({ field, item }) => { + const value = item[field.path] + return value != null ? {value.toString()} : null } type Value = - | { kind: 'update', initial: number | null, value: string | number | null } - | { kind: 'create', value: string | number | null } - -function validate (value: Value, validation: Validation, label: string) { - const val = value.value - - // if we recieve null initially on the item view and the current value is null, - // we should always allow saving it because: - // - the value might be null in the database and we don't want to prevent saving the whole item because of that - // - we might have null because of an access control error - if (value.kind === 'update' && value.initial === null && val === null) { - return undefined - } - - if (value.kind === 'create' && value.value === null) { - return undefined - } - - if (validation.isRequired && val === null) { - return `${label} is required` - } - - // we don't parse infinite numbers into +-Infinity/NaN so that we don't lose the text that the user wrote - // so we need to try parsing it again here to provide good messages - if (typeof val === 'string') { - const number = parseFloat(val) - if (isNaN(number)) { - return `${label} must be a number` - } - return `${label} must be finite` - } - - if (typeof val === 'number') { - if (typeof validation?.min === 'number' && val < validation.min) { - return `${label} must be greater than or equal to ${validation.min}` - } - if (typeof validation?.max === 'number' && val > validation?.max) { - return `${label} must be less than or equal to ${validation.max}` - } - } - - return undefined -} - -function FloatInput ({ - value, - onChange, - id, - autoFocus, - forceValidation, - validationMessage, - placeholder, -}: { - id: string - autoFocus?: boolean - value: number | string | null - onChange: (value: number | string | null) => void - forceValidation?: boolean - validationMessage?: string - placeholder?: string -}) { - const [hasBlurred, setHasBlurred] = useState(false) - const props = useFormattedInput( - { - format: value => (value === null ? '' : value.toString()), - parse: raw => { - raw = raw.trim() - if (raw === '') { - return null - } - const parsed = parseFloat(raw) - if (Number.isFinite(parsed)) { - return parsed - } - return raw - }, - }, - { - value, - onChange, - onBlur: () => { - setHasBlurred(true) - }, - } - ) - - return ( - - - {(hasBlurred || forceValidation) && validationMessage && ( - {validationMessage} - )} - - ) -} - -export const Field = ({ - field, - value, - onChange, - autoFocus, - forceValidation, -}: FieldProps) => { - const message = validate(value, field.validation, field.label) - return ( - - {field.label} - {field.description} - {onChange ? ( - - { - onChange({ ...value, value: val }) - }} - aria-describedby={field.description === null ? undefined : `${field.path}-description`} - value={value.value} - forceValidation={forceValidation} - validationMessage={message} - /> - - ) : ( - value.value - )} - - ) -} + | { kind: 'create', value: string | null } + | { kind: 'update', initial: string | null, value: string | null } -export const Cell: CellComponent = ({ item, field, linkTo }) => { - const value = item[field.path] + '' - return linkTo ? {value} : {value} +type Validation = { + isRequired: boolean + min: string + max: string } -Cell.supportsLinkTo = true -export const CardValue: CardValueComponent = ({ item, field }) => { - return ( - - {field.label} - {item[field.path]} - - ) +function validate_ ( + value: Value, + validation: Validation, + label: string, +): string | undefined { + const { + value: input, + kind, + } = value + if (kind === 'update' && value.initial === null && input === null) return + if (validation.isRequired && input === null) return `${label} is required` + if (typeof input !== 'string') return + const v = parseFloat(input) + if (Number.isNaN(v)) return `${label} is not a valid float` + if (validation.min !== undefined && v < parseFloat(validation.min)) return `${label} must be greater than or equal to ${validation.min}` + if (validation.max !== undefined && v > parseFloat(validation.max)) return `${label} must be less than or equal to ${validation.max}` } -export const controller = ( - config: FieldControllerConfig<{ validation: Validation, defaultValue: number | null }> -): FieldController & { +export function controller ( + config: FieldControllerConfig<{ + validation: Validation + defaultValue: string | null + }> +): FieldController & { validation: Validation -} => { +} { + const validate = (value: Value) => { + return validate_( + value, + config.fieldMeta.validation, + config.label, + ) + } + return { path: config.path, label: config.label, description: config.description, graphqlSelection: config.path, validation: config.fieldMeta.validation, - defaultValue: { - kind: 'create', - value: config.fieldMeta.defaultValue, + defaultValue: { kind: 'create', value: config.fieldMeta.defaultValue, }, + deserialize: data => ({ kind: 'update', value: data[config.path], initial: data[config.path] }), + serialize: value => { + const v = value.value !== null ? parseFloat(value.value) : null + return { [config.path]: Number.isFinite(v) ? v : null } }, - deserialize: data => ({ - kind: 'update', - initial: data[config.path], - value: data[config.path], - }), - serialize: value => ({ [config.path]: value.value }), - validate: value => validate(value, config.fieldMeta.validation, config.label) === undefined, filter: { - Filter ({ autoFocus, type, onChange, value }) { + Filter (props) { + const { autoFocus, context, forceValidation, typeLabel, onChange, type, value, ...otherProps } = props + const [isDirty, setDirty] = useState(false) + if (type === 'empty' || type === 'not_empty') return null + + const labelProps = context === 'add' + ? { label: config.label, description: typeLabel } + : { label: typeLabel } + return ( - { - if (type === 'in' || type === 'not_in') { - onChange(event.target.value.replace(/[^\d.,\s-]/g, '')) - return - } - onChange(event.target.value.replace(/[^\d.\s-]/g, '')) - }} - value={value} + setDirty(true)} + onChange={x => onChange?.(x === '' ? null : x)} + value={value ?? ''} /> ) }, graphql: ({ type, value }) => { - const valueWithoutWhitespace = value.replace(/\s/g, '') - const parsed = - type === 'in' || type === 'not_in' - ? valueWithoutWhitespace.split(',').map(x => parseFloat(x)) - : parseFloat(valueWithoutWhitespace) - if (type === 'not') { - return { [config.path]: { not: { equals: parsed } } } - } - const key = type === 'is' ? 'equals' : type === 'not_in' ? 'notIn' : type - return { [config.path]: { [key]: parsed } } + if (type === 'empty') return { [config.path]: { equals: null } } + if (type === 'not_empty') return { [config.path]: { not: { equals: null } } } + if (type === 'not') return { [config.path]: { not: { equals: value } } } + return { [config.path]: { [type]: value } } }, - Label ({ label, value, type }) { - let renderedValue = value - if (['in', 'not_in'].includes(type)) { - renderedValue = value - .split(',') - .map(value => value.trim()) - .join(', ') - } - return `${label.toLowerCase()}: ${renderedValue}` + Label ({ label, type, value }) { + if (type === 'empty' || type === 'not_empty') return label.toLocaleLowerCase() + const operator = TYPE_OPERATOR_MAP[type as keyof typeof TYPE_OPERATOR_MAP] + return `${operator} ${value}` }, types: { - is: { + equals: { label: 'Is exactly', - initialValue: '', + initialValue: null, }, not: { label: 'Is not exactly', - initialValue: '', + initialValue: null, }, gt: { label: 'Is greater than', - initialValue: '', + initialValue: null, }, lt: { label: 'Is less than', - initialValue: '', + initialValue: null, }, gte: { label: 'Is greater than or equal to', - initialValue: '', + initialValue: null, }, lte: { label: 'Is less than or equal to', - initialValue: '', + initialValue: null, }, - in: { - label: 'Is one of', - initialValue: '', + empty: { + label: 'Is empty', + initialValue: null, }, - not_in: { - label: 'Is not one of', - initialValue: '', + not_empty: { + label: 'Is not empty', + initialValue: null, }, }, }, + + validate: value => validate(value) === undefined, } } + +export function Field ({ + field, + value, + onChange, + autoFocus, + forceValidation, +}: FieldProps) { + const [isDirty, setDirty] = useState(false) + const isReadOnly = !onChange + + const validate = (value: Value) => { + return validate_( + value, + field.validation, + field.label, + ) + } + + return ( + setDirty(true)} + onChange={x => onChange?.({ ...value, value: x === '' ? null : x })} + value={value.value ?? ''} + /> + ) +} diff --git a/packages/core/src/fields/types/image/views/Field.tsx b/packages/core/src/fields/types/image/views/Field.tsx index cb61545679b..d4496b7922c 100644 --- a/packages/core/src/fields/types/image/views/Field.tsx +++ b/packages/core/src/fields/types/image/views/Field.tsx @@ -1,43 +1,34 @@ -/** @jsxRuntime classic */ -/** @jsx jsx */ +import React, { + type PropsWithChildren, + type SyntheticEvent, + Fragment, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react' + +import { ActionButton } from '@keystar/ui/button' +import { Field as KeystarField } from '@keystar/ui/field' +import { HStack, VStack } from '@keystar/ui/layout' +import { css, tokenSchema, transition } from '@keystar/ui/style' +import { Text } from '@keystar/ui/typography' -import bytes from 'bytes' -import { Fragment, type ReactNode, type RefObject, useEffect, useMemo, useRef, useState } from 'react' -import { jsx, Stack, Text } from '@keystone-ui/core' -import { FieldContainer, FieldDescription, FieldLabel } from '@keystone-ui/fields' -import { Button } from '@keystone-ui/button' import { type FieldProps } from '../../../../types' +import { formatBytes, useTrimStartStyles } from '../../file/views/Field' import { SUPPORTED_IMAGE_EXTENSIONS } from '../utils' import { type ImageValue } from './index' import { type controller } from '.' -function useObjectURL (fileData: File | undefined) { - const [objectURL, setObjectURL] = useState(undefined) - useEffect(() => { - if (fileData) { - const url = URL.createObjectURL(fileData) - setObjectURL(url) - return () => { - URL.revokeObjectURL(url) - } - } - }, [fileData]) - return objectURL -} - -export function Field ({ - autoFocus, - field, - value, - onChange, -}: FieldProps) { +export function Field(props: FieldProps) { + const { autoFocus, field, onChange, value } = props const inputRef = useRef(null) - const errorMessage = createErrorMessage(value) const onUploadChange = ({ currentTarget: { validity, files }, - }: React.SyntheticEvent) => { + }: SyntheticEvent) => { const file = files?.[0] if (!file) return // bail if the user cancels from the file browser onChange?.({ @@ -46,228 +37,153 @@ export function Field ({ previous: value, }) } + const onFileTrigger = () => { + inputRef.current?.click() + } // Generate a random input key when the value changes, to ensure the file input is unmounted and // remounted (this is the only way to reset its value and ensure onChange will fire again if // the user selects the same file again) const inputKey = useMemo(() => Math.random(), [value]) const accept = useMemo( - () => SUPPORTED_IMAGE_EXTENSIONS.map(ext => [`.${ext}`, `image/${ext}`].join(', ')).join(', '), + () => + SUPPORTED_IMAGE_EXTENSIONS.map(ext => + [`.${ext}`, `image/${ext}`].join(', ') + ).join(', '), [] ) + return ( - - {field.label} - {field.description} - - - {errorMessage && ( - - {errorMessage} - + + {inputProps => ( + + + + )} - + ) } -function ImgView ({ - errorMessage, - value, - onChange, - field, - inputRef, -}: { - errorMessage?: string - value: ImageValue +function ImageView(props: { + onFileTrigger: () => void + isInvalid?: boolean onChange?: (value: ImageValue) => void - field: ReturnType - inputRef: RefObject + value: ImageValue }) { - const [imageDimensions, setImageDimensions] = useState({ width: 0, height: 0 }) - const imagePathFromUpload = useObjectURL( - errorMessage === undefined && value.kind === 'upload' ? value.data.file : undefined - ) - const imageSrc = value.kind === 'from-server' ? value.data.src : imagePathFromUpload + const { isInvalid, onFileTrigger, onChange, value } = props + const imageData = useImageData(value) return ( - - - {errorMessage || (value.kind !== 'from-server' && value.kind !== 'upload') ? ( - - ) : ( - - {value.kind === 'upload' && ( -
- Save to complete upload -
- )} - { - if (value.kind === 'upload') { - setImageDimensions({ - width: event.currentTarget.naturalWidth, - height: event.currentTarget.naturalHeight, - }) - } - }} - css={{ - objectFit: 'contain', - width: '100%', - height: '100%', - }} - alt={`Image uploaded to ${field.path} field`} - src={imageSrc} - /> -
- )} -
- {value.kind === 'from-server' || value.kind === 'upload' ? ( - onChange && ( - - {errorMessage === undefined ? ( - - ) : null} - - + + {isInvalid || !imageData ? null : ( + + {onChange && ( + + Change {value.kind === 'from-server' && ( - + )} {value.kind === 'upload' && ( - + )} - - - ) - ) : ( - - + Upload + {value.kind === 'remove' && value.previous && ( - + )} - + )} -
+ ) } -function createErrorMessage (value: ImageValue) { +function createErrorMessage(value: ImageValue) { if (value.kind === 'upload') { return validateImage(value.data) } } -export function validateImage ({ +function useObjectURL(fileData: File | undefined) { + const [objectURL, setObjectURL] = useState(undefined) + useEffect(() => { + if (fileData) { + const url = URL.createObjectURL(fileData) + setObjectURL(url) + return () => { + URL.revokeObjectURL(url) + } + } + }, [fileData]) + return objectURL +} + +export function validateImage({ file, validity, }: { file: File validity: ValidityState -}): string | undefined { +}) { if (!validity.valid) { return 'Something went wrong, please reload and try again.' } + // check if the file is actually an image if (!file.type.includes('image')) { return `Sorry, that file type isn't accepted. Please try ${SUPPORTED_IMAGE_EXTENSIONS.reduce( @@ -290,83 +206,139 @@ export function validateImage ({ // Styled Components // ============================== -export const ImageMeta = ({ - width = 0, - height = 0, - size, -}: { - width?: number - height?: number - size: number -}) => { - return ( - - Size: {`${bytes(size)}`} - Dimensions: {`${width} x ${height}`} - - ) -} +const SMALL_CONTAINER_MAX = `@container (max-width: 419px)` +const SMALL_CONTAINER_MIN = `@container (min-width: 420px)` -export const ImageWrapper = ({ children, url }: { children: ReactNode, url?: string }) => { - if (url) { - return ( - - {children} - - ) +function ImageDetails(props: PropsWithChildren) { + const trimStartStyles = useTrimStartStyles() + + // hide content until the uploaded image until it's available; use dimensions + // as an indicator since they're set on load. + const loadedStyles = { + opacity: props.height && props.width ? 1 : 0, + transition: transition('opacity'), } + return (
- {children} + preview of the upload + + + + {props.name} + + + + {formatBytes(props.size)} · {props.width} +  ×  + {props.height} + + + {/* field controls dependant on value type */} + {props.children} +
) } -export const Placeholder = () => { - return ( - - - +type ImageData = { + height: number + name: string + onLoad: (event: SyntheticEvent) => void + size: number + url: string + width: number +} + +function useImageData(value: ImageValue): ImageData | null { + // only relevant for uploaded images, but we must observe the rules of hooks + // so these can't be called conditionally. + const imagePathFromUpload = useObjectURL( + value.kind === 'upload' ? value.data.file : undefined ) + const [dimensions, setDimensions] = useState({ height: 0, width: 0 }) + const onLoad = useCallback( + (event: SyntheticEvent) => { + if (value.kind === 'upload') { + setDimensions({ + height: event.currentTarget.naturalHeight, + width: event.currentTarget.naturalWidth, + }) + } + }, + [value.kind] + ) + + // reset dimensions when the user cancels the upload. we use the dimensions as + // a signal that the image has loaded. + useEffect(() => { + if (value.kind !== 'upload') { + setDimensions({ height: 0, width: 0 }) + } + }, [value.kind]) + + switch (value.kind) { + case 'from-server': + return { + height: value.data.height, + width: value.data.width, + name: `${value.data.id}.${value.data.extension}`, + size: value.data.filesize, + url: value.data.src, + onLoad, + } as const + + case 'upload': + return { + height: dimensions.height, + width: dimensions.width, + name: value.data.file.name, + size: value.data.file.size, + // always string for simpler types, should be unreachable if the file + // selection fails validation. + url: imagePathFromUpload || '', + onLoad, + } as const + + default: + return null + } } diff --git a/packages/core/src/fields/types/image/views/index.tsx b/packages/core/src/fields/types/image/views/index.tsx index 1bf5d97061a..cdee1d0a840 100644 --- a/packages/core/src/fields/types/image/views/index.tsx +++ b/packages/core/src/fields/types/image/views/index.tsx @@ -2,14 +2,12 @@ /** @jsx jsx */ import { jsx } from '@keystone-ui/core' -import { FieldContainer, FieldLabel } from '@keystone-ui/fields' import { - type CardValueComponent, type CellComponent, type FieldController, type FieldControllerConfig, } from '../../../../types' -import { validateImage, ImageWrapper } from './Field' +import { validateImage } from './Field' export { Field } from './Field' @@ -31,20 +29,6 @@ export const Cell: CellComponent = ({ item, field }) => { ) } -export const CardValue: CardValueComponent = ({ item, field }) => { - const data = item[field.path] - return ( - - {field.label} - {data && ( - - {data.filename} - - )} - - ) -} - type ImageData = { src: string height: number diff --git a/packages/core/src/fields/types/integer/index.ts b/packages/core/src/fields/types/integer/index.ts index 43e6a3499f6..d9e13abecf1 100644 --- a/packages/core/src/fields/types/integer/index.ts +++ b/packages/core/src/fields/types/integer/index.ts @@ -16,7 +16,7 @@ import { mergeFieldHooks } from '../../resolve-hooks' export type IntegerFieldConfig = CommonFieldConfig & { isIndexed?: boolean | 'unique' - defaultValue?: number | { kind: 'autoincrement' } + defaultValue?: number | null | { kind: 'autoincrement' } validation?: { isRequired?: boolean min?: number @@ -29,13 +29,13 @@ export type IntegerFieldConfig = } } -// these are the lowest and highest values for a signed 32-bit integer -const MAX_INT = 2147483647 -const MIN_INT = -2147483648 +// for a signed 32-bit integer +const MAX_INT = 0x7fffffff +const MIN_INT = -0x80000000 export function integer (config: IntegerFieldConfig = {}): FieldTypeFunc { const { - defaultValue: _defaultValue, + defaultValue: defaultValue_, isIndexed, validation = {}, } = config @@ -45,15 +45,10 @@ export function integer (config: Integer min, max } = validation + const defaultValue = typeof defaultValue_ === 'number' ? defaultValue_ : (defaultValue_?.kind ?? null) return (meta) => { - const defaultValue = _defaultValue ?? null - const hasAutoIncDefault = - typeof defaultValue == 'object' && - defaultValue !== null && - defaultValue.kind === 'autoincrement' - - if (hasAutoIncDefault) { + if (defaultValue === 'autoincrement') { if (meta.provider === 'sqlite' || meta.provider === 'mysql') { throw new Error(`${meta.listKey}.${meta.fieldKey} specifies defaultValue: { kind: 'autoincrement' }, this is not supported on ${meta.provider}`) } @@ -66,6 +61,9 @@ export function integer (config: Integer ) } } + if (defaultValue !== null && !Number.isInteger(defaultValue)) { + throw new Error(`${meta.listKey}.${meta.fieldKey} specifies a default value of: ${defaultValue} but it must be a valid finite number`) + } if (min !== undefined && !Number.isInteger(min)) { throw new Error(`${meta.listKey}.${meta.fieldKey} specifies validation.min: ${min} but it must be an integer`) } @@ -109,14 +107,13 @@ export function integer (config: Integer kind: 'scalar', mode, scalar: 'Int', - // this will resolve to 'index' if the boolean is true, otherwise other values - false will be converted to undefined index: isIndexed === true ? 'index' : isIndexed || undefined, default: typeof defaultValue === 'number' ? { kind: 'literal', value: defaultValue } - : defaultValue?.kind === 'autoincrement' - ? { kind: 'autoincrement' } - : undefined, + : (defaultValue === 'autoincrement') + ? { kind: 'autoincrement' } + : undefined, map: config.db?.map, extendPrismaSchema: config.db?.extendPrismaSchema, })({ @@ -134,7 +131,8 @@ export function integer (config: Integer defaultValue: typeof defaultValue === 'number' ? defaultValue : undefined, }), resolve (value) { - if (value === undefined && typeof defaultValue === 'number') { + if (value === undefined) { + if (defaultValue === 'autoincrement') return null return defaultValue } return value @@ -149,14 +147,11 @@ export function integer (config: Integer getAdminMeta () { return { validation: { + isRequired, min: min ?? MIN_INT, max: max ?? MAX_INT, - isRequired, }, - defaultValue: - defaultValue === null || typeof defaultValue === 'number' - ? defaultValue - : 'autoincrement', + defaultValue } }, }) diff --git a/packages/core/src/fields/types/integer/views/index.tsx b/packages/core/src/fields/types/integer/views/index.tsx index 2ddabf17e80..fbe4ed86cad 100644 --- a/packages/core/src/fields/types/integer/views/index.tsx +++ b/packages/core/src/fields/types/integer/views/index.tsx @@ -1,190 +1,80 @@ -/** @jsxRuntime classic */ -/** @jsx jsx */ +import React, { useState } from 'react' + +import { ContextualHelp } from '@keystar/ui/contextual-help' +import { Content } from '@keystar/ui/slots' +import { NumberField } from '@keystar/ui/number-field' + +import { Heading, Text } from '@keystar/ui/typography' -import { jsx } from '@keystone-ui/core' -import { FieldContainer, FieldDescription, FieldLabel, TextInput } from '@keystone-ui/fields' -import { useState } from 'react' import { - type CardValueComponent, type CellComponent, type FieldController, type FieldControllerConfig, type FieldProps, } from '../../../../types' -import { CellLink, CellContainer } from '../../../../admin-ui/components' -import { useFormattedInput } from './utils' -function IntegerInput ({ - value, - onChange, - id, - autoFocus, - forceValidation, - validationMessage, - placeholder, -}: { - id: string - autoFocus?: boolean - value: number | string | null - onChange: (value: number | string | null) => void - forceValidation?: boolean - validationMessage?: string - placeholder?: string -}) { - const [hasBlurred, setHasBlurred] = useState(false) - const props = useFormattedInput( - { - format: value => (value === null ? '' : value.toString()), - parse: raw => { - raw = raw.trim() - if (raw === '') { - return null - } - if (/^[+-]?\d+$/.test(raw)) { - const parsed = parseInt(raw) - if (!Number.isSafeInteger(parsed)) { - return raw - } - return parsed - } - return raw - }, - }, - { - value, - onChange, - onBlur: () => { - setHasBlurred(true) - }, - } - ) - return ( - - - {(hasBlurred || forceValidation) && validationMessage && ( - {validationMessage} - )} - - ) +const TYPE_OPERATOR_MAP = { + equals: '=', + not: '≠', + gt: '>', + lt: '<', + gte: '≥', + lte: '≤', +} as const + +export const Cell: CellComponent = ({ field, item }) => { + const value = item[field.path] + return value != null ? {value.toString()} : null } -export const Field = ({ - field, - value, - onChange, - autoFocus, - forceValidation, -}: FieldProps) => { - const message = validate(value, field.validation, field.label, field.hasAutoIncrementDefault) - - return ( - - {field.label} - {field.description} - {onChange ? ( - - { - onChange({ ...value, value: val }) - }} - value={value.value} - forceValidation={forceValidation} - placeholder={ - field.hasAutoIncrementDefault && value.kind === 'create' - ? 'Defaults to an incremented number' - : undefined - } - validationMessage={message} - aria-describedby={field.description === null ? undefined : `${field.path}-description`} - /> - - ) : ( - value.value - )} - - ) -} - -export const Cell: CellComponent = ({ item, field, linkTo }) => { - const value = item[field.path] + '' - return linkTo ? {value} : {value} -} -Cell.supportsLinkTo = true +type Value = + | { kind: 'create', value: number | null } + | { kind: 'update', initial: number | null, value: number | null } -export const CardValue: CardValueComponent = ({ item, field }) => { - return ( - - {field.label} - {item[field.path] === null ? '' : item[field.path]} - - ) +type Validation = { + isRequired: boolean + min: number + max: number } -function validate ( +function validate_ ( value: Value, validation: Validation, label: string, hasAutoIncrementDefault: boolean ): string | undefined { - const val = value.value - if (typeof val === 'string') { - return `${label} must be a whole number` - } - - // if we recieve null initially on the item view and the current value is null, - // we should always allow saving it because: - // - the value might be null in the database and we don't want to prevent saving the whole item because of that - // - we might have null because of an access control error - if (value.kind === 'update' && value.initial === null && val === null) { - return undefined - } - - if (value.kind === 'create' && value.value === null && hasAutoIncrementDefault) { - return undefined - } - - if (validation.isRequired && val === null) { - return `${label} is required` - } - if (typeof val === 'number') { - if (val < validation.min) { - return `${label} must be greater than or equal to ${validation.min}` - } - if (val > validation.max) { - return `${label} must be less than or equal to ${validation.max}` - } - } - - return undefined -} - -type Validation = { - isRequired: boolean - min: number - max: number + const { + value: input, + kind, + } = value + if (kind === 'create' && hasAutoIncrementDefault && input === null) return + if (kind === 'update' && value.initial === null && input === null) return + if (validation.isRequired && input === null) return `${label} is required` + if (typeof input !== 'number') return + const v = input + if (!Number.isInteger(v)) return `${label} is not a valid integer` + if (validation.min !== undefined && v < validation.min) return `${label} must be greater than or equal to ${validation.min}` + if (validation.max !== undefined && v > validation.max) return `${label} must be less than or equal to ${validation.max}` } -type Value = - | { kind: 'update', initial: number | null, value: string | number | null } - | { kind: 'create', value: string | number | null } - -export const controller = ( +export function controller ( config: FieldControllerConfig<{ validation: Validation defaultValue: number | null | 'autoincrement' }> -): FieldController & { +): FieldController & { validation: Validation hasAutoIncrementDefault: boolean -} => { +} { + const validate = (value: Value) => { + return validate_( + value, + config.fieldMeta.validation, + config.label, + config.fieldMeta.defaultValue === 'autoincrement' + ) + } + return { path: config.path, label: config.label, @@ -193,94 +83,139 @@ export const controller = ( validation: config.fieldMeta.validation, defaultValue: { kind: 'create', - value: - config.fieldMeta.defaultValue === 'autoincrement' ? null : config.fieldMeta.defaultValue, + value: config.fieldMeta.defaultValue === 'autoincrement' ? null : config.fieldMeta.defaultValue, }, deserialize: data => ({ kind: 'update', value: data[config.path], initial: data[config.path] }), serialize: value => ({ [config.path]: value.value }), - hasAutoIncrementDefault: config.fieldMeta.defaultValue === 'autoincrement', - validate: value => - validate( - value, - config.fieldMeta.validation, - config.label, - config.fieldMeta.defaultValue === 'autoincrement' - ) === undefined, filter: { - Filter ({ autoFocus, type, onChange, value }) { - return ( - { - if (type === 'in' || type === 'not_in') { - onChange(event.target.value.replace(/[^\d,\s-]/g, '')) - return - } + Filter (props) { + const { autoFocus, context, forceValidation, typeLabel, onChange, type, value, ...otherProps } = props + const [isDirty, setDirty] = useState(false) + if (type === 'empty' || type === 'not_empty') return null - onChange(event.target.value.replace(/[^\d\s-]/g, '')) - }} - value={value} + const labelProps = context === 'add' + ? { label: config.label, description: typeLabel } + : { label: typeLabel } + + return ( + setDirty(true)} + onChange={x => onChange?.((!Number.isFinite(x)) ? null : x)} + value={value ?? NaN} /> ) }, graphql: ({ type, value }) => { - const valueWithoutWhitespace = value.replace(/\s/g, '') - const parsed = - type === 'in' || type === 'not_in' - ? valueWithoutWhitespace.split(',').map(x => parseInt(x)) - : parseInt(valueWithoutWhitespace) - if (type === 'not') { - return { [config.path]: { not: { equals: parsed } } } - } - const key = type === 'is' ? 'equals' : type === 'not_in' ? 'notIn' : type - return { [config.path]: { [key]: parsed } } + if (type === 'empty') return { [config.path]: { equals: null } } + if (type === 'not_empty') return { [config.path]: { not: { equals: null } } } + if (type === 'not') return { [config.path]: { not: { equals: value } } } + return { [config.path]: { [type]: value } } }, - Label ({ label, value, type }) { - let renderedValue = value - if (['in', 'not_in'].includes(type)) { - renderedValue = value - .split(',') - .map(value => value.trim()) - .join(', ') - } - return `${label.toLowerCase()}: ${renderedValue}` + Label ({ label, type, value }) { + if (type === 'empty' || type === 'not_empty') return label.toLocaleLowerCase() + const operator = TYPE_OPERATOR_MAP[type as keyof typeof TYPE_OPERATOR_MAP] + return `${operator} ${value}` }, types: { - is: { + equals: { label: 'Is exactly', - initialValue: '', + initialValue: null, }, not: { label: 'Is not exactly', - initialValue: '', + initialValue: null, }, gt: { label: 'Is greater than', - initialValue: '', + initialValue: null, }, lt: { label: 'Is less than', - initialValue: '', + initialValue: null, }, gte: { label: 'Is greater than or equal to', - initialValue: '', + initialValue: null, }, lte: { label: 'Is less than or equal to', - initialValue: '', + initialValue: null, }, - in: { - label: 'Is one of', - initialValue: '', + empty: { + label: 'Is empty', + initialValue: null, }, - not_in: { - label: 'Is not one of', - initialValue: '', + not_empty: { + label: 'Is not empty', + initialValue: null, }, }, }, + + hasAutoIncrementDefault: config.fieldMeta.defaultValue === 'autoincrement', + validate: value => validate(value) === undefined, } } + +export function Field ({ + field, + value, + onChange, + autoFocus, + forceValidation, +}: FieldProps) { + const [isDirty, setDirty] = useState(false) + const isReadOnly = !onChange || field.hasAutoIncrementDefault + + if (field.hasAutoIncrementDefault && value.kind === 'create') { + return ( + + Auto increment + + + This field is set to auto increment. It will default to the next available number. + + + + )} + /> + ) + } + + const validate = (value: Value) => { + return validate_( + value, + field.validation, + field.label, + field.hasAutoIncrementDefault + ) + } + + return ( + setDirty(true)} + onChange={x => onChange?.({ ...value, value: (!Number.isFinite(x)) ? null : x })} + value={value.value ?? NaN} + /> + ) +} diff --git a/packages/core/src/fields/types/integer/views/utils.tsx b/packages/core/src/fields/types/integer/views/utils.tsx deleted file mode 100644 index 63e8fc558cf..00000000000 --- a/packages/core/src/fields/types/integer/views/utils.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { type ChangeEvent, type FocusEvent, useState } from 'react' - -type ParsedValueBase = undefined | symbol | boolean | object | number | null | bigint - -type Config = { - parse: (value: string) => ParsedValue | string - format: (value: ParsedValue) => string -} - -export function useFormattedInput ( - config: Config, - { - value, - onChange, - onBlur, - onFocus, - }: { - value: string | ParsedValue - onChange: (value: string | ParsedValue) => void - onFocus?: (event: FocusEvent) => void - onBlur?: (event: FocusEvent) => void - } -) { - // typeof value === 'string' implies the unparsed form - // typeof value !== 'string' implies the parsed form - if (typeof value === 'string' && typeof config.parse(value) !== 'string') { - throw new Error(`Expected ${typeof config.parse(value)}, got ${typeof value}`) - } - const [internalValueState, setInternalValueState] = useState(() => - typeof value === 'string' ? value : config.format(value) - ) - const [isFocused, setIsFocused] = useState(false) - if (typeof value === 'string' && value !== internalValueState) { - setInternalValueState(value) - } - // If the value is not a string, we know it's in the parsed form - if (typeof value !== 'string') { - const formatted = config.format(value) - // When the input is blurred, we want to show always show the formatted - // version so if we're not focussed and the formatted version is different - // to the current version, we need to update it. - if (!isFocused && formatted !== internalValueState) { - setInternalValueState(formatted) - } - - const parsedInternal = config.parse(internalValueState) - - // We updating the internal value here because the - // external value has changed. - if (typeof parsedInternal !== 'string' && config.format(parsedInternal) !== formatted) { - setInternalValueState(formatted) - } - } - - return { - value: internalValueState, - onChange (event: ChangeEvent) { - const value = event.target.value - const parsed = config.parse(value) - onChange(parsed) - setInternalValueState(value) - }, - onFocus (event: FocusEvent) { - onFocus?.(event) - setIsFocused(true) - }, - onBlur (event: FocusEvent) { - onBlur?.(event) - setIsFocused(false) - // this isn't strictly necessary since we already do this in render - // this just saves another rerender after setIsFocused(false) - if (typeof value !== 'string') { - setInternalValueState(config.format(value)) - } - }, - } -} diff --git a/packages/core/src/fields/types/json/views/index.tsx b/packages/core/src/fields/types/json/views/index.tsx index f32da245310..b19c9cf70f1 100644 --- a/packages/core/src/fields/types/json/views/index.tsx +++ b/packages/core/src/fields/types/json/views/index.tsx @@ -1,79 +1,50 @@ -/** @jsxRuntime classic */ -/** @jsx jsx */ +import React from 'react' + +import { css, tokenSchema } from '@keystar/ui/style' +import { TextArea } from '@keystar/ui/text-field' +import { Text } from '@keystar/ui/typography' -import { jsx, Stack, Text } from '@keystone-ui/core' -import { FieldContainer, FieldDescription, FieldLabel, TextArea } from '@keystone-ui/fields' import { - type CardValueComponent, type CellComponent, type FieldController, type FieldControllerConfig, type FieldProps, type JSONValue, } from '../../../../types' -import { CellContainer, CellLink } from '../../../../admin-ui/components' -export const Field = ({ - field, - forceValidation, - value, - onChange, - autoFocus, -}: FieldProps) => { +export const Field = (props: FieldProps) => { + const { autoFocus, field, forceValidation, onChange, value } = props + const errorMessage = forceValidation ? 'Invalid JSON' : undefined + return ( - - {field.label} - {field.description} - -