sanity-plugin-workspace-home 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,88 @@
1
+ {
2
+ "name": "sanity-plugin-workspace-home",
3
+ "version": "1.0.0",
4
+ "description": "A home screen for your multi-workspace Sanity Studio",
5
+ "keywords": [
6
+ "sanity",
7
+ "sanity-plugin"
8
+ ],
9
+ "homepage": "https://github.com/sanity-io/sanity-plugin-workspace-home#readme",
10
+ "bugs": {
11
+ "url": "https://github.com/sanity-io/sanity-plugin-workspace-home/issues"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git@github.com:sanity-io/sanity-plugin-workspace-home.git"
16
+ },
17
+ "license": "MIT",
18
+ "author": "Sanity <hello@sanity.io>",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "source": "./src/index.ts",
23
+ "require": "./dist/index.js",
24
+ "import": "./dist/index.esm.js",
25
+ "default": "./dist/index.esm.js"
26
+ },
27
+ "./package.json": "./package.json"
28
+ },
29
+ "main": "./dist/index.js",
30
+ "module": "./dist/index.esm.js",
31
+ "source": "./src/index.ts",
32
+ "types": "./dist/index.d.ts",
33
+ "files": [
34
+ "dist",
35
+ "sanity.json",
36
+ "src",
37
+ "v2-incompatible.js"
38
+ ],
39
+ "scripts": {
40
+ "build": "run-s clean && plugin-kit verify-package --silent && pkg-utils build --strict && pkg-utils --strict",
41
+ "clean": "rimraf dist",
42
+ "format": "prettier --write --cache --ignore-unknown .",
43
+ "link-watch": "plugin-kit link-watch",
44
+ "lint": "eslint .",
45
+ "prepublishOnly": "run-s build",
46
+ "watch": "pkg-utils watch --strict",
47
+ "prepare": "husky install"
48
+ },
49
+ "dependencies": {
50
+ "@sanity/incompatible-plugin": "^1.0.4"
51
+ },
52
+ "devDependencies": {
53
+ "@commitlint/cli": "^17.5.0",
54
+ "@commitlint/config-conventional": "^17.4.4",
55
+ "@sanity/pkg-utils": "^2.2.13",
56
+ "@sanity/plugin-kit": "^3.1.7",
57
+ "@sanity/semantic-release-preset": "^4.0.2",
58
+ "@types/react": "^18.0.28",
59
+ "@types/styled-components": "^5.1.26",
60
+ "@typescript-eslint/eslint-plugin": "^5.56.0",
61
+ "@typescript-eslint/parser": "^5.56.0",
62
+ "eslint": "^8.36.0",
63
+ "eslint-config-prettier": "^8.8.0",
64
+ "eslint-config-sanity": "^6.0.0",
65
+ "eslint-plugin-prettier": "^4.2.1",
66
+ "eslint-plugin-react": "^7.32.2",
67
+ "eslint-plugin-react-hooks": "^4.6.0",
68
+ "husky": "^8.0.3",
69
+ "lint-staged": "^13.2.0",
70
+ "npm-run-all": "^4.1.5",
71
+ "prettier": "^2.8.6",
72
+ "prettier-plugin-packagejson": "^2.4.3",
73
+ "react": "^18.2.0",
74
+ "react-dom": "^18.2.0",
75
+ "react-is": "^18.2.0",
76
+ "rimraf": "^4.4.0",
77
+ "sanity": "^3.7.1",
78
+ "typescript": "^5.0.2"
79
+ },
80
+ "peerDependencies": {
81
+ "react": "^18",
82
+ "sanity": "^3",
83
+ "styled-components": "^5.3.9"
84
+ },
85
+ "engines": {
86
+ "node": ">=14"
87
+ }
88
+ }
package/sanity.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "parts": [
3
+ {
4
+ "implements": "part:@sanity/base/sanity-root",
5
+ "path": "./v2-incompatible.js"
6
+ }
7
+ ]
8
+ }
@@ -0,0 +1,41 @@
1
+ import {useActiveWorkspace, useWorkspaces} from 'sanity'
2
+ import {Button, Flex, Container, Stack, Heading, Card} from '@sanity/ui'
3
+
4
+ import WorkspacePreview from './WorkspacePreview'
5
+ import {useEffect} from 'react'
6
+
7
+ export default function WorkspaceHome() {
8
+ const [home, ...workspaces] = useWorkspaces()
9
+ const {setActiveWorkspace} = useActiveWorkspace()
10
+
11
+ // Listen to keypress of workspace index
12
+ useEffect(() => {
13
+ const handleKeypress = (event: KeyboardEvent) => {
14
+ const index = parseInt(event.key, 10) - 1
15
+ if (index >= 0 && index < workspaces.length) {
16
+ setActiveWorkspace(workspaces[index].name)
17
+ }
18
+ }
19
+
20
+ window.addEventListener('keypress', handleKeypress)
21
+
22
+ return () => {
23
+ window.removeEventListener('keypress', handleKeypress)
24
+ }
25
+ }, [workspaces, setActiveWorkspace])
26
+
27
+ return (
28
+ <Card tone="transparent" height="fill">
29
+ <Flex direction="column" height="fill" padding={[4, 5, 6, 6]}>
30
+ <Container>
31
+ <Stack space={4}>
32
+ <Heading>Workspaces</Heading>
33
+ {workspaces.map((workspace, index) => (
34
+ <WorkspacePreview key={workspace.name} workspace={workspace} index={index} />
35
+ ))}
36
+ </Stack>
37
+ </Container>
38
+ </Flex>
39
+ </Card>
40
+ )
41
+ }
@@ -0,0 +1,77 @@
1
+ import {Button, Hotkeys, Stack, Box, Flex, Heading, Card, Text} from '@sanity/ui'
2
+ // import {StarIcon} from '@sanity/icons'
3
+ import React, {createElement, isValidElement, useMemo} from 'react'
4
+ import {isValidElementType} from 'react-is'
5
+ import {useActiveWorkspace, WorkspaceSummary} from 'sanity'
6
+ import styled from 'styled-components'
7
+
8
+ const createIcon = (icon: React.ComponentType | React.ReactNode) => {
9
+ if (isValidElementType(icon)) return createElement(icon)
10
+ if (isValidElement(icon)) return icon
11
+ return undefined
12
+ }
13
+
14
+ export const MediaCard = styled(Card)`
15
+ width: 35px;
16
+ height: 35px;
17
+
18
+ svg {
19
+ width: 100%;
20
+ height: 100%;
21
+ }
22
+ `
23
+
24
+ export type WorkspacePreviewProps = {
25
+ workspace: WorkspaceSummary
26
+ index: number
27
+ }
28
+
29
+ export default function WorkspacePreview(props: WorkspacePreviewProps) {
30
+ const {icon, title, name, subtitle} = props.workspace
31
+ const {setActiveWorkspace} = useActiveWorkspace()
32
+ const iconComponent = useMemo(() => createIcon(icon), [icon])
33
+
34
+ // TODO: Favorites feature
35
+ // get favorite workspace name from localStorage
36
+ // const favoriteWorkspace = localStorage.getItem('favoriteWorkspace')
37
+ // save local state to force re-render
38
+ // const [isFavourite, setIsFavourite] = React.useState(favoriteWorkspace === name)
39
+
40
+ // set favorite workspace name to localStorage
41
+ // const setFavoriteWorkspace = useCallback(() => {
42
+ // localStorage.setItem('favoriteWorkspace', name)
43
+ // }, [name])
44
+
45
+ return (
46
+ <Card shadow={1} radius={2}>
47
+ <Flex align="center" gap={[2, 3, 4, 4]} paddingX={[2, 3, 4, 4]}>
48
+ <Hotkeys keys={[String(props.index + 1)]} padding={2} />
49
+ <Box flex={1}>
50
+ <Stack>
51
+ <Button mode="bleed" tone="default" onClick={() => setActiveWorkspace(name)}>
52
+ <Flex align="center" gap={[2, 3, 4, 4]}>
53
+ {iconComponent ? <MediaCard>{createIcon(iconComponent)}</MediaCard> : null}
54
+ <Box flex={1}>
55
+ <Stack space={[1, 2, 3, 3]}>
56
+ <Heading>{title || name}</Heading>
57
+ {subtitle ? <Text muted>{subtitle}</Text> : null}
58
+ </Stack>
59
+ </Box>
60
+ </Flex>
61
+ </Button>
62
+ </Stack>
63
+ </Box>
64
+ {/* <Button
65
+ mode={isFavourite ? 'default' : 'bleed'}
66
+ tone={isFavourite ? 'primary' : 'default'}
67
+ icon={StarIcon}
68
+ title="Favorite"
69
+ onClick={() => {
70
+ setIsFavourite(!isFavourite)
71
+ setFavoriteWorkspace()
72
+ }}
73
+ /> */}
74
+ </Flex>
75
+ </Card>
76
+ )
77
+ }
package/src/index.ts ADDED
@@ -0,0 +1,29 @@
1
+ import {Config, definePlugin} from 'sanity'
2
+ import {HomeIcon} from '@sanity/icons'
3
+
4
+ import {workspaceHomeTool} from './tool'
5
+
6
+ export const workspaceHome = definePlugin(() => {
7
+ return {
8
+ name: 'sanity-plugin-workspace-home',
9
+ tools: [workspaceHomeTool],
10
+ }
11
+ })
12
+
13
+ type WorkspaceHomeConfigProps = {
14
+ projectId: string
15
+ dataset: string
16
+ }
17
+
18
+ export const workspaceHomeConfig = ({
19
+ projectId = ``,
20
+ dataset = ``,
21
+ }: WorkspaceHomeConfigProps): Config => ({
22
+ name: 'home',
23
+ title: 'Home',
24
+ basePath: '/home',
25
+ projectId,
26
+ dataset,
27
+ icon: HomeIcon,
28
+ plugins: [workspaceHome()],
29
+ })
@@ -0,0 +1,10 @@
1
+ import {HomeIcon} from '@sanity/icons'
2
+
3
+ import WorkspaceHome from '../components/WorkspaceHome'
4
+
5
+ export const workspaceHomeTool = {
6
+ title: 'Workspace Home',
7
+ name: 'workspace-home',
8
+ icon: HomeIcon,
9
+ component: WorkspaceHome,
10
+ }
@@ -0,0 +1,11 @@
1
+ const {showIncompatiblePluginDialog} = require('@sanity/incompatible-plugin')
2
+ const {name, version, sanityExchangeUrl} = require('./package.json')
3
+
4
+ export default showIncompatiblePluginDialog({
5
+ name: name,
6
+ versions: {
7
+ v3: version,
8
+ v2: undefined,
9
+ },
10
+ sanityExchangeUrl,
11
+ })