@ossy/app 1.30.1 → 1.31.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 +10 -10
- package/runtime/page-runtime.js +1 -1
- package/src/shell/App.jsx +36 -0
- package/src/shell/AppContext.js +5 -0
- package/src/shell/AppSettings.jsx +29 -0
- package/src/shell/ThemeEditor.jsx +146 -0
- package/src/shell/index.js +4 -0
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ossy/app",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.31.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"source": "./src/index.js",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": "./src/index.js",
|
|
10
|
+
"./shell": "./src/shell/index.js",
|
|
10
11
|
"./runtime/page-runtime": "./runtime/page-runtime.js",
|
|
11
12
|
"./runtime/api-runtime": "./runtime/api-runtime.js",
|
|
12
13
|
"./runtime/task-runtime": "./runtime/task-runtime.js"
|
|
@@ -37,15 +38,14 @@
|
|
|
37
38
|
"@babel/eslint-parser": "^7.15.8",
|
|
38
39
|
"@babel/preset-react": "^7.26.3",
|
|
39
40
|
"@babel/register": "^7.25.9",
|
|
40
|
-
"@ossy/
|
|
41
|
-
"@ossy/design-system": "^1.30.1",
|
|
41
|
+
"@ossy/design-system": "^1.31.0",
|
|
42
42
|
"@ossy/pages": "^1.23.0",
|
|
43
|
-
"@ossy/platform": "^1.
|
|
44
|
-
"@ossy/router": "^1.
|
|
45
|
-
"@ossy/router-react": "^1.
|
|
46
|
-
"@ossy/sdk": "^1.
|
|
47
|
-
"@ossy/sdk-react": "^1.
|
|
48
|
-
"@ossy/themes": "^1.
|
|
43
|
+
"@ossy/platform": "^1.30.0",
|
|
44
|
+
"@ossy/router": "^1.31.0",
|
|
45
|
+
"@ossy/router-react": "^1.31.0",
|
|
46
|
+
"@ossy/sdk": "^1.31.0",
|
|
47
|
+
"@ossy/sdk-react": "^1.31.0",
|
|
48
|
+
"@ossy/themes": "^1.31.0",
|
|
49
49
|
"@rollup/plugin-alias": "^6.0.0",
|
|
50
50
|
"@rollup/plugin-babel": "^7.0.0",
|
|
51
51
|
"@rollup/plugin-commonjs": "^29.0.0",
|
|
@@ -79,5 +79,5 @@
|
|
|
79
79
|
"README.md",
|
|
80
80
|
"tsconfig.json"
|
|
81
81
|
],
|
|
82
|
-
"gitHead": "
|
|
82
|
+
"gitHead": "ccbb5575213d2703e3cad5608b85c3478fc421bc"
|
|
83
83
|
}
|
package/runtime/page-runtime.js
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { SDK } from '@ossy/sdk'
|
|
3
|
+
import { WorkspaceProvider } from '@ossy/sdk-react'
|
|
4
|
+
import { Theme, ComponentSlotsProvider } from '@ossy/design-system'
|
|
5
|
+
import { ThemeEditor } from './ThemeEditor.jsx'
|
|
6
|
+
import { defaultAppSettings } from './AppSettings.jsx'
|
|
7
|
+
import { Router } from '@ossy/router-react'
|
|
8
|
+
import { AppContext } from './AppContext.js'
|
|
9
|
+
|
|
10
|
+
export const App = ({ children, ..._appSettings }) => {
|
|
11
|
+
const appSettings = { ...defaultAppSettings(), ..._appSettings }
|
|
12
|
+
|
|
13
|
+
// `components` holds resolved ComponentType values — not JSON-serializable,
|
|
14
|
+
// so we keep them out of AppContext and provide them via ComponentSlotsProvider.
|
|
15
|
+
const { components, ...contextSettings } = appSettings
|
|
16
|
+
|
|
17
|
+
const sdk = SDK.of({
|
|
18
|
+
apiUrl: appSettings.apiUrl,
|
|
19
|
+
workspaceId: appSettings.workspaceId
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<AppContext.Provider value={contextSettings}>
|
|
24
|
+
<ComponentSlotsProvider slots={components || {}}>
|
|
25
|
+
<Theme theme={appSettings.theme} themes={appSettings.themes}>
|
|
26
|
+
<WorkspaceProvider sdk={sdk}>
|
|
27
|
+
<Router {...appSettings} pages={appSettings.pages || []}>
|
|
28
|
+
{children}
|
|
29
|
+
</Router>
|
|
30
|
+
{appSettings.devMode && <ThemeEditor />}
|
|
31
|
+
</WorkspaceProvider>
|
|
32
|
+
</Theme>
|
|
33
|
+
</ComponentSlotsProvider>
|
|
34
|
+
</AppContext.Provider>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React, { createContext } from 'react'
|
|
2
|
+
import { defaultAppSettings } from './AppSettings.jsx'
|
|
3
|
+
export const AppContext = createContext(defaultAppSettings())
|
|
4
|
+
export const useApp = () => React.useContext(AppContext)
|
|
5
|
+
export const useAppSettings = () => React.useContext(AppContext)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
|
|
2
|
+
export function defaultAppSettings() {
|
|
3
|
+
return {
|
|
4
|
+
workspaceId: undefined,
|
|
5
|
+
defaultLanguage: undefined,
|
|
6
|
+
supportedLanguages: [],
|
|
7
|
+
theme: undefined,
|
|
8
|
+
themes: undefined,
|
|
9
|
+
routes: [],
|
|
10
|
+
pages: [],
|
|
11
|
+
initialEntries: [],
|
|
12
|
+
initialIndex: 0,
|
|
13
|
+
router: 'browser',
|
|
14
|
+
gaId: undefined,
|
|
15
|
+
apiUrl: undefined,
|
|
16
|
+
devMode: false,
|
|
17
|
+
/** Site / app label (e.g. chrome); not wired to `<title>` — pages own document title. */
|
|
18
|
+
documentTitle: undefined,
|
|
19
|
+
title: undefined,
|
|
20
|
+
metaDescription: undefined,
|
|
21
|
+
themeColor: undefined,
|
|
22
|
+
/** `<html lang>`; falls back to `defaultLanguage` then `en`. */
|
|
23
|
+
htmlLang: undefined,
|
|
24
|
+
/** Favicon URL; default `/favicon.ico`. Use `null` or `false` to omit. */
|
|
25
|
+
faviconHref: undefined,
|
|
26
|
+
/** When true, main app sidebar is collapsed to icons (from server cookie / app-settings). */
|
|
27
|
+
sidebarPrimaryCollapsed: false,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import React, { useState, useMemo, useCallback, useEffect } from 'react'
|
|
2
|
+
import { useResource } from '@ossy/sdk-react'
|
|
3
|
+
import { Overlay, Button, useTheme, View, Text } from '@ossy/design-system'
|
|
4
|
+
import { useApp } from './AppContext.js'
|
|
5
|
+
|
|
6
|
+
const overlayStyles = {
|
|
7
|
+
background: 'transparent',
|
|
8
|
+
display: 'content',
|
|
9
|
+
pointerEvents: 'none'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const blobStyles = {
|
|
13
|
+
boxShadow: '2px 2px 5px hsla(0, 0%, 0%, .2)',
|
|
14
|
+
borderRadius: '999px',
|
|
15
|
+
position: 'absolute',
|
|
16
|
+
right: 'var(--space-m)',
|
|
17
|
+
bottom: 'var(--space-m)',
|
|
18
|
+
cursor: 'pointer',
|
|
19
|
+
transition: 'transform .5s',
|
|
20
|
+
pointerEvents: 'auto',
|
|
21
|
+
padding: 'var(--space-m)'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const editorContainerStyles = {
|
|
25
|
+
position: 'absolute',
|
|
26
|
+
right: '0',
|
|
27
|
+
top: '0',
|
|
28
|
+
height: '100%',
|
|
29
|
+
width: '100%',
|
|
30
|
+
display: 'flex',
|
|
31
|
+
justifyContent: 'flex-end',
|
|
32
|
+
alignItems: 'stretch',
|
|
33
|
+
padding: '8px 32px'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const editorStyles = {
|
|
37
|
+
width: '100%',
|
|
38
|
+
maxWidth: '350px',
|
|
39
|
+
height: '100%',
|
|
40
|
+
boxShadow: '2px 2px 5px hsla(0, 0%, 0%, .2)',
|
|
41
|
+
padding: '16px 16px 76px 16px',
|
|
42
|
+
overflowX: 'none',
|
|
43
|
+
overflowY: 'auto',
|
|
44
|
+
pointerEvents: 'auto'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const ThemeSwitcher = () => {
|
|
48
|
+
const { activeTheme, setTheme, themes } = useTheme()
|
|
49
|
+
return (
|
|
50
|
+
<View gap="s">
|
|
51
|
+
<Text>Theme</Text>
|
|
52
|
+
<View layout="row-wrap" gap="s">
|
|
53
|
+
{
|
|
54
|
+
themes.map(themeName => <Button variant={themeName === activeTheme ? 'tag-active' : 'tag'} onClick={() => setTheme(themeName)}>{themeName}</Button>)
|
|
55
|
+
}
|
|
56
|
+
</View>
|
|
57
|
+
</View>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const ThemeEditor = () => {
|
|
62
|
+
const app = useApp()
|
|
63
|
+
const { updateResourceContent } = useResource('PCX53TaGviq4_8KvK-VOp')
|
|
64
|
+
const [isEditorOpen, setIsEditorOpen] = useState(false)
|
|
65
|
+
const [viewCount, setViewCount] = useState(0)
|
|
66
|
+
// const [theme, temporarilyUpdateTheme] = useTheme()
|
|
67
|
+
const theme = {}
|
|
68
|
+
const temporarilyUpdateTheme = () => {}
|
|
69
|
+
|
|
70
|
+
const toggleStyles = useMemo(() => !isEditorOpen
|
|
71
|
+
? blobStyles
|
|
72
|
+
: { ...blobStyles, transform: 'rotate(-45deg)' }, [isEditorOpen])
|
|
73
|
+
|
|
74
|
+
const onToggle = useCallback(() => {
|
|
75
|
+
setIsEditorOpen(!isEditorOpen)
|
|
76
|
+
}, [isEditorOpen, setIsEditorOpen])
|
|
77
|
+
|
|
78
|
+
const onThemeChange = (event) => {
|
|
79
|
+
|
|
80
|
+
const name = event.target.dataset.name
|
|
81
|
+
const updatedValue = event.target.value
|
|
82
|
+
|
|
83
|
+
if (!name) return
|
|
84
|
+
if (!updatedValue) return
|
|
85
|
+
|
|
86
|
+
temporarilyUpdateTheme(theme => ({
|
|
87
|
+
...theme,
|
|
88
|
+
[name]: updatedValue
|
|
89
|
+
}))
|
|
90
|
+
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const onSaveTheme = event => {
|
|
94
|
+
event.preventDefault()
|
|
95
|
+
updateResourceContent(theme)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
if (!isEditorOpen) return
|
|
100
|
+
const views = document.querySelectorAll('[data-view]')
|
|
101
|
+
// views.forEach(view => view.style.border = '1px solid red')
|
|
102
|
+
setViewCount(views.length)
|
|
103
|
+
}, [isEditorOpen])
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<Overlay isVisible={true} style={overlayStyles}>
|
|
107
|
+
|
|
108
|
+
{
|
|
109
|
+
isEditorOpen && (
|
|
110
|
+
<div style={editorContainerStyles}>
|
|
111
|
+
<View surface="primary" roundness="m" gap="m" style={editorStyles}>
|
|
112
|
+
<View as="form" gap="s" onSubmit={onSaveTheme} onChange={onThemeChange}>
|
|
113
|
+
{Object.entries(theme).map(([name, value]) => (
|
|
114
|
+
<div style={{ marginBottom: '16px' }}>
|
|
115
|
+
<label style={{ display: 'block', fontFamily: 'sans-serif', marginBottom: '4px', fontWeight: 'bold' }}>{name}</label>
|
|
116
|
+
<input value={value} data-name={name} style={{ width: '100%', padding: '4px' }}/>
|
|
117
|
+
</div>
|
|
118
|
+
))}
|
|
119
|
+
<Button type="submit" variant="cta">
|
|
120
|
+
Save
|
|
121
|
+
</Button>
|
|
122
|
+
</View>
|
|
123
|
+
<ThemeSwitcher/>
|
|
124
|
+
<Text>Views: {viewCount}</Text>
|
|
125
|
+
</View>
|
|
126
|
+
<View>
|
|
127
|
+
|
|
128
|
+
<View>
|
|
129
|
+
Pages: {app?.pages?.length || 0}
|
|
130
|
+
</View>
|
|
131
|
+
|
|
132
|
+
<View>
|
|
133
|
+
{(app?.pages || []).map(page => (
|
|
134
|
+
<Button>{page.id}</Button>
|
|
135
|
+
))}
|
|
136
|
+
</View>
|
|
137
|
+
</View>
|
|
138
|
+
</div>
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
<Button variant="cta" prefix="math-plus" style={toggleStyles} onClick={onToggle} />
|
|
143
|
+
|
|
144
|
+
</Overlay>
|
|
145
|
+
)
|
|
146
|
+
}
|