@teambit/workspace 1.0.1079 → 1.0.1081
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/dist/{preview-1785260578779.js → preview-1785278532531.js} +2 -2
- package/dist/ui/workspace/use-workspace.js +179 -27
- package/dist/ui/workspace/use-workspace.js.map +1 -1
- package/dist/ui/workspace/workspace-overview/index.d.ts +1 -1
- package/dist/ui/workspace/workspace-overview/index.js +5 -5
- package/dist/ui/workspace/workspace-overview/index.js.map +1 -1
- package/dist/ui/workspace/workspace-overview/workspace-overview.js +13 -143
- package/dist/ui/workspace/workspace-overview/workspace-overview.js.map +1 -1
- package/dist/ui/workspace/workspace-skeleton.d.ts +8 -0
- package/dist/ui/workspace/workspace-skeleton.js +73 -0
- package/dist/ui/workspace/workspace-skeleton.js.map +1 -0
- package/dist/ui/workspace/workspace-skeleton.module.scss +102 -0
- package/dist/ui/workspace/workspace.js +23 -7
- package/dist/ui/workspace/workspace.js.map +1 -1
- package/package.json +65 -74
- package/ui/workspace/use-workspace.ts +185 -27
- package/ui/workspace/workspace-overview/index.ts +1 -1
- package/ui/workspace/workspace-overview/workspace-overview.tsx +9 -102
- package/ui/workspace/workspace-skeleton.module.scss +102 -0
- package/ui/workspace/workspace-skeleton.tsx +35 -0
- package/ui/workspace/workspace.tsx +16 -5
- package/dist/ui/workspace/workspace-overview/card-overlays.d.ts +0 -5
- package/dist/ui/workspace/workspace-overview/card-overlays.js +0 -133
- package/dist/ui/workspace/workspace-overview/card-overlays.js.map +0 -1
- package/dist/ui/workspace/workspace-overview/card-overlays.module.scss +0 -72
- package/dist/ui/workspace/workspace-overview/filter-utils.d.ts +0 -10
- package/dist/ui/workspace/workspace-overview/filter-utils.js +0 -40
- package/dist/ui/workspace/workspace-overview/filter-utils.js.map +0 -1
- package/dist/ui/workspace/workspace-overview/hope-component-card.d.ts +0 -15
- package/dist/ui/workspace/workspace-overview/hope-component-card.js +0 -186
- package/dist/ui/workspace/workspace-overview/hope-component-card.js.map +0 -1
- package/dist/ui/workspace/workspace-overview/hope-component-card.module.scss +0 -169
- package/dist/ui/workspace/workspace-overview/link-plugin.d.ts +0 -3
- package/dist/ui/workspace/workspace-overview/link-plugin.js +0 -14
- package/dist/ui/workspace/workspace-overview/link-plugin.js.map +0 -1
- package/dist/ui/workspace/workspace-overview/namespace-header.d.ts +0 -9
- package/dist/ui/workspace/workspace-overview/namespace-header.js +0 -107
- package/dist/ui/workspace/workspace-overview/namespace-header.js.map +0 -1
- package/dist/ui/workspace/workspace-overview/namespace-header.module.scss +0 -88
- package/dist/ui/workspace/workspace-overview/namespace-sort.d.ts +0 -8
- package/dist/ui/workspace/workspace-overview/namespace-sort.js +0 -47
- package/dist/ui/workspace/workspace-overview/namespace-sort.js.map +0 -1
- package/dist/ui/workspace/workspace-overview/scope-sort.d.ts +0 -1
- package/dist/ui/workspace/workspace-overview/scope-sort.js +0 -14
- package/dist/ui/workspace/workspace-overview/scope-sort.js.map +0 -1
- package/dist/ui/workspace/workspace-overview/use-query-param-with-default.d.ts +0 -6
- package/dist/ui/workspace/workspace-overview/use-query-param-with-default.js +0 -102
- package/dist/ui/workspace/workspace-overview/use-query-param-with-default.js.map +0 -1
- package/dist/ui/workspace/workspace-overview/use-workspace-aggregation.d.ts +0 -3
- package/dist/ui/workspace/workspace-overview/use-workspace-aggregation.js +0 -88
- package/dist/ui/workspace/workspace-overview/use-workspace-aggregation.js.map +0 -1
- package/dist/ui/workspace/workspace-overview/workspace-filter-panel.d.ts +0 -13
- package/dist/ui/workspace/workspace-overview/workspace-filter-panel.js +0 -110
- package/dist/ui/workspace/workspace-overview/workspace-filter-panel.js.map +0 -1
- package/dist/ui/workspace/workspace-overview/workspace-overview.module.scss +0 -91
- package/dist/ui/workspace/workspace-overview/workspace-overview.sort.d.ts +0 -1
- package/dist/ui/workspace/workspace-overview/workspace-overview.sort.js +0 -30
- package/dist/ui/workspace/workspace-overview/workspace-overview.sort.js.map +0 -1
- package/dist/ui/workspace/workspace-overview/workspace-overview.types.d.ts +0 -28
- package/dist/ui/workspace/workspace-overview/workspace-overview.types.js +0 -3
- package/dist/ui/workspace/workspace-overview/workspace-overview.types.js.map +0 -1
- package/ui/workspace/workspace-overview/card-overlays.module.scss +0 -72
- package/ui/workspace/workspace-overview/card-overlays.tsx +0 -66
- package/ui/workspace/workspace-overview/filter-utils.ts +0 -42
- package/ui/workspace/workspace-overview/hope-component-card.module.scss +0 -169
- package/ui/workspace/workspace-overview/hope-component-card.tsx +0 -152
- package/ui/workspace/workspace-overview/link-plugin.ts +0 -5
- package/ui/workspace/workspace-overview/namespace-header.module.scss +0 -88
- package/ui/workspace/workspace-overview/namespace-header.tsx +0 -72
- package/ui/workspace/workspace-overview/namespace-sort.ts +0 -44
- package/ui/workspace/workspace-overview/scope-sort.ts +0 -9
- package/ui/workspace/workspace-overview/use-query-param-with-default.ts +0 -115
- package/ui/workspace/workspace-overview/use-workspace-aggregation.ts +0 -79
- package/ui/workspace/workspace-overview/workspace-filter-panel.tsx +0 -109
- package/ui/workspace/workspace-overview/workspace-overview.module.scss +0 -91
- package/ui/workspace/workspace-overview/workspace-overview.sort.ts +0 -29
- package/ui/workspace/workspace-overview/workspace-overview.types.ts +0 -30
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
.card {
|
|
2
|
-
position: relative;
|
|
3
|
-
border-radius: 10px;
|
|
4
|
-
overflow: hidden;
|
|
5
|
-
background: var(--surface-color);
|
|
6
|
-
border: 1px solid var(--border-medium-color);
|
|
7
|
-
cursor: pointer;
|
|
8
|
-
transition:
|
|
9
|
-
box-shadow 0.15s ease,
|
|
10
|
-
border-color 0.15s ease;
|
|
11
|
-
|
|
12
|
-
&:hover {
|
|
13
|
-
box-shadow: 0 4px 14px rgba(20, 0, 104, 0.06);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
.cardBuilding {
|
|
18
|
-
composes: card;
|
|
19
|
-
|
|
20
|
-
&:hover {
|
|
21
|
-
box-shadow: none;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.linkWrapper {
|
|
26
|
-
text-decoration: none;
|
|
27
|
-
color: inherit;
|
|
28
|
-
display: block;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/* ---- Preview ---- */
|
|
32
|
-
|
|
33
|
-
.preview {
|
|
34
|
-
height: 180px;
|
|
35
|
-
position: relative;
|
|
36
|
-
overflow: hidden;
|
|
37
|
-
background: var(--surface-color);
|
|
38
|
-
border-bottom: 1px solid var(--border-medium-color);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
.previewQueued {
|
|
42
|
-
composes: preview;
|
|
43
|
-
background: var(--surface01-color);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.previewInner {
|
|
47
|
-
position: absolute;
|
|
48
|
-
inset: 0;
|
|
49
|
-
overflow: hidden;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/* ---- Env badge ---- */
|
|
53
|
-
|
|
54
|
-
.envBadge {
|
|
55
|
-
position: absolute;
|
|
56
|
-
bottom: 8px;
|
|
57
|
-
right: 8px;
|
|
58
|
-
background: var(--surface-color);
|
|
59
|
-
padding: 4px;
|
|
60
|
-
border-radius: 8px;
|
|
61
|
-
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
|
62
|
-
display: flex;
|
|
63
|
-
align-items: center;
|
|
64
|
-
justify-content: center;
|
|
65
|
-
width: 24px;
|
|
66
|
-
height: 24px;
|
|
67
|
-
z-index: 3;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.envIcon {
|
|
71
|
-
height: 14px;
|
|
72
|
-
display: block;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/* ---- Status corner ---- */
|
|
76
|
-
|
|
77
|
-
.statusCorner {
|
|
78
|
-
position: absolute;
|
|
79
|
-
top: 10px;
|
|
80
|
-
right: 10px;
|
|
81
|
-
z-index: 2;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/* ---- Footer ---- */
|
|
85
|
-
|
|
86
|
-
.footer {
|
|
87
|
-
padding: 8px 12px;
|
|
88
|
-
display: flex;
|
|
89
|
-
align-items: center;
|
|
90
|
-
gap: 8px;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
.name {
|
|
94
|
-
flex: 1;
|
|
95
|
-
min-width: 0;
|
|
96
|
-
font-size: 12.5px;
|
|
97
|
-
font-weight: 500;
|
|
98
|
-
font-family: 'JetBrains Mono', ui-monospace, Menlo, monospace;
|
|
99
|
-
color: var(--on-background-color);
|
|
100
|
-
overflow: hidden;
|
|
101
|
-
text-overflow: ellipsis;
|
|
102
|
-
white-space: nowrap;
|
|
103
|
-
letter-spacing: -0.01em;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.nameQueued {
|
|
107
|
-
composes: name;
|
|
108
|
-
color: var(--on-background-medium-color);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
.hash {
|
|
112
|
-
font-size: 10.5px;
|
|
113
|
-
padding: 1px 6px;
|
|
114
|
-
border-radius: 4px;
|
|
115
|
-
background: var(--surface01-color);
|
|
116
|
-
font-family: 'JetBrains Mono', ui-monospace, Menlo, monospace;
|
|
117
|
-
color: var(--on-background-low-color);
|
|
118
|
-
flex-shrink: 0;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
.scopeBadge {
|
|
122
|
-
width: 20px;
|
|
123
|
-
height: 20px;
|
|
124
|
-
border-radius: 5px;
|
|
125
|
-
display: flex;
|
|
126
|
-
align-items: center;
|
|
127
|
-
justify-content: center;
|
|
128
|
-
flex-shrink: 0;
|
|
129
|
-
overflow: hidden;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.scopeBadgeIcon {
|
|
133
|
-
width: 12px;
|
|
134
|
-
height: 12px;
|
|
135
|
-
object-fit: contain;
|
|
136
|
-
filter: brightness(0) invert(1);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
.scopeBadgeInitial {
|
|
140
|
-
font-family: 'JetBrains Mono', ui-monospace, Menlo, monospace;
|
|
141
|
-
font-size: 10px;
|
|
142
|
-
font-weight: 700;
|
|
143
|
-
color: white;
|
|
144
|
-
line-height: 1;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
.buildingLabel {
|
|
148
|
-
font-size: 10px;
|
|
149
|
-
font-weight: 600;
|
|
150
|
-
letter-spacing: 0.06em;
|
|
151
|
-
text-transform: uppercase;
|
|
152
|
-
flex-shrink: 0;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/* ---- Load preview button ---- */
|
|
156
|
-
|
|
157
|
-
.loadPreview {
|
|
158
|
-
position: absolute;
|
|
159
|
-
right: 4px !important;
|
|
160
|
-
left: unset !important;
|
|
161
|
-
z-index: 4;
|
|
162
|
-
background-color: var(--surface01-color) !important;
|
|
163
|
-
color: var(--on-surface-color) !important;
|
|
164
|
-
border: 1px solid var(--border-medium-color);
|
|
165
|
-
|
|
166
|
-
&:hover {
|
|
167
|
-
background-color: var(--surface-hover-color) !important;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { Link } from '@teambit/base-react.navigation.link';
|
|
3
|
-
import { PreviewPlaceholder } from '@teambit/preview.ui.preview-placeholder';
|
|
4
|
-
import { Tooltip } from '@teambit/design.ui.tooltip';
|
|
5
|
-
import { LoadPreview } from '@teambit/workspace.ui.load-preview';
|
|
6
|
-
import { ComponentID } from '@teambit/component-id';
|
|
7
|
-
import type { ComponentModel } from '@teambit/component';
|
|
8
|
-
import type { ComponentDescriptor } from '@teambit/component-descriptor';
|
|
9
|
-
import type { ScopeID } from '@teambit/scopes.scope-id';
|
|
10
|
-
import { getComponentStatus } from './filter-utils';
|
|
11
|
-
import { ChangedPill, BuildSpinner, BuildingPreview } from './card-overlays';
|
|
12
|
-
import styles from './hope-component-card.module.scss';
|
|
13
|
-
|
|
14
|
-
export type HopeComponentCardProps = {
|
|
15
|
-
component: ComponentModel;
|
|
16
|
-
componentDescriptor: ComponentDescriptor;
|
|
17
|
-
scope?: { id: ScopeID; icon?: string; backgroundIconColor?: string };
|
|
18
|
-
showPreview?: boolean;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export function HopeComponentCard({
|
|
22
|
-
component,
|
|
23
|
-
componentDescriptor,
|
|
24
|
-
scope,
|
|
25
|
-
showPreview: showPreviewProp,
|
|
26
|
-
}: HopeComponentCardProps) {
|
|
27
|
-
const [shouldShowPreview, setShouldShowPreview] = useState(Boolean(showPreviewProp));
|
|
28
|
-
const prevServerUrlRef = useRef(component.server?.url);
|
|
29
|
-
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
if (prevServerUrlRef.current !== component.server?.url && shouldShowPreview) {
|
|
32
|
-
setShouldShowPreview(false);
|
|
33
|
-
setTimeout(() => setShouldShowPreview(true), 50);
|
|
34
|
-
}
|
|
35
|
-
prevServerUrlRef.current = component.server?.url;
|
|
36
|
-
}, [component.server?.url]);
|
|
37
|
-
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
setShouldShowPreview(Boolean(showPreviewProp));
|
|
40
|
-
}, [showPreviewProp]);
|
|
41
|
-
|
|
42
|
-
const item = { component } as any;
|
|
43
|
-
const status = getComponentStatus(item);
|
|
44
|
-
const accent = 'var(--bit-accent-color, #6c5ce7)';
|
|
45
|
-
|
|
46
|
-
const isBuilding = status === 'building';
|
|
47
|
-
const isQueued = status === 'queued';
|
|
48
|
-
const isChanged = status === 'changed';
|
|
49
|
-
|
|
50
|
-
const href = `${component.id.fullName}?scope=${component.id.scope}`;
|
|
51
|
-
|
|
52
|
-
const loadPreviewVisible = component.compositions.length > 0 && !isBuilding && !shouldShowPreview;
|
|
53
|
-
|
|
54
|
-
const showPreviewClick = (e: React.MouseEvent) => {
|
|
55
|
-
e.stopPropagation();
|
|
56
|
-
e.preventDefault();
|
|
57
|
-
setShouldShowPreview(true);
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const envAspect = componentDescriptor.get<any>('teambit.envs/envs');
|
|
61
|
-
const env = envAspect?.data || envAspect;
|
|
62
|
-
const envComponentId = env?.id ? ComponentID.fromString(env.id) : undefined;
|
|
63
|
-
|
|
64
|
-
const cardClass = isBuilding ? styles.cardBuilding : styles.card;
|
|
65
|
-
const buildingBorderStyle = isBuilding
|
|
66
|
-
? {
|
|
67
|
-
borderColor: accent,
|
|
68
|
-
boxShadow: `0 0 0 3px color-mix(in srgb, var(--bit-accent-color, #6c5ce7) 10%, transparent)`,
|
|
69
|
-
}
|
|
70
|
-
: undefined;
|
|
71
|
-
|
|
72
|
-
const nameLabel = component.id.namespace ? `${component.id.namespace}/${component.id.name}` : component.id.name;
|
|
73
|
-
|
|
74
|
-
const shortHash = component.id.version?.slice(0, 7);
|
|
75
|
-
|
|
76
|
-
const scopeInitial = component.id.scope?.split('.').pop()?.charAt(0).toUpperCase();
|
|
77
|
-
|
|
78
|
-
return (
|
|
79
|
-
<div className={cardClass} style={buildingBorderStyle}>
|
|
80
|
-
{loadPreviewVisible && <LoadPreview className={styles.loadPreview} onClick={showPreviewClick} />}
|
|
81
|
-
|
|
82
|
-
<Link href={href} className={styles.linkWrapper}>
|
|
83
|
-
<div className={styles.preview}>
|
|
84
|
-
<div className={styles.previewInner}>
|
|
85
|
-
<CardPreview
|
|
86
|
-
component={component}
|
|
87
|
-
componentDescriptor={componentDescriptor}
|
|
88
|
-
status={status}
|
|
89
|
-
shouldShowPreview={shouldShowPreview}
|
|
90
|
-
/>
|
|
91
|
-
</div>
|
|
92
|
-
|
|
93
|
-
{!isQueued && env?.icon && (
|
|
94
|
-
<div className={styles.envBadge}>
|
|
95
|
-
<Tooltip delay={300} content={envComponentId?.name}>
|
|
96
|
-
<img src={env.icon} className={styles.envIcon} alt="" />
|
|
97
|
-
</Tooltip>
|
|
98
|
-
</div>
|
|
99
|
-
)}
|
|
100
|
-
|
|
101
|
-
{(isChanged || isBuilding) && (
|
|
102
|
-
<div className={styles.statusCorner}>
|
|
103
|
-
{isChanged && <ChangedPill />}
|
|
104
|
-
{isBuilding && <BuildSpinner />}
|
|
105
|
-
</div>
|
|
106
|
-
)}
|
|
107
|
-
</div>
|
|
108
|
-
|
|
109
|
-
<div className={styles.footer}>
|
|
110
|
-
<Tooltip delay={300} content={component.id.scope}>
|
|
111
|
-
<div className={styles.scopeBadge} style={{ background: scope?.backgroundIconColor || accent }}>
|
|
112
|
-
{scope?.icon ? (
|
|
113
|
-
<img src={scope.icon} className={styles.scopeBadgeIcon} alt="" />
|
|
114
|
-
) : (
|
|
115
|
-
<span className={styles.scopeBadgeInitial}>{scopeInitial}</span>
|
|
116
|
-
)}
|
|
117
|
-
</div>
|
|
118
|
-
</Tooltip>
|
|
119
|
-
<span className={styles.name}>{nameLabel}</span>
|
|
120
|
-
{!isBuilding && !isQueued && shortHash && <span className={styles.hash}>{shortHash}</span>}
|
|
121
|
-
{isBuilding && (
|
|
122
|
-
<span className={styles.buildingLabel} style={{ color: accent }}>
|
|
123
|
-
BUILDING
|
|
124
|
-
</span>
|
|
125
|
-
)}
|
|
126
|
-
</div>
|
|
127
|
-
</Link>
|
|
128
|
-
</div>
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function CardPreview({
|
|
133
|
-
component,
|
|
134
|
-
componentDescriptor,
|
|
135
|
-
status,
|
|
136
|
-
shouldShowPreview,
|
|
137
|
-
}: {
|
|
138
|
-
component: ComponentModel;
|
|
139
|
-
componentDescriptor: ComponentDescriptor;
|
|
140
|
-
status: string;
|
|
141
|
-
shouldShowPreview: boolean;
|
|
142
|
-
}) {
|
|
143
|
-
if (status === 'building') return <BuildingPreview />;
|
|
144
|
-
|
|
145
|
-
return (
|
|
146
|
-
<PreviewPlaceholder
|
|
147
|
-
component={component}
|
|
148
|
-
componentDescriptor={componentDescriptor}
|
|
149
|
-
shouldShowPreview={shouldShowPreview}
|
|
150
|
-
/>
|
|
151
|
-
);
|
|
152
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
.header {
|
|
2
|
-
display: flex;
|
|
3
|
-
align-items: center;
|
|
4
|
-
gap: 10px;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
.dot {
|
|
8
|
-
width: 10px;
|
|
9
|
-
height: 10px;
|
|
10
|
-
border-radius: 3px;
|
|
11
|
-
flex-shrink: 0;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
.name {
|
|
15
|
-
font-size: 20px;
|
|
16
|
-
font-weight: 600;
|
|
17
|
-
letter-spacing: 0.02em;
|
|
18
|
-
color: var(--on-background-color);
|
|
19
|
-
flex-shrink: 0;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
.count {
|
|
23
|
-
font-size: 12px;
|
|
24
|
-
color: var(--on-background-low-color);
|
|
25
|
-
flex-shrink: 0;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.buildingPill {
|
|
29
|
-
display: inline-flex;
|
|
30
|
-
align-items: center;
|
|
31
|
-
gap: 5px;
|
|
32
|
-
font-size: 10.5px;
|
|
33
|
-
font-weight: 500;
|
|
34
|
-
padding: 1px 8px;
|
|
35
|
-
border-radius: 999px;
|
|
36
|
-
margin-left: 4px;
|
|
37
|
-
flex-shrink: 0;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.buildingDot {
|
|
41
|
-
width: 5px;
|
|
42
|
-
height: 5px;
|
|
43
|
-
border-radius: 50%;
|
|
44
|
-
animation: pulse 1.4s ease-in-out infinite;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.scopeIconBadge {
|
|
48
|
-
width: 24px;
|
|
49
|
-
height: 24px;
|
|
50
|
-
border-radius: 6px;
|
|
51
|
-
display: flex;
|
|
52
|
-
align-items: center;
|
|
53
|
-
justify-content: center;
|
|
54
|
-
flex-shrink: 0;
|
|
55
|
-
overflow: hidden;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.scopeIconImg {
|
|
59
|
-
width: 14px;
|
|
60
|
-
height: 14px;
|
|
61
|
-
object-fit: contain;
|
|
62
|
-
filter: brightness(0) invert(1);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.scopeInitial {
|
|
66
|
-
font-family: 'JetBrains Mono', ui-monospace, Menlo, monospace;
|
|
67
|
-
font-size: 11px;
|
|
68
|
-
font-weight: 700;
|
|
69
|
-
color: white;
|
|
70
|
-
line-height: 1;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
.divider {
|
|
74
|
-
flex: 1;
|
|
75
|
-
height: 1px;
|
|
76
|
-
background: var(--border-medium-color);
|
|
77
|
-
margin-left: 4px;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
@keyframes pulse {
|
|
81
|
-
0%,
|
|
82
|
-
100% {
|
|
83
|
-
opacity: 1;
|
|
84
|
-
}
|
|
85
|
-
50% {
|
|
86
|
-
opacity: 0.55;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { getComponentStatus } from './filter-utils';
|
|
3
|
-
import type { WorkspaceItem } from './workspace-overview.types';
|
|
4
|
-
import styles from './namespace-header.module.scss';
|
|
5
|
-
|
|
6
|
-
export interface NamespaceHeaderProps {
|
|
7
|
-
namespace: string;
|
|
8
|
-
items: WorkspaceItem[];
|
|
9
|
-
scopeIcon?: string;
|
|
10
|
-
scopeIconColor?: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function NamespaceHeader({ namespace, items, scopeIcon, scopeIconColor }: NamespaceHeaderProps) {
|
|
14
|
-
const accent = 'var(--bit-accent-color, #6c5ce7)';
|
|
15
|
-
const tint = 'color-mix(in srgb, var(--bit-accent-color, #6c5ce7) 12%, transparent)';
|
|
16
|
-
|
|
17
|
-
let buildingCount = 0;
|
|
18
|
-
let readyCount = 0;
|
|
19
|
-
for (const item of items) {
|
|
20
|
-
const s = getComponentStatus(item);
|
|
21
|
-
if (s === 'building') buildingCount++;
|
|
22
|
-
if (s === 'built' || s === 'changed') readyCount++;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<header className={styles.header}>
|
|
27
|
-
<HeaderIcon scopeIcon={scopeIcon} scopeIconColor={scopeIconColor} namespace={namespace} accent={accent} />
|
|
28
|
-
<span className={styles.name}>{namespace}</span>
|
|
29
|
-
<span className={styles.count}>
|
|
30
|
-
{readyCount}/{items.length}
|
|
31
|
-
</span>
|
|
32
|
-
{buildingCount > 0 && (
|
|
33
|
-
<span className={styles.buildingPill} style={{ color: accent, background: tint }}>
|
|
34
|
-
<span className={styles.buildingDot} style={{ background: accent }} />
|
|
35
|
-
building
|
|
36
|
-
</span>
|
|
37
|
-
)}
|
|
38
|
-
<div className={styles.divider} />
|
|
39
|
-
</header>
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function HeaderIcon({
|
|
44
|
-
scopeIcon,
|
|
45
|
-
scopeIconColor,
|
|
46
|
-
namespace,
|
|
47
|
-
accent,
|
|
48
|
-
}: {
|
|
49
|
-
scopeIcon?: string;
|
|
50
|
-
scopeIconColor?: string;
|
|
51
|
-
namespace: string;
|
|
52
|
-
accent: string;
|
|
53
|
-
}) {
|
|
54
|
-
if (scopeIcon) {
|
|
55
|
-
return (
|
|
56
|
-
<div className={styles.scopeIconBadge} style={{ background: scopeIconColor || accent }}>
|
|
57
|
-
<img src={scopeIcon} className={styles.scopeIconImg} alt="" />
|
|
58
|
-
</div>
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (scopeIconColor) {
|
|
63
|
-
const initial = namespace.split(/[./]/).pop()?.charAt(0).toUpperCase() || '?';
|
|
64
|
-
return (
|
|
65
|
-
<div className={styles.scopeIconBadge} style={{ background: scopeIconColor }}>
|
|
66
|
-
<span className={styles.scopeInitial}>{initial}</span>
|
|
67
|
-
</div>
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return <span className={styles.dot} style={{ background: accent }} />;
|
|
72
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { WorkspaceItem } from './workspace-overview.types';
|
|
2
|
-
|
|
3
|
-
export const PRIORITY_HIGH = ['ui', 'pages'] as string[];
|
|
4
|
-
export const PRIORITY_MED = ['design'] as string[];
|
|
5
|
-
export const PRIORITY_LOW = ['entities', 'provider', 'hooks', 'icons'] as string[];
|
|
6
|
-
|
|
7
|
-
export function getRootNamespace(ns: string): string {
|
|
8
|
-
if (!ns) return '';
|
|
9
|
-
return ns.split('/')[0]!;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function namespacePriority(ns: string): number {
|
|
13
|
-
const root = getRootNamespace(ns);
|
|
14
|
-
|
|
15
|
-
// internal namespaces (starting with _) go last
|
|
16
|
-
if (root.startsWith('_')) return 4;
|
|
17
|
-
|
|
18
|
-
if (PRIORITY_HIGH.includes(root)) return 0;
|
|
19
|
-
if (PRIORITY_MED.includes(root)) return 1;
|
|
20
|
-
if (PRIORITY_LOW.includes(root)) return 3;
|
|
21
|
-
return 2;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function sortNamespacesAdvanced(list: string[]): string[] {
|
|
25
|
-
return [...list].sort((a, b) => {
|
|
26
|
-
const pa = namespacePriority(a);
|
|
27
|
-
const pb = namespacePriority(b);
|
|
28
|
-
if (pa !== pb) return pa - pb;
|
|
29
|
-
return a.localeCompare(b);
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function sortItemsByNamespace(items: WorkspaceItem[]): WorkspaceItem[] {
|
|
34
|
-
return [...items].sort((a, b) => {
|
|
35
|
-
const na = a.component.id.namespace || '/';
|
|
36
|
-
const nb = b.component.id.namespace || '/';
|
|
37
|
-
|
|
38
|
-
const pa = namespacePriority(na);
|
|
39
|
-
const pb = namespacePriority(nb);
|
|
40
|
-
if (pa !== pb) return pa - pb;
|
|
41
|
-
|
|
42
|
-
return a.component.id.name.localeCompare(b.component.id.name);
|
|
43
|
-
});
|
|
44
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export function sortScopes(scopes: string[]) {
|
|
2
|
-
const designPrefix = 'design';
|
|
3
|
-
|
|
4
|
-
const nonDesign = scopes.filter((s) => !s.includes(designPrefix)).sort((a, b) => a.localeCompare(b));
|
|
5
|
-
|
|
6
|
-
const design = scopes.filter((s) => s.includes(designPrefix)).sort((a, b) => a.localeCompare(b));
|
|
7
|
-
|
|
8
|
-
return [...nonDesign, ...design];
|
|
9
|
-
}
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { useCallback, useMemo } from 'react';
|
|
2
|
-
import { useSearchParams } from 'react-router-dom';
|
|
3
|
-
|
|
4
|
-
const STORAGE_KEY_PREFIX = 'workspace-overview:';
|
|
5
|
-
|
|
6
|
-
export interface QueryParamOptions {
|
|
7
|
-
/** persist value to localStorage across sessions. Default: true */
|
|
8
|
-
persist?: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function useQueryParamWithDefault<T extends string>(
|
|
12
|
-
paramName: string,
|
|
13
|
-
fallback: T,
|
|
14
|
-
options: QueryParamOptions = {}
|
|
15
|
-
): [T, (value: T | null) => void] {
|
|
16
|
-
const { persist = true } = options;
|
|
17
|
-
const [searchParams, setSearchParams] = useSearchParams();
|
|
18
|
-
const storageKey = STORAGE_KEY_PREFIX + paramName;
|
|
19
|
-
|
|
20
|
-
const value = useMemo((): T => {
|
|
21
|
-
const urlValue = searchParams.get(paramName);
|
|
22
|
-
if (urlValue) return urlValue as T;
|
|
23
|
-
|
|
24
|
-
if (persist) {
|
|
25
|
-
const stored = safeGetItem(storageKey);
|
|
26
|
-
if (stored) return stored as T;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return fallback;
|
|
30
|
-
}, [searchParams, paramName, storageKey, fallback, persist]);
|
|
31
|
-
|
|
32
|
-
const setValue = useCallback(
|
|
33
|
-
(newValue: T | null) => {
|
|
34
|
-
const newParams = new URLSearchParams(searchParams);
|
|
35
|
-
|
|
36
|
-
if (newValue === null) {
|
|
37
|
-
newParams.delete(paramName);
|
|
38
|
-
if (persist) safeRemoveItem(storageKey);
|
|
39
|
-
} else {
|
|
40
|
-
newParams.set(paramName, newValue);
|
|
41
|
-
if (persist) safeSetItem(storageKey, newValue);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
setSearchParams(newParams);
|
|
45
|
-
},
|
|
46
|
-
[searchParams, setSearchParams, paramName, storageKey, persist]
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
return [value, setValue];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function useListParamWithDefault(
|
|
53
|
-
paramName: string,
|
|
54
|
-
options: QueryParamOptions = {}
|
|
55
|
-
): [string[], (values: string[]) => void] {
|
|
56
|
-
const { persist = false } = options;
|
|
57
|
-
const [searchParams, setSearchParams] = useSearchParams();
|
|
58
|
-
const storageKey = STORAGE_KEY_PREFIX + paramName;
|
|
59
|
-
|
|
60
|
-
const values = useMemo((): string[] => {
|
|
61
|
-
const urlValue = searchParams.get(paramName);
|
|
62
|
-
if (urlValue) return urlValue.split(',').filter(Boolean);
|
|
63
|
-
|
|
64
|
-
if (persist) {
|
|
65
|
-
const stored = safeGetItem(storageKey);
|
|
66
|
-
if (stored) return stored.split(',').filter(Boolean);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return [];
|
|
70
|
-
}, [searchParams, paramName, storageKey, persist]);
|
|
71
|
-
|
|
72
|
-
const setValues = useCallback(
|
|
73
|
-
(newValues: string[]) => {
|
|
74
|
-
const newParams = new URLSearchParams(searchParams);
|
|
75
|
-
const joined = newValues.filter(Boolean).join(',');
|
|
76
|
-
|
|
77
|
-
if (joined) {
|
|
78
|
-
newParams.set(paramName, joined);
|
|
79
|
-
if (persist) safeSetItem(storageKey, joined);
|
|
80
|
-
} else {
|
|
81
|
-
newParams.delete(paramName);
|
|
82
|
-
if (persist) safeRemoveItem(storageKey);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
setSearchParams(newParams);
|
|
86
|
-
},
|
|
87
|
-
[searchParams, setSearchParams, paramName, storageKey, persist]
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
return [values, setValues];
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function safeGetItem(key: string): string | null {
|
|
94
|
-
try {
|
|
95
|
-
return localStorage.getItem(key);
|
|
96
|
-
} catch {
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function safeSetItem(key: string, value: string): void {
|
|
102
|
-
try {
|
|
103
|
-
localStorage.setItem(key, value);
|
|
104
|
-
} catch {
|
|
105
|
-
// ignore storage errors
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function safeRemoveItem(key: string): void {
|
|
110
|
-
try {
|
|
111
|
-
localStorage.removeItem(key);
|
|
112
|
-
} catch {
|
|
113
|
-
// ignore storage errors
|
|
114
|
-
}
|
|
115
|
-
}
|