@cogito.ai/astryx-web 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +34 -2
- package/astryx.integration.mjs +15 -0
- package/bin/astryx-web.mjs +353 -0
- package/docs/dock-accessibility.doc.mjs +24 -0
- package/docs/dock-components.doc.mjs +26 -0
- package/docs/dock-copy.doc.mjs +23 -0
- package/docs/dock-design-base.doc.mjs +35 -0
- package/docs/dock-environment.doc.mjs +24 -0
- package/docs/dock-layout.doc.mjs +25 -0
- package/docs/dock-product-stance.doc.mjs +25 -0
- package/docs/dock-refuse-list.doc.mjs +34 -0
- package/docs/dock-states.doc.mjs +23 -0
- package/docs/dock-style-archetypes.doc.mjs +104 -0
- package/docs/dock-touch-mobile.doc.mjs +27 -0
- package/docs/dock-visual-acceptance.doc.mjs +23 -0
- package/package.json +51 -3
- package/skills/astryx-web-design/SKILL.md +21 -0
- package/skills/astryx-web-design/references/acceptance.md +12 -0
- package/skills/astryx-web-design/references/design-direction.md +52 -0
- package/skills/astryx-web-design/references/page-building.md +18 -0
- package/templates/dock-editor-frame.doc.mjs +19 -0
- package/templates/dock-editor-frame.tsx +229 -0
- package/templates/dock-form-flow.doc.mjs +19 -0
- package/templates/dock-form-flow.tsx +169 -0
- package/templates/dock-landing.doc.mjs +11 -0
- package/templates/dock-landing.tsx +104 -0
- package/templates/dock-list.doc.mjs +20 -0
- package/templates/dock-list.tsx +223 -0
- package/templates/dock-settings.doc.mjs +19 -0
- package/templates/dock-settings.tsx +272 -0
- package/templates/dock-workbench.doc.mjs +11 -0
- package/templates/dock-workbench.tsx +193 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { Button } from '@astryxdesign/core/Button'
|
|
2
|
+
import { FormLayout } from '@astryxdesign/core/FormLayout'
|
|
3
|
+
import { Heading } from '@astryxdesign/core/Heading'
|
|
4
|
+
import { Stack } from '@astryxdesign/core/Stack'
|
|
5
|
+
import { Text } from '@astryxdesign/core/Text'
|
|
6
|
+
import { TextArea } from '@astryxdesign/core/TextArea'
|
|
7
|
+
import { TextInput } from '@astryxdesign/core/TextInput'
|
|
8
|
+
import { useTranslator } from '@astryxdesign/core/i18n'
|
|
9
|
+
import * as stylex from '@stylexjs/stylex'
|
|
10
|
+
import { useState } from 'react'
|
|
11
|
+
import { PageShell } from '@/components/layout'
|
|
12
|
+
import { useCurrentLocale } from '@/i18n'
|
|
13
|
+
|
|
14
|
+
const styles = stylex.create({
|
|
15
|
+
fields: {
|
|
16
|
+
display: 'grid',
|
|
17
|
+
gridTemplateColumns: 'minmax(0, 1fr)',
|
|
18
|
+
gap: '0.75rem',
|
|
19
|
+
'@container (min-width: 640px)': {
|
|
20
|
+
gridTemplateColumns: 'auto minmax(0, 1fr)',
|
|
21
|
+
gap: '0.75rem 1rem',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
actions: {
|
|
25
|
+
position: 'sticky',
|
|
26
|
+
bottom: 0,
|
|
27
|
+
marginBlockStart: 'auto',
|
|
28
|
+
paddingBlock: '0.75rem',
|
|
29
|
+
paddingBlockEnd: 'max(0.75rem, env(safe-area-inset-bottom))',
|
|
30
|
+
backgroundColor: 'var(--color-background-surface)',
|
|
31
|
+
'@container (min-width: 640px)': {
|
|
32
|
+
position: 'static',
|
|
33
|
+
justifyContent: 'flex-end',
|
|
34
|
+
marginBlockStart: 0,
|
|
35
|
+
paddingBlock: 0,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
type Step = 0 | 1
|
|
41
|
+
|
|
42
|
+
export default function DockFormFlow() {
|
|
43
|
+
const locale = useCurrentLocale()
|
|
44
|
+
const t = useTranslator()
|
|
45
|
+
const [step, setStep] = useState<Step>(0)
|
|
46
|
+
const [name, setName] = useState('')
|
|
47
|
+
const [details, setDetails] = useState('')
|
|
48
|
+
const [error, setError] = useState<string | null>(null)
|
|
49
|
+
|
|
50
|
+
function goNext() {
|
|
51
|
+
if (!name.trim()) {
|
|
52
|
+
setError(t('page.__PAGE__.error.name'))
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
setError(null)
|
|
56
|
+
setStep(1)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<PageShell
|
|
61
|
+
title={t('page.__PAGE__.title')}
|
|
62
|
+
backHref={`/${locale}`}
|
|
63
|
+
backLabel={t('page.__PAGE__.back')}
|
|
64
|
+
>
|
|
65
|
+
<Stack gap={4} minHeight="100%">
|
|
66
|
+
<Text type="supporting" color="secondary">
|
|
67
|
+
{t('page.__PAGE__.step', { current: step + 1, total: 2 })}
|
|
68
|
+
</Text>
|
|
69
|
+
<Stack gap={1}>
|
|
70
|
+
<Heading level={2}>
|
|
71
|
+
{step === 0 ? t('page.__PAGE__.step.details') : t('page.__PAGE__.step.review')}
|
|
72
|
+
</Heading>
|
|
73
|
+
<Text type="supporting" color="secondary">
|
|
74
|
+
{step === 0
|
|
75
|
+
? t('page.__PAGE__.step.details.description')
|
|
76
|
+
: t('page.__PAGE__.step.review.description')}
|
|
77
|
+
</Text>
|
|
78
|
+
</Stack>
|
|
79
|
+
|
|
80
|
+
{step === 0 ? (
|
|
81
|
+
<Stack gap={4}>
|
|
82
|
+
<FormLayout direction="horizontal-labels" xstyle={styles.fields}>
|
|
83
|
+
<TextInput
|
|
84
|
+
label={t('page.__PAGE__.field.name')}
|
|
85
|
+
value={name}
|
|
86
|
+
onChange={(value) => {
|
|
87
|
+
setName(value)
|
|
88
|
+
if (error) setError(null)
|
|
89
|
+
}}
|
|
90
|
+
placeholder={t('page.__PAGE__.field.name.placeholder')}
|
|
91
|
+
isRequired
|
|
92
|
+
status={error ? { type: 'error', message: error } : undefined}
|
|
93
|
+
/>
|
|
94
|
+
</FormLayout>
|
|
95
|
+
<TextArea
|
|
96
|
+
label={t('page.__PAGE__.field.details')}
|
|
97
|
+
value={details}
|
|
98
|
+
onChange={setDetails}
|
|
99
|
+
placeholder={t('page.__PAGE__.field.details.placeholder')}
|
|
100
|
+
/>
|
|
101
|
+
</Stack>
|
|
102
|
+
) : (
|
|
103
|
+
<Stack gap={2}>
|
|
104
|
+
<Text type="body">{name}</Text>
|
|
105
|
+
<Text type="supporting" color="secondary">
|
|
106
|
+
{details || t('page.__PAGE__.review.noDetails')}
|
|
107
|
+
</Text>
|
|
108
|
+
</Stack>
|
|
109
|
+
)}
|
|
110
|
+
|
|
111
|
+
<Stack direction="horizontal" hAlign="between" gap={2} width="100%" xstyle={styles.actions}>
|
|
112
|
+
<Button
|
|
113
|
+
label={t('page.__PAGE__.backStep')}
|
|
114
|
+
variant="secondary"
|
|
115
|
+
isDisabled={step === 0}
|
|
116
|
+
onClick={() => setStep(0)}
|
|
117
|
+
/>
|
|
118
|
+
<Button
|
|
119
|
+
label={step === 0 ? t('page.__PAGE__.next') : t('page.__PAGE__.create')}
|
|
120
|
+
variant="primary"
|
|
121
|
+
onClick={step === 0 ? goNext : () => undefined}
|
|
122
|
+
/>
|
|
123
|
+
</Stack>
|
|
124
|
+
</Stack>
|
|
125
|
+
</PageShell>
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/*
|
|
130
|
+
astryx-messages
|
|
131
|
+
{
|
|
132
|
+
"en": {
|
|
133
|
+
"page.__PAGE__.title": "Create item",
|
|
134
|
+
"page.__PAGE__.back": "Back",
|
|
135
|
+
"page.__PAGE__.step": "Step {current} of {total}",
|
|
136
|
+
"page.__PAGE__.step.details": "Details",
|
|
137
|
+
"page.__PAGE__.step.review": "Review",
|
|
138
|
+
"page.__PAGE__.step.details.description": "Add the information needed for this item.",
|
|
139
|
+
"page.__PAGE__.step.review.description": "Check the information before creating the item.",
|
|
140
|
+
"page.__PAGE__.field.name": "Name",
|
|
141
|
+
"page.__PAGE__.field.name.placeholder": "Name this item",
|
|
142
|
+
"page.__PAGE__.field.details": "Details",
|
|
143
|
+
"page.__PAGE__.field.details.placeholder": "Add anything the next person should know",
|
|
144
|
+
"page.__PAGE__.error.name": "Enter a name to continue.",
|
|
145
|
+
"page.__PAGE__.review.noDetails": "No details added.",
|
|
146
|
+
"page.__PAGE__.backStep": "Back",
|
|
147
|
+
"page.__PAGE__.next": "Next",
|
|
148
|
+
"page.__PAGE__.create": "Create"
|
|
149
|
+
},
|
|
150
|
+
"zh-CN": {
|
|
151
|
+
"page.__PAGE__.title": "创建事项",
|
|
152
|
+
"page.__PAGE__.back": "返回",
|
|
153
|
+
"page.__PAGE__.step": "第 {current} 步,共 {total} 步",
|
|
154
|
+
"page.__PAGE__.step.details": "详细信息",
|
|
155
|
+
"page.__PAGE__.step.review": "检查",
|
|
156
|
+
"page.__PAGE__.step.details.description": "添加此事项所需的信息。",
|
|
157
|
+
"page.__PAGE__.step.review.description": "创建前检查这些信息。",
|
|
158
|
+
"page.__PAGE__.field.name": "名称",
|
|
159
|
+
"page.__PAGE__.field.name.placeholder": "为此事项命名",
|
|
160
|
+
"page.__PAGE__.field.details": "详细信息",
|
|
161
|
+
"page.__PAGE__.field.details.placeholder": "添加下一位处理者需要了解的内容",
|
|
162
|
+
"page.__PAGE__.error.name": "请输入名称后继续。",
|
|
163
|
+
"page.__PAGE__.review.noDetails": "尚未添加详细信息。",
|
|
164
|
+
"page.__PAGE__.backStep": "上一步",
|
|
165
|
+
"page.__PAGE__.next": "下一步",
|
|
166
|
+
"page.__PAGE__.create": "创建"
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
*/
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** @type {import('@astryxdesign/cli/authoring').IntegrationTemplateDoc} */
|
|
2
|
+
export default {
|
|
3
|
+
type: 'page',
|
|
4
|
+
name: 'Landing page',
|
|
5
|
+
displayName: 'Landing page',
|
|
6
|
+
description:
|
|
7
|
+
'A first-level landing page with a short orientation line and module entries that lead one route deeper.',
|
|
8
|
+
category: 'Landing - Modules',
|
|
9
|
+
keywords: ['landing page', 'home', 'index', 'module entries', 'mobile', 'mobile landing'],
|
|
10
|
+
isReady: true,
|
|
11
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Blockquote } from '@astryxdesign/core/Blockquote'
|
|
2
|
+
import { ClickableCard } from '@astryxdesign/core/ClickableCard'
|
|
3
|
+
import { Grid } from '@astryxdesign/core/Grid'
|
|
4
|
+
import { Heading } from '@astryxdesign/core/Heading'
|
|
5
|
+
import { Stack } from '@astryxdesign/core/Stack'
|
|
6
|
+
import { Text } from '@astryxdesign/core/Text'
|
|
7
|
+
import { VisuallyHidden } from '@astryxdesign/core/VisuallyHidden'
|
|
8
|
+
import { useTranslator } from '@astryxdesign/core/i18n'
|
|
9
|
+
import * as stylex from '@stylexjs/stylex'
|
|
10
|
+
import { PageContainer } from '@/components/layout'
|
|
11
|
+
import { useCurrentLocale } from '@/i18n'
|
|
12
|
+
|
|
13
|
+
const styles = stylex.create({
|
|
14
|
+
entries: {
|
|
15
|
+
gridTemplateColumns: 'minmax(0, 1fr)',
|
|
16
|
+
'@container (min-width: 600px)': {
|
|
17
|
+
gridTemplateColumns: 'repeat(3, minmax(0, 1fr))',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const SAMPLE_MODULES = [
|
|
23
|
+
{
|
|
24
|
+
id: 'primary',
|
|
25
|
+
path: 'primary',
|
|
26
|
+
titleKey: 'page.__PAGE__.module.primary.title',
|
|
27
|
+
descriptionKey: 'page.__PAGE__.module.primary.description',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 'secondary',
|
|
31
|
+
path: 'secondary',
|
|
32
|
+
titleKey: 'page.__PAGE__.module.secondary.title',
|
|
33
|
+
descriptionKey: 'page.__PAGE__.module.secondary.description',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'settings',
|
|
37
|
+
path: 'settings',
|
|
38
|
+
titleKey: 'page.__PAGE__.module.settings.title',
|
|
39
|
+
descriptionKey: 'page.__PAGE__.module.settings.description',
|
|
40
|
+
},
|
|
41
|
+
] as const
|
|
42
|
+
// Replace with feature data
|
|
43
|
+
|
|
44
|
+
export default function DockLanding() {
|
|
45
|
+
const locale = useCurrentLocale()
|
|
46
|
+
const t = useTranslator()
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<PageContainer>
|
|
50
|
+
<VisuallyHidden>
|
|
51
|
+
<Heading level={1}>{t('page.__PAGE__.title')}</Heading>
|
|
52
|
+
</VisuallyHidden>
|
|
53
|
+
<Blockquote>{t('page.__PAGE__.orientation')}</Blockquote>
|
|
54
|
+
<Grid columns={3} gap={3} xstyle={styles.entries}>
|
|
55
|
+
{SAMPLE_MODULES.map((module) => {
|
|
56
|
+
const title = t(module.titleKey)
|
|
57
|
+
return (
|
|
58
|
+
<ClickableCard
|
|
59
|
+
key={module.id}
|
|
60
|
+
label={title}
|
|
61
|
+
href={`/${locale}/${module.path}`}
|
|
62
|
+
padding={3}
|
|
63
|
+
>
|
|
64
|
+
<Stack gap={1}>
|
|
65
|
+
<Text type="body" weight="semibold">
|
|
66
|
+
{title}
|
|
67
|
+
</Text>
|
|
68
|
+
<Text type="supporting" color="secondary">
|
|
69
|
+
{t(module.descriptionKey)}
|
|
70
|
+
</Text>
|
|
71
|
+
</Stack>
|
|
72
|
+
</ClickableCard>
|
|
73
|
+
)
|
|
74
|
+
})}
|
|
75
|
+
</Grid>
|
|
76
|
+
</PageContainer>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/*
|
|
81
|
+
astryx-messages
|
|
82
|
+
{
|
|
83
|
+
"en": {
|
|
84
|
+
"page.__PAGE__.title": "Overview",
|
|
85
|
+
"page.__PAGE__.orientation": "Choose a module to continue.",
|
|
86
|
+
"page.__PAGE__.module.primary.title": "Primary workspace",
|
|
87
|
+
"page.__PAGE__.module.primary.description": "Open the main work for this page.",
|
|
88
|
+
"page.__PAGE__.module.secondary.title": "Recent activity",
|
|
89
|
+
"page.__PAGE__.module.secondary.description": "Review the latest changes and decisions.",
|
|
90
|
+
"page.__PAGE__.module.settings.title": "Settings",
|
|
91
|
+
"page.__PAGE__.module.settings.description": "Adjust preferences for this workspace."
|
|
92
|
+
},
|
|
93
|
+
"zh-CN": {
|
|
94
|
+
"page.__PAGE__.title": "概览",
|
|
95
|
+
"page.__PAGE__.orientation": "选择一个模块继续。",
|
|
96
|
+
"page.__PAGE__.module.primary.title": "主工作区",
|
|
97
|
+
"page.__PAGE__.module.primary.description": "打开此页面的主要工作。",
|
|
98
|
+
"page.__PAGE__.module.secondary.title": "最近活动",
|
|
99
|
+
"page.__PAGE__.module.secondary.description": "查看最近的更改与决定。",
|
|
100
|
+
"page.__PAGE__.module.settings.title": "设置",
|
|
101
|
+
"page.__PAGE__.module.settings.description": "调整此工作区的偏好。"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
*/
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** @type {import('@astryxdesign/cli/authoring').IntegrationTemplateDoc} */
|
|
2
|
+
export default {
|
|
3
|
+
type: 'page',
|
|
4
|
+
name: 'List page',
|
|
5
|
+
displayName: 'List page',
|
|
6
|
+
description:
|
|
7
|
+
'A searchable collection with a primary action, a compact filter, list rows, an empty state, and load-more pagination.',
|
|
8
|
+
category: 'List - Collection',
|
|
9
|
+
keywords: [
|
|
10
|
+
'list page',
|
|
11
|
+
'library',
|
|
12
|
+
'collection',
|
|
13
|
+
'search',
|
|
14
|
+
'recent items',
|
|
15
|
+
'mobile',
|
|
16
|
+
'mobile list',
|
|
17
|
+
],
|
|
18
|
+
replaces: 'library',
|
|
19
|
+
isReady: true,
|
|
20
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { Button } from '@astryxdesign/core/Button'
|
|
2
|
+
import { EmptyState } from '@astryxdesign/core/EmptyState'
|
|
3
|
+
import { Heading } from '@astryxdesign/core/Heading'
|
|
4
|
+
import { Icon } from '@astryxdesign/core/Icon'
|
|
5
|
+
import { List, ListItem } from '@astryxdesign/core/List'
|
|
6
|
+
import { MoreMenu } from '@astryxdesign/core/MoreMenu'
|
|
7
|
+
import { SegmentedControl, SegmentedControlItem } from '@astryxdesign/core/SegmentedControl'
|
|
8
|
+
import { Stack } from '@astryxdesign/core/Stack'
|
|
9
|
+
import { Text } from '@astryxdesign/core/Text'
|
|
10
|
+
import { TextInput } from '@astryxdesign/core/TextInput'
|
|
11
|
+
import { Timestamp } from '@astryxdesign/core/Timestamp'
|
|
12
|
+
import { VisuallyHidden } from '@astryxdesign/core/VisuallyHidden'
|
|
13
|
+
import { useTranslator } from '@astryxdesign/core/i18n'
|
|
14
|
+
import { useState } from 'react'
|
|
15
|
+
import { PageShell } from '@/components/layout'
|
|
16
|
+
import { useCurrentLocale } from '@/i18n'
|
|
17
|
+
|
|
18
|
+
const FILTERS = ['recent', 'all', 'archived'] as const
|
|
19
|
+
type Filter = (typeof FILTERS)[number]
|
|
20
|
+
|
|
21
|
+
const SAMPLE_ITEMS = [
|
|
22
|
+
{
|
|
23
|
+
id: 'brief',
|
|
24
|
+
titleKey: 'page.__PAGE__.item.brief.title',
|
|
25
|
+
metaKey: 'page.__PAGE__.item.brief.meta',
|
|
26
|
+
updatedAt: '2026-11-01T09:00:00.000Z',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'notes',
|
|
30
|
+
titleKey: 'page.__PAGE__.item.notes.title',
|
|
31
|
+
metaKey: 'page.__PAGE__.item.notes.meta',
|
|
32
|
+
updatedAt: '2026-10-28T14:30:00.000Z',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 'archive',
|
|
36
|
+
titleKey: 'page.__PAGE__.item.archive.title',
|
|
37
|
+
metaKey: 'page.__PAGE__.item.archive.meta',
|
|
38
|
+
updatedAt: '2026-10-20T08:15:00.000Z',
|
|
39
|
+
},
|
|
40
|
+
] as const
|
|
41
|
+
// Replace with feature data
|
|
42
|
+
|
|
43
|
+
function isFilter(value: string): value is Filter {
|
|
44
|
+
return FILTERS.some((filter) => filter === value)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default function DockList() {
|
|
48
|
+
const locale = useCurrentLocale()
|
|
49
|
+
const t = useTranslator()
|
|
50
|
+
const [search, setSearch] = useState('')
|
|
51
|
+
const [filter, setFilter] = useState<Filter>('recent')
|
|
52
|
+
const hasItems: boolean = SAMPLE_ITEMS.length > 0
|
|
53
|
+
|
|
54
|
+
const visibleItems = SAMPLE_ITEMS.filter((item) => {
|
|
55
|
+
const query = search.trim().toLocaleLowerCase()
|
|
56
|
+
if (!query) return true
|
|
57
|
+
return `${t(item.titleKey)} ${t(item.metaKey)}`.toLocaleLowerCase().includes(query)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<PageShell
|
|
62
|
+
title={t('page.__PAGE__.title')}
|
|
63
|
+
backHref={`/${locale}`}
|
|
64
|
+
backLabel={t('page.__PAGE__.back')}
|
|
65
|
+
actions={
|
|
66
|
+
<Button
|
|
67
|
+
label={t('page.__PAGE__.new')}
|
|
68
|
+
size="sm"
|
|
69
|
+
variant="primary"
|
|
70
|
+
onClick={() => undefined}
|
|
71
|
+
/>
|
|
72
|
+
}
|
|
73
|
+
>
|
|
74
|
+
<Stack gap={4}>
|
|
75
|
+
<TextInput
|
|
76
|
+
label={t('page.__PAGE__.search.label')}
|
|
77
|
+
isLabelHidden
|
|
78
|
+
value={search}
|
|
79
|
+
onChange={setSearch}
|
|
80
|
+
placeholder={t('page.__PAGE__.search.placeholder')}
|
|
81
|
+
startIcon={<Icon icon="search" size="sm" />}
|
|
82
|
+
hasClear
|
|
83
|
+
/>
|
|
84
|
+
<SegmentedControl
|
|
85
|
+
label={t('page.__PAGE__.filter.label')}
|
|
86
|
+
value={filter}
|
|
87
|
+
onChange={(value) => {
|
|
88
|
+
if (isFilter(value)) setFilter(value)
|
|
89
|
+
}}
|
|
90
|
+
layout="fill"
|
|
91
|
+
>
|
|
92
|
+
{FILTERS.map((candidate) => (
|
|
93
|
+
<SegmentedControlItem
|
|
94
|
+
key={candidate}
|
|
95
|
+
value={candidate}
|
|
96
|
+
label={t(`page.__PAGE__.filter.${candidate}`)}
|
|
97
|
+
/>
|
|
98
|
+
))}
|
|
99
|
+
</SegmentedControl>
|
|
100
|
+
|
|
101
|
+
{!hasItems ? (
|
|
102
|
+
<EmptyState
|
|
103
|
+
title={t('page.__PAGE__.empty.title')}
|
|
104
|
+
description={t('page.__PAGE__.empty.description')}
|
|
105
|
+
actions={
|
|
106
|
+
<Button label={t('page.__PAGE__.new')} variant="primary" onClick={() => undefined} />
|
|
107
|
+
}
|
|
108
|
+
/>
|
|
109
|
+
) : visibleItems.length === 0 ? (
|
|
110
|
+
<Text type="body" color="secondary">
|
|
111
|
+
{t('page.__PAGE__.noResults')}
|
|
112
|
+
</Text>
|
|
113
|
+
) : (
|
|
114
|
+
<List
|
|
115
|
+
density="balanced"
|
|
116
|
+
hasDividers
|
|
117
|
+
header={
|
|
118
|
+
<VisuallyHidden>
|
|
119
|
+
<Heading level={2}>{t('page.__PAGE__.listTitle')}</Heading>
|
|
120
|
+
</VisuallyHidden>
|
|
121
|
+
}
|
|
122
|
+
>
|
|
123
|
+
{visibleItems.map((item) => (
|
|
124
|
+
<ListItem
|
|
125
|
+
key={item.id}
|
|
126
|
+
label={t(item.titleKey)}
|
|
127
|
+
description={
|
|
128
|
+
<Stack direction="horizontal" gap={2} vAlign="center" wrap="wrap">
|
|
129
|
+
<Text type="supporting" color="secondary">
|
|
130
|
+
{t(item.metaKey)}
|
|
131
|
+
</Text>
|
|
132
|
+
<Timestamp
|
|
133
|
+
value={item.updatedAt}
|
|
134
|
+
format="date"
|
|
135
|
+
type="supporting"
|
|
136
|
+
color="secondary"
|
|
137
|
+
/>
|
|
138
|
+
</Stack>
|
|
139
|
+
}
|
|
140
|
+
onClick={() => undefined}
|
|
141
|
+
endContent={
|
|
142
|
+
<MoreMenu
|
|
143
|
+
label={t('page.__PAGE__.row.options')}
|
|
144
|
+
size="sm"
|
|
145
|
+
alignment="end"
|
|
146
|
+
presentation="adaptive"
|
|
147
|
+
items={[
|
|
148
|
+
{
|
|
149
|
+
id: 'archive',
|
|
150
|
+
label: t('page.__PAGE__.row.archive'),
|
|
151
|
+
icon: <Icon icon="close" size="sm" />,
|
|
152
|
+
},
|
|
153
|
+
]}
|
|
154
|
+
/>
|
|
155
|
+
}
|
|
156
|
+
/>
|
|
157
|
+
))}
|
|
158
|
+
</List>
|
|
159
|
+
)}
|
|
160
|
+
|
|
161
|
+
{visibleItems.length > 0 ? (
|
|
162
|
+
<Stack direction="horizontal" hAlign="center">
|
|
163
|
+
<Button label={t('page.__PAGE__.loadMore')} variant="ghost" onClick={() => undefined} />
|
|
164
|
+
</Stack>
|
|
165
|
+
) : null}
|
|
166
|
+
</Stack>
|
|
167
|
+
</PageShell>
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/*
|
|
172
|
+
astryx-messages
|
|
173
|
+
{
|
|
174
|
+
"en": {
|
|
175
|
+
"page.__PAGE__.title": "Documents",
|
|
176
|
+
"page.__PAGE__.back": "Back",
|
|
177
|
+
"page.__PAGE__.new": "New",
|
|
178
|
+
"page.__PAGE__.search.label": "Search documents",
|
|
179
|
+
"page.__PAGE__.search.placeholder": "Search documents",
|
|
180
|
+
"page.__PAGE__.filter.label": "Document view",
|
|
181
|
+
"page.__PAGE__.filter.recent": "Recent",
|
|
182
|
+
"page.__PAGE__.filter.all": "All",
|
|
183
|
+
"page.__PAGE__.filter.archived": "Archived",
|
|
184
|
+
"page.__PAGE__.empty.title": "No documents yet",
|
|
185
|
+
"page.__PAGE__.empty.description": "Create the first document to start this collection.",
|
|
186
|
+
"page.__PAGE__.noResults": "No documents match this search.",
|
|
187
|
+
"page.__PAGE__.listTitle": "Documents",
|
|
188
|
+
"page.__PAGE__.row.options": "Document actions",
|
|
189
|
+
"page.__PAGE__.row.archive": "Archive",
|
|
190
|
+
"page.__PAGE__.item.brief.title": "Project brief",
|
|
191
|
+
"page.__PAGE__.item.brief.meta": "Product",
|
|
192
|
+
"page.__PAGE__.item.notes.title": "Research notes",
|
|
193
|
+
"page.__PAGE__.item.notes.meta": "Research",
|
|
194
|
+
"page.__PAGE__.item.archive.title": "Team guidelines",
|
|
195
|
+
"page.__PAGE__.item.archive.meta": "Operations",
|
|
196
|
+
"page.__PAGE__.loadMore": "Load more"
|
|
197
|
+
},
|
|
198
|
+
"zh-CN": {
|
|
199
|
+
"page.__PAGE__.title": "文档",
|
|
200
|
+
"page.__PAGE__.back": "返回",
|
|
201
|
+
"page.__PAGE__.new": "新建",
|
|
202
|
+
"page.__PAGE__.search.label": "搜索文档",
|
|
203
|
+
"page.__PAGE__.search.placeholder": "搜索文档",
|
|
204
|
+
"page.__PAGE__.filter.label": "文档视图",
|
|
205
|
+
"page.__PAGE__.filter.recent": "最近",
|
|
206
|
+
"page.__PAGE__.filter.all": "全部",
|
|
207
|
+
"page.__PAGE__.filter.archived": "已归档",
|
|
208
|
+
"page.__PAGE__.empty.title": "暂无文档",
|
|
209
|
+
"page.__PAGE__.empty.description": "创建第一份文档来开始这个集合。",
|
|
210
|
+
"page.__PAGE__.noResults": "没有文档符合此搜索。",
|
|
211
|
+
"page.__PAGE__.listTitle": "文档",
|
|
212
|
+
"page.__PAGE__.row.options": "文档操作",
|
|
213
|
+
"page.__PAGE__.row.archive": "归档",
|
|
214
|
+
"page.__PAGE__.item.brief.title": "项目简介",
|
|
215
|
+
"page.__PAGE__.item.brief.meta": "产品",
|
|
216
|
+
"page.__PAGE__.item.notes.title": "研究笔记",
|
|
217
|
+
"page.__PAGE__.item.notes.meta": "研究",
|
|
218
|
+
"page.__PAGE__.item.archive.title": "团队指南",
|
|
219
|
+
"page.__PAGE__.item.archive.meta": "运营",
|
|
220
|
+
"page.__PAGE__.loadMore": "加载更多"
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
*/
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** @type {import('@astryxdesign/cli/authoring').IntegrationTemplateDoc} */
|
|
2
|
+
export default {
|
|
3
|
+
type: 'page',
|
|
4
|
+
name: 'Settings page',
|
|
5
|
+
displayName: 'Settings page',
|
|
6
|
+
description:
|
|
7
|
+
'A mobile-first settings surface with compact tabs, a wide side navigation, and one scrollable section panel.',
|
|
8
|
+
category: 'Settings - Sections',
|
|
9
|
+
keywords: [
|
|
10
|
+
'settings page',
|
|
11
|
+
'preferences',
|
|
12
|
+
'sections',
|
|
13
|
+
'mobile',
|
|
14
|
+
'mobile settings',
|
|
15
|
+
'account settings',
|
|
16
|
+
],
|
|
17
|
+
replaces: 'settings',
|
|
18
|
+
isReady: true,
|
|
19
|
+
}
|