@ossy/resources 1.11.5 → 1.11.7
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 +3 -3
- package/src/AudioResource.jsx +5 -5
- package/src/CreateDocument.jsx +13 -50
- package/src/GenericResourceCard.jsx +38 -0
- package/src/GenericResourceDetail.jsx +61 -0
- package/src/GenericResourceForm.jsx +106 -0
- package/src/ImageResource.jsx +0 -1
- package/src/PDFResource.jsx +9 -9
- package/src/ResourceContentPage.jsx +1 -1
- package/src/ResourceFactory.jsx +7 -3
- package/src/ResourceGenericView.jsx +10 -11
- package/src/ResourceList.jsx +4 -4
- package/src/ResourcePanel.jsx +14 -15
- package/src/Upload.jsx +4 -4
- package/src/VideoResource.jsx +5 -5
- package/src/index.js +3 -0
- package/src/resource-create.page.jsx +47 -0
- package/src/resource-detail.page.jsx +27 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ossy/resources",
|
|
3
3
|
"description": "Resource domain — aggregate and events for the Ossy resource model",
|
|
4
|
-
"version": "1.11.
|
|
4
|
+
"version": "1.11.7",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./src/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@jest/globals": "^30.2.0",
|
|
22
|
-
"@ossy/platform": "^1.38.
|
|
22
|
+
"@ossy/platform": "^1.38.7",
|
|
23
23
|
"casual": "^1.6.2",
|
|
24
24
|
"jest": "^30.2.0"
|
|
25
25
|
},
|
|
@@ -31,5 +31,5 @@
|
|
|
31
31
|
"/src",
|
|
32
32
|
"README.md"
|
|
33
33
|
],
|
|
34
|
-
"gitHead": "
|
|
34
|
+
"gitHead": "f65df0cd6da864c314c4f424112e05e8e2dec0ff"
|
|
35
35
|
}
|
package/src/AudioResource.jsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { useResource } from '@ossy/sdk-react'
|
|
3
|
-
import {
|
|
3
|
+
import { View } from '@ossy/design-system'
|
|
4
4
|
|
|
5
5
|
export const AudioResource = ({
|
|
6
6
|
resourceId,
|
|
@@ -9,8 +9,8 @@ export const AudioResource = ({
|
|
|
9
9
|
const { resource } = useResource(resourceId)
|
|
10
10
|
|
|
11
11
|
return (
|
|
12
|
-
<
|
|
13
|
-
<
|
|
12
|
+
<View stack bordered>
|
|
13
|
+
<View.Item fill style={{ padding: '16px 8px', height: '100%' }}>
|
|
14
14
|
|
|
15
15
|
<View layout="off-center-s" inset="m" style={{ height: '100%'}}>
|
|
16
16
|
<View
|
|
@@ -21,7 +21,7 @@ export const AudioResource = ({
|
|
|
21
21
|
/>
|
|
22
22
|
</View>
|
|
23
23
|
|
|
24
|
-
</
|
|
25
|
-
</
|
|
24
|
+
</View.Item>
|
|
25
|
+
</View>
|
|
26
26
|
)
|
|
27
27
|
}
|
package/src/CreateDocument.jsx
CHANGED
|
@@ -1,73 +1,36 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
|
-
import { useResources
|
|
3
|
-
import {
|
|
4
|
-
Alert,
|
|
5
|
-
InputTitle,
|
|
6
|
-
Button,
|
|
7
|
-
useInputValue,
|
|
8
|
-
Fields,
|
|
9
|
-
applyFieldChange,
|
|
10
|
-
} from '@ossy/design-system'
|
|
2
|
+
import { useResources } from '@ossy/sdk-react'
|
|
3
|
+
import { Alert } from '@ossy/design-system'
|
|
11
4
|
import { useRouter } from '@ossy/router-react'
|
|
5
|
+
import { GenericResourceForm } from './GenericResourceForm.jsx'
|
|
12
6
|
|
|
13
7
|
export const CreateDocument = () => {
|
|
14
8
|
const router = useRouter()
|
|
15
9
|
const templateId = router.searchParams.templateId
|
|
16
10
|
const location = router.searchParams.location
|
|
17
|
-
const template = useResourceTemplate(templateId)
|
|
18
|
-
const [documentData, setDocumentData] = useState({})
|
|
19
11
|
const { createDocument } = useResources()
|
|
20
|
-
const [documentName, setDocumentName] = useInputValue()
|
|
21
12
|
const [error, setError] = useState()
|
|
22
13
|
|
|
23
14
|
const onCancel = () => {
|
|
24
|
-
setDocumentData({})
|
|
25
15
|
const search = new URLSearchParams({ location }).toString()
|
|
26
16
|
router.navigate(`@storage/home?${search}`)
|
|
27
17
|
}
|
|
28
18
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const onCreateDocument = () => {
|
|
36
|
-
if (documentName === '' || documentName === undefined) {
|
|
37
|
-
setError('Document name needs to be set')
|
|
38
|
-
return ''
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
createDocument({
|
|
42
|
-
type: templateId,
|
|
43
|
-
location: location,
|
|
44
|
-
name: documentName,
|
|
45
|
-
content: documentData
|
|
46
|
-
})
|
|
47
|
-
.then(() => onFinish())
|
|
48
|
-
.catch(error => { setError(error.message) })
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const updateFieldData = event => {
|
|
52
|
-
setDocumentData(prev => applyFieldChange(prev, event))
|
|
19
|
+
const onSubmit = ({ name, content, type }) => {
|
|
20
|
+
createDocument({ type, location, name, content })
|
|
21
|
+
.then(() => onCancel())
|
|
22
|
+
.catch((err) => setError(err?.message || 'Create failed'))
|
|
53
23
|
}
|
|
54
24
|
|
|
55
25
|
return (
|
|
56
26
|
<>
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
value={documentName}
|
|
63
|
-
onChange={setDocumentName}
|
|
27
|
+
<GenericResourceForm
|
|
28
|
+
templateId={templateId}
|
|
29
|
+
mode="create"
|
|
30
|
+
onSubmit={onSubmit}
|
|
31
|
+
onCancel={onCancel}
|
|
64
32
|
/>
|
|
65
|
-
|
|
66
|
-
{ error && <Alert>{error}</Alert>}
|
|
67
|
-
<div style={{ display: 'flex', justifyContent: 'flex-end', paddingTop: 'var(--space-l)'}}>
|
|
68
|
-
<Button variant="link" onClick={onCancel}>Cancel</Button>
|
|
69
|
-
<Button variant="cta" onClick={onCreateDocument}>Create {template?.name}</Button>
|
|
70
|
-
</div>
|
|
33
|
+
{error && <Alert>{error}</Alert>}
|
|
71
34
|
</>
|
|
72
35
|
)
|
|
73
36
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { useResource } from '@ossy/sdk-react'
|
|
3
|
+
import { Slot, resourceSlot, View, Title, Text, DelayedRender } from '@ossy/design-system'
|
|
4
|
+
|
|
5
|
+
function GenericResourceCardFallback ({ resource }) {
|
|
6
|
+
if (!resource) return null
|
|
7
|
+
return (
|
|
8
|
+
<View surface="secondary" roundness="m" inset="m" gap="xs">
|
|
9
|
+
<Title variant="secondary">{resource.name}</Title>
|
|
10
|
+
{resource.content?.description && (
|
|
11
|
+
<Text size="s" color="secondary">{resource.content.description}</Text>
|
|
12
|
+
)}
|
|
13
|
+
</View>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Card-sized resource preview — `resource:{type}/card`. */
|
|
18
|
+
export function GenericResourceCard ({ resourceId, resource: resourceProp }) {
|
|
19
|
+
const { resource: loaded } = useResource(resourceId)
|
|
20
|
+
const resource = resourceProp ?? loaded
|
|
21
|
+
|
|
22
|
+
if (!resource) {
|
|
23
|
+
return (
|
|
24
|
+
<DelayedRender>
|
|
25
|
+
<Text>Loading…</Text>
|
|
26
|
+
</DelayedRender>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Slot
|
|
32
|
+
name={resourceSlot(resource.type, 'card')}
|
|
33
|
+
resourceId={resourceId}
|
|
34
|
+
resource={resource}
|
|
35
|
+
fallback={<GenericResourceCardFallback resource={resource} />}
|
|
36
|
+
/>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { useResource, useResourceTemplate } from '@ossy/sdk-react'
|
|
3
|
+
import {
|
|
4
|
+
Slot,
|
|
5
|
+
resourceSlot,
|
|
6
|
+
View,
|
|
7
|
+
Title,
|
|
8
|
+
Text,
|
|
9
|
+
DelayedRender,
|
|
10
|
+
Fields,
|
|
11
|
+
} from '@ossy/design-system'
|
|
12
|
+
import { ResourceGenericView } from './ResourceGenericView.jsx'
|
|
13
|
+
|
|
14
|
+
function GenericResourceDetailFallback ({ resourceId }) {
|
|
15
|
+
const { resource } = useResource(resourceId)
|
|
16
|
+
const template = useResourceTemplate(resource?.type)
|
|
17
|
+
|
|
18
|
+
if (!resource) {
|
|
19
|
+
return (
|
|
20
|
+
<DelayedRender>
|
|
21
|
+
<Text>Loading…</Text>
|
|
22
|
+
</DelayedRender>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (template?.fields?.length) {
|
|
27
|
+
return (
|
|
28
|
+
<View stack gap="m" inset="m">
|
|
29
|
+
<Title>{resource.name}</Title>
|
|
30
|
+
<Fields data={resource.content || {}} fields={template.fields} />
|
|
31
|
+
</View>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return <ResourceGenericView resourceId={resourceId} />
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Generic resource detail host — resolves `resource:{type}/detail` from the slot
|
|
40
|
+
* registry; falls back to template fields or a minimal placeholder.
|
|
41
|
+
*/
|
|
42
|
+
export function GenericResourceDetail ({ resourceId }) {
|
|
43
|
+
const { resource } = useResource(resourceId)
|
|
44
|
+
|
|
45
|
+
if (!resource) {
|
|
46
|
+
return (
|
|
47
|
+
<DelayedRender>
|
|
48
|
+
<Text>Loading…</Text>
|
|
49
|
+
</DelayedRender>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Slot
|
|
55
|
+
name={resourceSlot(resource.type, 'detail')}
|
|
56
|
+
resourceId={resourceId}
|
|
57
|
+
resource={resource}
|
|
58
|
+
fallback={<GenericResourceDetailFallback resourceId={resourceId} />}
|
|
59
|
+
/>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { useResourceTemplate } from '@ossy/sdk-react'
|
|
3
|
+
import {
|
|
4
|
+
Slot,
|
|
5
|
+
resourceSlot,
|
|
6
|
+
View,
|
|
7
|
+
Title,
|
|
8
|
+
Text,
|
|
9
|
+
InputTitle,
|
|
10
|
+
Button,
|
|
11
|
+
Alert,
|
|
12
|
+
Fields,
|
|
13
|
+
applyFieldChange,
|
|
14
|
+
useInputValue,
|
|
15
|
+
} from '@ossy/design-system'
|
|
16
|
+
|
|
17
|
+
function GenericResourceFormFallback ({
|
|
18
|
+
templateId,
|
|
19
|
+
mode = 'create',
|
|
20
|
+
initialName = '',
|
|
21
|
+
initialContent = {},
|
|
22
|
+
onSubmit,
|
|
23
|
+
onCancel,
|
|
24
|
+
submitLabel,
|
|
25
|
+
}) {
|
|
26
|
+
const template = useResourceTemplate(templateId)
|
|
27
|
+
const [name, setName] = useInputValue(initialName)
|
|
28
|
+
const [content, setContent] = useState(initialContent)
|
|
29
|
+
const [error, setError] = useState()
|
|
30
|
+
|
|
31
|
+
const handleSubmit = () => {
|
|
32
|
+
if (!name?.trim()) {
|
|
33
|
+
setError('Name is required')
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
setError(undefined)
|
|
37
|
+
onSubmit?.({ name: name.trim(), content, type: templateId })
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<View stack gap="m">
|
|
42
|
+
<InputTitle
|
|
43
|
+
id="resource-name"
|
|
44
|
+
type="text"
|
|
45
|
+
placeholder="Untitled"
|
|
46
|
+
value={name}
|
|
47
|
+
onChange={setName}
|
|
48
|
+
/>
|
|
49
|
+
<Fields
|
|
50
|
+
data={content}
|
|
51
|
+
onChange={(event) => setContent((prev) => applyFieldChange(prev, event))}
|
|
52
|
+
fields={template?.fields || []}
|
|
53
|
+
/>
|
|
54
|
+
{error && <Alert>{error}</Alert>}
|
|
55
|
+
<View layout="row" gap="s" justifyContent="flex-end">
|
|
56
|
+
{onCancel && <Button variant="link" onClick={onCancel}>Cancel</Button>}
|
|
57
|
+
<Button variant="cta" onClick={handleSubmit}>
|
|
58
|
+
{submitLabel || (mode === 'edit' ? 'Save' : `Create ${template?.name || 'resource'}`)}
|
|
59
|
+
</Button>
|
|
60
|
+
</View>
|
|
61
|
+
</View>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Generic resource create/edit host — resolves `resource:{type}/form` from the
|
|
67
|
+
* slot registry; falls back to template-driven Fields.
|
|
68
|
+
*/
|
|
69
|
+
export function GenericResourceForm ({
|
|
70
|
+
templateId,
|
|
71
|
+
mode = 'create',
|
|
72
|
+
resourceId,
|
|
73
|
+
resource,
|
|
74
|
+
initialName,
|
|
75
|
+
initialContent,
|
|
76
|
+
onSubmit,
|
|
77
|
+
onCancel,
|
|
78
|
+
submitLabel,
|
|
79
|
+
}) {
|
|
80
|
+
if (!templateId) {
|
|
81
|
+
return <Text>Select a resource type to continue.</Text>
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<Slot
|
|
86
|
+
name={resourceSlot(templateId, 'form')}
|
|
87
|
+
templateId={templateId}
|
|
88
|
+
mode={mode}
|
|
89
|
+
resourceId={resourceId}
|
|
90
|
+
resource={resource}
|
|
91
|
+
onSubmit={onSubmit}
|
|
92
|
+
onCancel={onCancel}
|
|
93
|
+
fallback={
|
|
94
|
+
<GenericResourceFormFallback
|
|
95
|
+
templateId={templateId}
|
|
96
|
+
mode={mode}
|
|
97
|
+
initialName={initialName ?? resource?.name}
|
|
98
|
+
initialContent={initialContent ?? resource?.content ?? {}}
|
|
99
|
+
onSubmit={onSubmit}
|
|
100
|
+
onCancel={onCancel}
|
|
101
|
+
submitLabel={submitLabel}
|
|
102
|
+
/>
|
|
103
|
+
}
|
|
104
|
+
/>
|
|
105
|
+
)
|
|
106
|
+
}
|
package/src/ImageResource.jsx
CHANGED
package/src/PDFResource.jsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { useResource } from '@ossy/sdk-react'
|
|
3
|
-
import {
|
|
3
|
+
import { View } from '@ossy/design-system'
|
|
4
4
|
|
|
5
5
|
export const PDFResource = ({
|
|
6
6
|
resourceId
|
|
@@ -8,13 +8,13 @@ export const PDFResource = ({
|
|
|
8
8
|
const { resource } = useResource(resourceId)
|
|
9
9
|
|
|
10
10
|
return (
|
|
11
|
-
<
|
|
12
|
-
<
|
|
13
|
-
<
|
|
11
|
+
<View stack bordered>
|
|
12
|
+
<View.Item fill>
|
|
13
|
+
<View stack bordered>
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
<
|
|
17
|
+
<View.Item fill style={{ padding: '16px 8px' }}>
|
|
18
18
|
|
|
19
19
|
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
|
20
20
|
<embed
|
|
@@ -24,10 +24,10 @@ export const PDFResource = ({
|
|
|
24
24
|
height="800px"
|
|
25
25
|
/>
|
|
26
26
|
</div>
|
|
27
|
-
</
|
|
27
|
+
</View.Item>
|
|
28
28
|
|
|
29
|
-
</
|
|
30
|
-
</
|
|
31
|
-
</
|
|
29
|
+
</View>
|
|
30
|
+
</View.Item>
|
|
31
|
+
</View>
|
|
32
32
|
)
|
|
33
33
|
}
|
|
@@ -55,7 +55,7 @@ export const ResourceContentPage = (props) => {
|
|
|
55
55
|
|
|
56
56
|
<View layout="row" style={{ justifyContent: 'space-between', borderRadius: 'var(--space-l)', padding: 'var(--space-xxs)', background: 'transparent' }}>
|
|
57
57
|
<Button variant="neutral" onClick={() => router.back()} style={{ padding: 'var(--space-xs)', borderRadius: '50%' }}>
|
|
58
|
-
<Icon name="
|
|
58
|
+
<Icon name="chevron-left" />
|
|
59
59
|
</Button>
|
|
60
60
|
</View>
|
|
61
61
|
|
package/src/ResourceFactory.jsx
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { useResource, AsyncStatus, useWorkspace } from '@ossy/sdk-react'
|
|
3
3
|
import { Guide, Text, DelayedRender } from '@ossy/design-system'
|
|
4
|
-
import { DocumentView } from './DocumentView.jsx'
|
|
5
4
|
import { DocumentEdit } from './DocumentEdit.jsx'
|
|
6
5
|
import { ImageResource } from './ImageResource.jsx'
|
|
7
6
|
import { AudioResource } from './AudioResource.jsx'
|
|
8
7
|
import { VideoResource } from './VideoResource.jsx'
|
|
9
8
|
import { PDFResource } from './PDFResource.jsx'
|
|
10
9
|
import { ResourceGenericView } from './ResourceGenericView.jsx'
|
|
10
|
+
import { GenericResourceDetail } from './GenericResourceDetail.jsx'
|
|
11
11
|
|
|
12
12
|
export const ResourceFactory = ({
|
|
13
13
|
resourceId,
|
|
@@ -39,8 +39,12 @@ export const ResourceFactory = ({
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
if (workspace?.resourceTemplates?.find(({ id }) => id === resource.type)) {
|
|
42
|
-
if (mode === 'View')
|
|
43
|
-
|
|
42
|
+
if (mode === 'View') {
|
|
43
|
+
return <GenericResourceDetail resourceId={resourceId} />
|
|
44
|
+
}
|
|
45
|
+
if (mode === 'Edit') {
|
|
46
|
+
return <DocumentEdit resourceId={resourceId} form={form} />
|
|
47
|
+
}
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
if (['image/jpeg', 'image/png'].includes(resource.type)) {
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { useResource } from '@ossy/sdk-react'
|
|
3
3
|
import {
|
|
4
|
-
Stack,
|
|
5
4
|
View,
|
|
6
|
-
|
|
5
|
+
Icon,
|
|
7
6
|
} from '@ossy/design-system'
|
|
8
7
|
|
|
9
8
|
export const ResourceGenericView = ({
|
|
@@ -12,21 +11,21 @@ export const ResourceGenericView = ({
|
|
|
12
11
|
const { resource } = useResource(resourceId)
|
|
13
12
|
|
|
14
13
|
return (
|
|
15
|
-
<
|
|
16
|
-
<
|
|
17
|
-
<
|
|
14
|
+
<View stack bordered>
|
|
15
|
+
<View.Item fill>
|
|
16
|
+
<View stack bordered>
|
|
18
17
|
|
|
19
|
-
<
|
|
18
|
+
<View.Item fill style={{ padding: '16px 8px' }}>
|
|
20
19
|
|
|
21
20
|
<View alignItems="center" justifyContent="center" style={{ height: '100%', maxHeight: '400px' }}>
|
|
22
21
|
<View roundness="m" style={{ border: '1px solid var(--separator)', padding: 'var(--space-xl) var(--space-xl)'}}>
|
|
23
|
-
<
|
|
22
|
+
<Icon size="xl" name="file" />
|
|
24
23
|
</View>
|
|
25
24
|
</View>
|
|
26
|
-
</
|
|
25
|
+
</View.Item>
|
|
27
26
|
|
|
28
|
-
</
|
|
29
|
-
</
|
|
30
|
-
</
|
|
27
|
+
</View>
|
|
28
|
+
</View.Item>
|
|
29
|
+
</View>
|
|
31
30
|
)
|
|
32
31
|
}
|
package/src/ResourceList.jsx
CHANGED
|
@@ -4,7 +4,7 @@ import { useWorkspace } from '@ossy/sdk-react'
|
|
|
4
4
|
import {
|
|
5
5
|
Dropdown,
|
|
6
6
|
DropZone,
|
|
7
|
-
|
|
7
|
+
Icon,
|
|
8
8
|
Image,
|
|
9
9
|
View,
|
|
10
10
|
Text,
|
|
@@ -73,7 +73,7 @@ function InlineFolderRow({ name, onChange, onSave, onCancel }) {
|
|
|
73
73
|
}}
|
|
74
74
|
>
|
|
75
75
|
<View gap="m" layout="row" alignItems="center" style={{ flexGrow: 1, padding: '12px 0 12px 20px' }}>
|
|
76
|
-
<
|
|
76
|
+
<Icon size="s" name="folder" style={{ fill: 'hsl(0, 0%, 60%)' }} />
|
|
77
77
|
<input
|
|
78
78
|
ref={inputRef}
|
|
79
79
|
value={name}
|
|
@@ -163,7 +163,7 @@ function ResourceListItem({
|
|
|
163
163
|
|
|
164
164
|
if (resource?.type === 'directory') {
|
|
165
165
|
return (
|
|
166
|
-
<
|
|
166
|
+
<Icon size="s" name="folder" style={{ fill: 'hsl(0, 0%, 60%)'}} />
|
|
167
167
|
)
|
|
168
168
|
}
|
|
169
169
|
|
|
@@ -178,7 +178,7 @@ function ResourceListItem({
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
return (
|
|
181
|
-
<
|
|
181
|
+
<Icon size="s" name={resourceTemplates[resource.type]?.icon || 'file'} style={{ fill: 'hsl(0, 0%, 80%)'}} />
|
|
182
182
|
)
|
|
183
183
|
}
|
|
184
184
|
|
package/src/ResourcePanel.jsx
CHANGED
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
Title,
|
|
5
5
|
Overlay,
|
|
6
6
|
Switch,
|
|
7
|
-
Stack,
|
|
8
7
|
Guide,
|
|
9
8
|
View,
|
|
10
9
|
Text,
|
|
@@ -133,7 +132,7 @@ export const ResourcePanel = ({
|
|
|
133
132
|
}
|
|
134
133
|
|
|
135
134
|
return (
|
|
136
|
-
<
|
|
135
|
+
<View stack surface="primary" bordered style={{ height: '100%', width: '50%' }}>
|
|
137
136
|
|
|
138
137
|
<style href="@design-system/scroll" precedence='low'>{`
|
|
139
138
|
[data-scroll-hide] {
|
|
@@ -148,23 +147,23 @@ export const ResourcePanel = ({
|
|
|
148
147
|
|
|
149
148
|
{
|
|
150
149
|
!!error && (
|
|
151
|
-
<
|
|
150
|
+
<View.Item>
|
|
152
151
|
{error}
|
|
153
|
-
</
|
|
152
|
+
</View.Item>
|
|
154
153
|
)
|
|
155
154
|
}
|
|
156
155
|
|
|
157
|
-
<
|
|
156
|
+
<View.Item>
|
|
158
157
|
|
|
159
|
-
<
|
|
158
|
+
<View stack horizontal style={{ height: '48px', alignItems: 'center', gap: '4px' }}>
|
|
160
159
|
|
|
161
160
|
<Switch on={panelViewModes['panel-header']}>
|
|
162
161
|
|
|
163
162
|
<Switch.Case match={[ViewMode.View]}>
|
|
164
163
|
|
|
165
|
-
<
|
|
164
|
+
<View.Item fill surface="primary" style={{ padding: '4px 8px' }}>
|
|
166
165
|
<Title as="h3" variant="tertiary">{resourceName}</Title>
|
|
167
|
-
</
|
|
166
|
+
</View.Item>
|
|
168
167
|
|
|
169
168
|
<Button prefix="trash-empty" variant="command-danger" onClick={onRemoveResource}/>
|
|
170
169
|
<Button prefix="pen" variant="command" onClick={() => setPanelViewModes(x => ({ ...x, 'panel-header': ViewMode.Edit }))} />
|
|
@@ -174,7 +173,7 @@ export const ResourcePanel = ({
|
|
|
174
173
|
|
|
175
174
|
<Switch.Case match={[ViewMode.Edit]}>
|
|
176
175
|
|
|
177
|
-
<
|
|
176
|
+
<View.Item fill surface="primary" style={{ padding: '4px 8px' }}>
|
|
178
177
|
<InputTitle
|
|
179
178
|
id="document-name"
|
|
180
179
|
type="text"
|
|
@@ -183,7 +182,7 @@ export const ResourcePanel = ({
|
|
|
183
182
|
onChange={setResourceName}
|
|
184
183
|
onBlur={onRenameResource}
|
|
185
184
|
/>
|
|
186
|
-
</
|
|
185
|
+
</View.Item>
|
|
187
186
|
|
|
188
187
|
<Button prefix="close" variant="command" onClick={() => setPanelViewModes(x => ({ ...x, 'panel-header': ViewMode.View }))} />
|
|
189
188
|
<Button prefix="check" variant="command" onClick={onRenameResource}/>
|
|
@@ -192,11 +191,11 @@ export const ResourcePanel = ({
|
|
|
192
191
|
|
|
193
192
|
</Switch>
|
|
194
193
|
|
|
195
|
-
</
|
|
194
|
+
</View>
|
|
196
195
|
|
|
197
|
-
</
|
|
196
|
+
</View.Item>
|
|
198
197
|
|
|
199
|
-
<
|
|
198
|
+
<View.Item fill={true} data-scroll-hide style={{ display: 'flex', flexDirection: 'column', height: '100%', overflowY: 'auto' }}>
|
|
200
199
|
|
|
201
200
|
{panels.map(({ content: Content, ...panel }) => {
|
|
202
201
|
const isExpanded = [ViewMode.View, ViewMode.Edit].includes(panelViewModes[panel.id])
|
|
@@ -239,7 +238,7 @@ export const ResourcePanel = ({
|
|
|
239
238
|
)
|
|
240
239
|
})}
|
|
241
240
|
|
|
242
|
-
</
|
|
241
|
+
</View.Item>
|
|
243
242
|
|
|
244
243
|
{
|
|
245
244
|
overlay === Overlays.RemoveDirectory && (
|
|
@@ -267,6 +266,6 @@ export const ResourcePanel = ({
|
|
|
267
266
|
)
|
|
268
267
|
}
|
|
269
268
|
|
|
270
|
-
</
|
|
269
|
+
</View>
|
|
271
270
|
)
|
|
272
271
|
}
|
package/src/Upload.jsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react'
|
|
2
|
-
import { Button, View, Text, Upload as _Upload,
|
|
2
|
+
import { Button, View, Text, Upload as _Upload, Icon } from '@ossy/design-system'
|
|
3
3
|
import { useRouter } from '@ossy/router-react'
|
|
4
4
|
|
|
5
5
|
const FlowStage = {
|
|
@@ -155,9 +155,9 @@ function UploadImgPreview({ file, size = "32px" }) {
|
|
|
155
155
|
|
|
156
156
|
function UploadStatus({ status }) {
|
|
157
157
|
if (status === 'preview') return <></>
|
|
158
|
-
if (status === 'uploading') return <
|
|
159
|
-
if (status === 'uploaded') return <
|
|
160
|
-
if (status === 'error') return <
|
|
158
|
+
if (status === 'uploading') return <Icon name="spinner" size="s" animation="rotate" />
|
|
159
|
+
if (status === 'uploaded') return <Icon name="check" size="s" />
|
|
160
|
+
if (status === 'error') return <Icon name="close" size="s" />
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
function getFlowStage(files = []) {
|
package/src/VideoResource.jsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { useResource } from '@ossy/sdk-react'
|
|
3
|
-
import {
|
|
3
|
+
import { View } from '@ossy/design-system'
|
|
4
4
|
|
|
5
5
|
export const VideoResource = ({
|
|
6
6
|
resourceId
|
|
@@ -8,8 +8,8 @@ export const VideoResource = ({
|
|
|
8
8
|
const { resource } = useResource(resourceId)
|
|
9
9
|
|
|
10
10
|
return (
|
|
11
|
-
<
|
|
12
|
-
<
|
|
11
|
+
<View stack bordered>
|
|
12
|
+
<View.Item fill style={{ padding: '16px 8px' }}>
|
|
13
13
|
|
|
14
14
|
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
|
15
15
|
<video
|
|
@@ -18,7 +18,7 @@ export const VideoResource = ({
|
|
|
18
18
|
style={{ width: 'auto', height: '400px', margin: 'var(--space-l) auto' }}
|
|
19
19
|
/>
|
|
20
20
|
</div>
|
|
21
|
-
</
|
|
22
|
-
</
|
|
21
|
+
</View.Item>
|
|
22
|
+
</View>
|
|
23
23
|
)
|
|
24
24
|
}
|
package/src/index.js
CHANGED
|
@@ -15,6 +15,9 @@ export * from './ResourceDescription.jsx'
|
|
|
15
15
|
export * from './ResourceDetails.jsx'
|
|
16
16
|
export * from './ResourceDialogMove.jsx'
|
|
17
17
|
export * from './ResourceFactory.jsx'
|
|
18
|
+
export * from './GenericResourceDetail.jsx'
|
|
19
|
+
export * from './GenericResourceForm.jsx'
|
|
20
|
+
export * from './GenericResourceCard.jsx'
|
|
18
21
|
export * from './ResourceGenericView.jsx'
|
|
19
22
|
export * from './ResourceList.jsx'
|
|
20
23
|
export * from './ResourcePage.jsx'
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { useResources } from '@ossy/sdk-react'
|
|
3
|
+
import { View } from '@ossy/design-system'
|
|
4
|
+
import { useRouter } from '@ossy/router-react'
|
|
5
|
+
import { GenericResourceForm } from './GenericResourceForm.jsx'
|
|
6
|
+
|
|
7
|
+
export const metadata = {
|
|
8
|
+
id: 'resources/create/generic',
|
|
9
|
+
path: {
|
|
10
|
+
en: '/resources/create/:templateId',
|
|
11
|
+
sv: '/resources/skapa/:templateId',
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Generic resource create page — slot lookup with template Fields fallback. */
|
|
16
|
+
export default function GenericResourceCreatePage () {
|
|
17
|
+
const router = useRouter()
|
|
18
|
+
const templateId = decodeURIComponent(router.params.templateId || '')
|
|
19
|
+
const location = router.searchParams.location
|
|
20
|
+
const { createDocument } = useResources()
|
|
21
|
+
const [error, setError] = useState()
|
|
22
|
+
|
|
23
|
+
const onCancel = () => {
|
|
24
|
+
const search = location ? new URLSearchParams({ location }).toString() : ''
|
|
25
|
+
router.navigate(`@storage/home${search ? `?${search}` : ''}`)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const onSubmit = ({ name, content, type }) => {
|
|
29
|
+
createDocument({ type, location, name, content })
|
|
30
|
+
.then(() => onCancel())
|
|
31
|
+
.catch((err) => setError(err?.message || 'Create failed'))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<View layout="off-center-m" style={{ height: '100%' }}>
|
|
36
|
+
<View slot="content" surface="primary" roundness="m" inset="m">
|
|
37
|
+
<GenericResourceForm
|
|
38
|
+
templateId={templateId}
|
|
39
|
+
mode="create"
|
|
40
|
+
onSubmit={onSubmit}
|
|
41
|
+
onCancel={onCancel}
|
|
42
|
+
/>
|
|
43
|
+
{error && <View inset="s"><span style={{ color: 'var(--danger)' }}>{error}</span></View>}
|
|
44
|
+
</View>
|
|
45
|
+
</View>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { View } from '@ossy/design-system'
|
|
3
|
+
import { useRouter } from '@ossy/router-react'
|
|
4
|
+
import { GenericResourceDetail } from './GenericResourceDetail.jsx'
|
|
5
|
+
|
|
6
|
+
export const metadata = {
|
|
7
|
+
id: '@resource',
|
|
8
|
+
title: 'Resource',
|
|
9
|
+
path: {
|
|
10
|
+
en: '/resources/item/:resourceId',
|
|
11
|
+
sv: '/resources/objekt/:resourceId',
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Generic resource detail page — slot lookup with template fallback. */
|
|
16
|
+
export default function ResourceDetailPage () {
|
|
17
|
+
const router = useRouter()
|
|
18
|
+
const resourceId = router.params.resourceId
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<View layout="off-center-m" style={{ height: '100%' }}>
|
|
22
|
+
<View slot="content" surface="primary" roundness="m" inset="m">
|
|
23
|
+
<GenericResourceDetail resourceId={resourceId} />
|
|
24
|
+
</View>
|
|
25
|
+
</View>
|
|
26
|
+
)
|
|
27
|
+
}
|