@terrafirmagreg-team/ui 0.1.0 → 1.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/package.json +14 -14
- package/src/components/blocks/button/icon-button.tsx +1 -0
- package/src/components/blocks/canvas/chevron-edge.tsx +126 -18
- package/src/components/blocks/canvas/flow-canvas.tsx +4 -0
- package/src/components/blocks/canvas/flow-toolbar.tsx +1 -0
- package/src/components/blocks/canvas/flow-viewport.tsx +1 -0
- package/src/components/blocks/fit-box.tsx +26 -0
- package/src/components/blocks/git/github-info.tsx +1 -0
- package/src/components/blocks/lang/content.tsx +40 -10
- package/src/components/blocks/lang/flag.tsx +1 -0
- package/src/components/blocks/lang/link.tsx +1 -0
- package/src/components/blocks/lang/redirect.tsx +2 -1
- package/src/components/blocks/lang/switcher.tsx +1 -0
- package/src/components/blocks/lang/untranslated-callout.tsx +4 -3
- package/src/components/blocks/lang/untranslated-chip.tsx +53 -0
- package/src/components/blocks/link/link-preview.tsx +2 -1
- package/src/components/blocks/list/row.tsx +1 -0
- package/src/components/blocks/media/glb/model.tsx +1 -0
- package/src/components/blocks/media/glb/scene.tsx +13 -12
- package/src/components/blocks/media/glb/viewer.tsx +1 -0
- package/src/components/blocks/media/image.tsx +3 -2
- package/src/components/blocks/media/slideshow.tsx +4 -3
- package/src/components/blocks/nav/breadcrumb.tsx +1 -0
- package/src/components/blocks/nav/copy-menu.tsx +37 -0
- package/src/components/blocks/nav/link.tsx +1 -0
- package/src/components/blocks/nav/site-menu.tsx +1 -0
- package/src/components/blocks/nav/site-title.tsx +1 -0
- package/src/components/blocks/nav/switcher.tsx +1 -0
- package/src/components/blocks/page/empty-state.tsx +1 -0
- package/src/components/blocks/page/heading.tsx +3 -2
- package/src/components/blocks/page/pager.tsx +110 -2
- package/src/components/blocks/readability/fields.tsx +250 -0
- package/src/components/blocks/readability/menu.tsx +74 -258
- package/src/components/blocks/readability/track.tsx +2 -1
- package/src/components/blocks/search/command.tsx +1 -0
- package/src/components/blocks/search/dialog.tsx +21 -9
- package/src/components/blocks/search/provider.tsx +2 -1
- package/src/components/blocks/sidebar.tsx +5 -5
- package/src/components/blocks/sprite.tsx +231 -0
- package/src/components/blocks/text/format-text.tsx +110 -0
- package/src/components/blocks/theme/toggle.tsx +4 -0
- package/src/components/blocks/tooltip/hint.tsx +1 -0
- package/src/components/blocks/tooltip/lazy.tsx +41 -0
- package/src/components/blocks/viewer/controls.tsx +11 -6
- package/src/components/blocks/viewer/float-bar.tsx +1 -0
- package/src/components/layouts/layout.tsx +2 -1
- package/src/components/layouts/left-sidebar/content.tsx +1 -0
- package/src/components/layouts/left-sidebar/context.tsx +1 -0
- package/src/components/layouts/left-sidebar/expand.tsx +2 -1
- package/src/components/layouts/left-sidebar/footer.tsx +10 -4
- package/src/components/layouts/left-sidebar/index.ts +1 -0
- package/src/components/layouts/left-sidebar/items.tsx +2 -1
- package/src/components/layouts/left-sidebar/search.tsx +4 -3
- package/src/components/layouts/left-sidebar/sidebar.tsx +3 -2
- package/src/components/layouts/left-sidebar/tree.tsx +1 -0
- package/src/components/layouts/right-sidebar.tsx +1 -0
- package/src/components/layouts/tree-context.tsx +1 -0
- package/src/components/mdx/accordion.tsx +3 -2
- package/src/components/mdx/banner.tsx +15 -12
- package/src/components/mdx/callout.tsx +1 -0
- package/src/components/mdx/card.tsx +12 -5
- package/src/components/mdx/codeblock.tsx +5 -4
- package/src/components/mdx/collapsible.tsx +1 -0
- package/src/components/mdx/files.tsx +1 -0
- package/src/components/mdx/glow-header.tsx +17 -3
- package/src/components/mdx/gradient-text.tsx +1 -0
- package/src/components/mdx/icon.tsx +35 -0
- package/src/components/mdx/image-zoom.tsx +5 -4
- package/src/components/mdx/math.tsx +30 -0
- package/src/components/mdx/mermaid.tsx +8 -1
- package/src/components/mdx/multiblock.tsx +1 -0
- package/src/components/mdx/preview.tsx +2 -1
- package/src/components/mdx/steps.tsx +1 -0
- package/src/components/mdx/table.tsx +1 -0
- package/src/components/mdx/tabs.tsx +2 -1
- package/src/components/mdx/type-table.tsx +7 -6
- package/src/{provider.tsx → components/provider.tsx} +1 -3
- package/src/hooks/canvas/use-flow-hover.ts +1 -0
- package/src/hooks/canvas/use-flow-viewport-persist.ts +1 -0
- package/src/hooks/page/use-fit-pages.ts +1 -0
- package/src/hooks/page/use-location-search.ts +8 -2
- package/src/hooks/page/use-page-preview.ts +2 -1
- package/src/hooks/page/use-paged-list.ts +1 -0
- package/src/hooks/readability/{provider.ts → use-readability.ts} +1 -0
- package/src/hooks/search/{dialog.ts → use-dialog.ts} +1 -0
- package/src/hooks/search/use-highlight-query.ts +8 -1
- package/src/hooks/search/{provider.ts → use-provider.ts} +1 -0
- package/src/hooks/use-async-value.ts +6 -3
- package/src/hooks/use-close-on-scroll.ts +1 -0
- package/src/hooks/use-copy-button.ts +1 -0
- package/src/hooks/use-cycle-index.ts +1 -0
- package/src/hooks/use-document-title.ts +1 -0
- package/src/hooks/use-element-size.ts +1 -0
- package/src/hooks/use-external-meta.ts +2 -1
- package/src/hooks/use-hot-key.ts +1 -0
- package/src/hooks/use-hover-follow.ts +1 -0
- package/src/hooks/use-in-view.ts +1 -0
- package/src/hooks/{lang.ts → use-lang.ts} +1 -0
- package/src/hooks/use-last-value.ts +1 -0
- package/src/hooks/{layout.ts → use-layout.ts} +1 -0
- package/src/hooks/use-marquee.ts +1 -0
- package/src/hooks/use-media-query.ts +1 -0
- package/src/hooks/use-mobile.ts +1 -0
- package/src/hooks/use-modifier-keys.ts +1 -0
- package/src/hooks/use-on-change.ts +1 -0
- package/src/hooks/use-pane-keys.ts +1 -0
- package/src/hooks/{sidebar.ts → use-sidebar.ts} +1 -0
- package/src/hooks/use-swipe.ts +1 -0
- package/src/hooks/use-tree.ts +1 -3
- package/src/hooks/viewer/use-in-float-bar.ts +1 -0
- package/src/hooks/viewer/use-viewer-prefs.ts +1 -0
- package/src/lib/canvas/edge.ts +78 -49
- package/src/lib/canvas/geometry.ts +1 -0
- package/src/lib/canvas/state.ts +1 -0
- package/src/lib/canvas/viewport.ts +1 -0
- package/src/lib/canvas/xyflow.ts +1 -0
- package/src/lib/config.ts +25 -0
- package/src/lib/mdx/callout.ts +1 -0
- package/src/lib/mdx/gradient.ts +1 -0
- package/src/lib/mdx/mermaid.ts +1 -0
- package/src/lib/media/crop-opaque.ts +1 -0
- package/src/lib/media/nearby-indexes.ts +1 -0
- package/src/lib/media/trim-transparent.ts +1 -0
- package/src/lib/readability/config.ts +1 -0
- package/src/lib/readability/spotlight.ts +1 -0
- package/src/lib/readability/store.ts +1 -0
- package/src/lib/tree.ts +1 -3
- package/src/lib/utils/cn.ts +1 -0
- package/src/lib/utils/format.ts +3 -3
- package/src/lib/utils/highlight-style.ts +10 -0
- package/src/lib/utils/history.ts +1 -0
- package/src/lib/utils/merge-refs.ts +1 -0
- package/src/lib/utils/nav-active.ts +1 -0
- package/src/lib/utils/stored-value.ts +1 -0
- package/src/lib/utils/urls.ts +2 -1
- package/src/locales/en_us.json +108 -117
- package/src/locales/pl_pl.json +33 -35
- package/src/locales/ru_ru.json +108 -117
- package/src/locales/uk_ua.json +33 -35
- package/src/locales/zh_cn.json +15 -17
- package/src/locales/zh_tw.json +15 -17
- package/src/locales.ts +1 -0
- package/src/types/canvas.ts +6 -1
- package/src/types/glb.ts +1 -0
- package/src/types/mdx-directives.ts +106 -0
- package/src/types/nav.ts +1 -0
- package/src/types/preview.ts +7 -1
- package/src/types/readability.ts +1 -0
- package/src/types/search.ts +1 -0
- package/src/types/tree.ts +1 -0
- package/src/types/viewer.ts +1 -0
- package/styles/content/base.css +198 -194
- package/styles/content/shiki.css +107 -108
- package/styles/content/typography.mjs +3637 -3941
- package/styles/content.css +5 -7
- package/styles.css +430 -413
- package/src/lib/utils/async-cache.ts +0 -32
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { CSSProperties, ReactNode } from 'react';
|
|
4
|
+
import { Skeleton } from '@terrafirmagreg-team/ui/components/ui/skeleton';
|
|
5
|
+
import { formatCount } from '@terrafirmagreg-team/ui/lib/utils/format';
|
|
6
|
+
import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
7
|
+
|
|
8
|
+
export const SPRITE_CHIP_CLASS =
|
|
9
|
+
'inline-flex shrink-0 items-center justify-center rounded-sm border border-border bg-secondary p-0.5 hover:bg-accent';
|
|
10
|
+
|
|
11
|
+
export function SpriteFrame({
|
|
12
|
+
size,
|
|
13
|
+
chip = false,
|
|
14
|
+
badge,
|
|
15
|
+
badgeClassName,
|
|
16
|
+
className,
|
|
17
|
+
style,
|
|
18
|
+
children,
|
|
19
|
+
}: {
|
|
20
|
+
size?: number;
|
|
21
|
+
chip?: boolean;
|
|
22
|
+
badge?: ReactNode;
|
|
23
|
+
badgeClassName?: string;
|
|
24
|
+
className?: string;
|
|
25
|
+
style?: CSSProperties;
|
|
26
|
+
children?: ReactNode;
|
|
27
|
+
}) {
|
|
28
|
+
return (
|
|
29
|
+
<span
|
|
30
|
+
className={cn(
|
|
31
|
+
'inline-flex shrink-0 items-center justify-center',
|
|
32
|
+
chip && SPRITE_CHIP_CLASS,
|
|
33
|
+
badge !== undefined && 'relative',
|
|
34
|
+
className,
|
|
35
|
+
)}
|
|
36
|
+
style={{ ...(size ? { width: size, height: size } : undefined), ...style }}
|
|
37
|
+
>
|
|
38
|
+
{children}
|
|
39
|
+
{badge !== undefined ? (
|
|
40
|
+
<span
|
|
41
|
+
className={cn(
|
|
42
|
+
'pointer-events-none absolute -right-0.5 -bottom-0.5 z-10',
|
|
43
|
+
badgeClassName,
|
|
44
|
+
)}
|
|
45
|
+
>
|
|
46
|
+
{badge}
|
|
47
|
+
</span>
|
|
48
|
+
) : null}
|
|
49
|
+
</span>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function Sprite({
|
|
54
|
+
src,
|
|
55
|
+
rect,
|
|
56
|
+
page,
|
|
57
|
+
size,
|
|
58
|
+
tint,
|
|
59
|
+
alt = '',
|
|
60
|
+
className,
|
|
61
|
+
fallback,
|
|
62
|
+
onError,
|
|
63
|
+
}: {
|
|
64
|
+
src?: string;
|
|
65
|
+
|
|
66
|
+
rect?: { x: number; y: number; w: number };
|
|
67
|
+
|
|
68
|
+
page?: { w: number; h: number };
|
|
69
|
+
size: number;
|
|
70
|
+
tint?: string;
|
|
71
|
+
alt?: string;
|
|
72
|
+
className?: string;
|
|
73
|
+
fallback?: ReactNode;
|
|
74
|
+
onError?: () => void;
|
|
75
|
+
}) {
|
|
76
|
+
if (!src) {
|
|
77
|
+
return (
|
|
78
|
+
fallback ?? (
|
|
79
|
+
<Skeleton
|
|
80
|
+
className={cn('inline-block shrink-0', className)}
|
|
81
|
+
style={{ width: size, height: size }}
|
|
82
|
+
/>
|
|
83
|
+
)
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
if (rect && page) {
|
|
87
|
+
const scale = size / Math.max(rect.w, 1);
|
|
88
|
+
const pos = `-${rect.x * scale}px -${rect.y * scale}px`;
|
|
89
|
+
const bgSize = `${page.w * scale}px ${page.h * scale}px`;
|
|
90
|
+
return (
|
|
91
|
+
<span
|
|
92
|
+
className={className ?? 'relative inline-block shrink-0'}
|
|
93
|
+
style={{
|
|
94
|
+
width: size,
|
|
95
|
+
height: size,
|
|
96
|
+
backgroundImage: `url(${src})`,
|
|
97
|
+
backgroundPosition: pos,
|
|
98
|
+
backgroundSize: bgSize,
|
|
99
|
+
imageRendering: 'pixelated',
|
|
100
|
+
...(tint
|
|
101
|
+
? {
|
|
102
|
+
backgroundColor: tint,
|
|
103
|
+
backgroundBlendMode: 'multiply',
|
|
104
|
+
WebkitMaskImage: `url(${src})`,
|
|
105
|
+
maskImage: `url(${src})`,
|
|
106
|
+
WebkitMaskPosition: pos,
|
|
107
|
+
maskPosition: pos,
|
|
108
|
+
WebkitMaskSize: bgSize,
|
|
109
|
+
maskSize: bgSize,
|
|
110
|
+
WebkitMaskRepeat: 'no-repeat',
|
|
111
|
+
maskRepeat: 'no-repeat',
|
|
112
|
+
}
|
|
113
|
+
: {}),
|
|
114
|
+
}}
|
|
115
|
+
/>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
return (
|
|
119
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
120
|
+
<img
|
|
121
|
+
src={src}
|
|
122
|
+
alt={alt}
|
|
123
|
+
width={size}
|
|
124
|
+
height={size}
|
|
125
|
+
className={cn('rounded-sm', className)}
|
|
126
|
+
style={{ width: size, height: size, imageRendering: 'pixelated' }}
|
|
127
|
+
loading="lazy"
|
|
128
|
+
decoding="async"
|
|
129
|
+
onError={onError}
|
|
130
|
+
/>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export const SPRITE_CLUSTER_UNITS = 18;
|
|
135
|
+
export const SPRITE_CLUSTER_INSET = 6;
|
|
136
|
+
const CLUSTER_BLEED = 1.15;
|
|
137
|
+
const CLUSTER_POSITIONS: readonly (readonly [number, number])[][] = [
|
|
138
|
+
[],
|
|
139
|
+
[[1.6, 1.6]],
|
|
140
|
+
[
|
|
141
|
+
[0.4, 0.8],
|
|
142
|
+
[2.8, 2.4],
|
|
143
|
+
],
|
|
144
|
+
[
|
|
145
|
+
[-0.8, 0],
|
|
146
|
+
[1.6, 1.6],
|
|
147
|
+
[4.0, 3.2],
|
|
148
|
+
],
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
export function SpriteCluster({
|
|
152
|
+
items,
|
|
153
|
+
size = 36,
|
|
154
|
+
chip = true,
|
|
155
|
+
inset = SPRITE_CLUSTER_INSET,
|
|
156
|
+
limit = 3,
|
|
157
|
+
}: {
|
|
158
|
+
items: ReactNode[];
|
|
159
|
+
size?: number;
|
|
160
|
+
chip?: boolean;
|
|
161
|
+
inset?: number;
|
|
162
|
+
limit?: number;
|
|
163
|
+
}) {
|
|
164
|
+
const inner = size - (chip ? inset : 0);
|
|
165
|
+
const scale = inner / SPRITE_CLUSTER_UNITS;
|
|
166
|
+
const shown = items.slice(0, Math.min(limit, CLUSTER_POSITIONS.length - 1));
|
|
167
|
+
const positions = CLUSTER_POSITIONS[shown.length];
|
|
168
|
+
return (
|
|
169
|
+
<SpriteFrame size={size} chip={chip}>
|
|
170
|
+
<span className="relative block" style={{ width: inner, height: inner }}>
|
|
171
|
+
{shown.map((node, i) => (
|
|
172
|
+
<span
|
|
173
|
+
key={i}
|
|
174
|
+
className="absolute"
|
|
175
|
+
style={{
|
|
176
|
+
left: positions[i][0] * CLUSTER_BLEED * scale,
|
|
177
|
+
top: positions[i][1] * CLUSTER_BLEED * scale,
|
|
178
|
+
zIndex: shown.length - i,
|
|
179
|
+
}}
|
|
180
|
+
>
|
|
181
|
+
{node}
|
|
182
|
+
</span>
|
|
183
|
+
))}
|
|
184
|
+
</span>
|
|
185
|
+
</SpriteFrame>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function SpriteCount({
|
|
190
|
+
count,
|
|
191
|
+
limit,
|
|
192
|
+
className,
|
|
193
|
+
style,
|
|
194
|
+
}: {
|
|
195
|
+
count: number;
|
|
196
|
+
limit?: number;
|
|
197
|
+
className?: string;
|
|
198
|
+
style?: CSSProperties;
|
|
199
|
+
}) {
|
|
200
|
+
if (count < 1) return null;
|
|
201
|
+
return (
|
|
202
|
+
<span aria-hidden className={className} style={style}>
|
|
203
|
+
{formatCount(count, limit)}
|
|
204
|
+
</span>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function SpriteList({
|
|
209
|
+
items,
|
|
210
|
+
variant = 'grid',
|
|
211
|
+
className,
|
|
212
|
+
}: {
|
|
213
|
+
items: { key: React.Key; node: ReactNode }[];
|
|
214
|
+
variant?: 'grid' | 'row';
|
|
215
|
+
className?: string;
|
|
216
|
+
}) {
|
|
217
|
+
return (
|
|
218
|
+
<ul
|
|
219
|
+
className={cn(
|
|
220
|
+
variant === 'grid'
|
|
221
|
+
? 'grid grid-cols-[repeat(auto-fill,minmax(--spacing(9),1fr))] gap-1'
|
|
222
|
+
: 'flex flex-wrap items-center gap-3',
|
|
223
|
+
className,
|
|
224
|
+
)}
|
|
225
|
+
>
|
|
226
|
+
{items.map((item) => (
|
|
227
|
+
<li key={item.key}>{item.node}</li>
|
|
228
|
+
))}
|
|
229
|
+
</ul>
|
|
230
|
+
);
|
|
231
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Fragment, type CSSProperties } from 'react';
|
|
2
|
+
|
|
3
|
+
export type TextFormat = {
|
|
4
|
+
marker: string;
|
|
5
|
+
colors: Record<string, string>;
|
|
6
|
+
styles?: Record<string, CSSProperties>;
|
|
7
|
+
reset?: string;
|
|
8
|
+
hex?: string;
|
|
9
|
+
ignore?: readonly string[];
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type Run = { text: string; style?: CSSProperties };
|
|
13
|
+
|
|
14
|
+
export function FormatText({
|
|
15
|
+
text,
|
|
16
|
+
format,
|
|
17
|
+
}: {
|
|
18
|
+
text: string;
|
|
19
|
+
format: TextFormat;
|
|
20
|
+
}) {
|
|
21
|
+
const codes = new Set([
|
|
22
|
+
...Object.keys(format.colors),
|
|
23
|
+
...Object.keys(format.styles ?? {}),
|
|
24
|
+
...(format.reset ? [format.reset] : []),
|
|
25
|
+
...(format.ignore ?? []),
|
|
26
|
+
]);
|
|
27
|
+
const runs: Run[] = [];
|
|
28
|
+
let style: CSSProperties | undefined;
|
|
29
|
+
let buf = '';
|
|
30
|
+
const flush = () => {
|
|
31
|
+
if (!buf) return;
|
|
32
|
+
runs.push(style ? { text: buf, style } : { text: buf });
|
|
33
|
+
buf = '';
|
|
34
|
+
};
|
|
35
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
36
|
+
const ch = text[i];
|
|
37
|
+
if (ch !== format.marker) {
|
|
38
|
+
buf += ch;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const code = text[i + 1]?.toLowerCase();
|
|
42
|
+
if (format.hex && code === format.hex) {
|
|
43
|
+
let hex = '';
|
|
44
|
+
let j = i + 2;
|
|
45
|
+
while (
|
|
46
|
+
hex.length < 6 &&
|
|
47
|
+
text[j] === format.marker &&
|
|
48
|
+
/^[0-9a-f]$/.test(text[j + 1]?.toLowerCase() ?? '')
|
|
49
|
+
) {
|
|
50
|
+
hex += (text[j + 1] ?? '').toLowerCase();
|
|
51
|
+
j += 2;
|
|
52
|
+
}
|
|
53
|
+
if (hex.length === 6) {
|
|
54
|
+
flush();
|
|
55
|
+
i = j - 1;
|
|
56
|
+
const next: CSSProperties = { ...(style ?? {}), color: `#${hex}` };
|
|
57
|
+
delete next.fontWeight;
|
|
58
|
+
delete next.fontStyle;
|
|
59
|
+
delete next.textDecoration;
|
|
60
|
+
style = next;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
buf += ch;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (code === undefined || !codes.has(code)) {
|
|
67
|
+
buf += ch;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
flush();
|
|
71
|
+
i += 1;
|
|
72
|
+
if (code === format.reset) {
|
|
73
|
+
style = undefined;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const next: CSSProperties = { ...(style ?? {}) };
|
|
77
|
+
const color = format.colors[code];
|
|
78
|
+
if (color) {
|
|
79
|
+
next.color = color;
|
|
80
|
+
delete next.fontWeight;
|
|
81
|
+
delete next.fontStyle;
|
|
82
|
+
delete next.textDecoration;
|
|
83
|
+
}
|
|
84
|
+
const extra = format.styles?.[code];
|
|
85
|
+
if (extra) {
|
|
86
|
+
for (const [key, value] of Object.entries(extra)) {
|
|
87
|
+
if (key === 'textDecoration' && next.textDecoration) {
|
|
88
|
+
next.textDecoration = `${next.textDecoration} ${String(value)}`;
|
|
89
|
+
} else {
|
|
90
|
+
(next as Record<string, unknown>)[key] = value;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
style = Object.keys(next).length ? next : undefined;
|
|
95
|
+
}
|
|
96
|
+
flush();
|
|
97
|
+
return (
|
|
98
|
+
<>
|
|
99
|
+
{runs.map((run, index) =>
|
|
100
|
+
run.style ? (
|
|
101
|
+
<span key={index} style={run.style}>
|
|
102
|
+
{run.text}
|
|
103
|
+
</span>
|
|
104
|
+
) : (
|
|
105
|
+
<Fragment key={index}>{run.text}</Fragment>
|
|
106
|
+
),
|
|
107
|
+
)}
|
|
108
|
+
</>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
@@ -17,12 +17,14 @@ export function ThemeToggle({
|
|
|
17
17
|
modes = ['light', 'dark', 'system'],
|
|
18
18
|
labels,
|
|
19
19
|
className,
|
|
20
|
+
buttonClassName,
|
|
20
21
|
...props
|
|
21
22
|
}: ComponentProps<'div'> & {
|
|
22
23
|
value?: string;
|
|
23
24
|
onValueChange?: (mode: ThemeToggleMode) => void;
|
|
24
25
|
modes?: readonly ThemeToggleMode[];
|
|
25
26
|
labels?: Partial<Record<ThemeToggleMode, string>>;
|
|
27
|
+
buttonClassName?: string;
|
|
26
28
|
}) {
|
|
27
29
|
const mounted = useSyncExternalStore(
|
|
28
30
|
noop,
|
|
@@ -57,6 +59,7 @@ export function ThemeToggle({
|
|
|
57
59
|
aria-pressed={active}
|
|
58
60
|
className={cn(
|
|
59
61
|
'size-6.5 p-1.5 text-muted-foreground',
|
|
62
|
+
buttonClassName,
|
|
60
63
|
active && 'bg-accent text-accent-foreground',
|
|
61
64
|
)}
|
|
62
65
|
onClick={() => select(mode)}
|
|
@@ -69,3 +72,4 @@ export function ThemeToggle({
|
|
|
69
72
|
</div>
|
|
70
73
|
);
|
|
71
74
|
}
|
|
75
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, type DependencyList, type ReactElement, type ReactNode } from 'react';
|
|
4
|
+
import { useAsyncValue } from '@terrafirmagreg-team/ui/hooks/use-async-value';
|
|
5
|
+
import {
|
|
6
|
+
Tooltip,
|
|
7
|
+
TooltipContent,
|
|
8
|
+
TooltipTrigger,
|
|
9
|
+
} from '@terrafirmagreg-team/ui/components/ui/tooltip';
|
|
10
|
+
|
|
11
|
+
export function LazyTooltip<T>({
|
|
12
|
+
load,
|
|
13
|
+
deps = [],
|
|
14
|
+
children,
|
|
15
|
+
trigger,
|
|
16
|
+
contentClassName,
|
|
17
|
+
onOpenChange,
|
|
18
|
+
}: {
|
|
19
|
+
load: () => Promise<T>;
|
|
20
|
+
deps?: DependencyList;
|
|
21
|
+
children: (value: T | undefined) => ReactNode;
|
|
22
|
+
trigger: ReactElement;
|
|
23
|
+
contentClassName?: string;
|
|
24
|
+
onOpenChange?: (open: boolean) => void;
|
|
25
|
+
}) {
|
|
26
|
+
const [open, setOpen] = useState(false);
|
|
27
|
+
const value = useAsyncValue(open ? load : false, [open, ...deps]);
|
|
28
|
+
return (
|
|
29
|
+
<Tooltip
|
|
30
|
+
onOpenChange={(next) => {
|
|
31
|
+
setOpen(next);
|
|
32
|
+
onOpenChange?.(next);
|
|
33
|
+
}}
|
|
34
|
+
>
|
|
35
|
+
<TooltipTrigger render={trigger} delay={150} closeDelay={100} />
|
|
36
|
+
<TooltipContent className={contentClassName}>
|
|
37
|
+
{children(value)}
|
|
38
|
+
</TooltipContent>
|
|
39
|
+
</Tooltip>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -55,16 +55,16 @@ export function ViewerZoomControls({
|
|
|
55
55
|
const t = useTranslations();
|
|
56
56
|
return (
|
|
57
57
|
<>
|
|
58
|
-
<IconButton overlay label={t('ui.canvas.zoom_in')} onClick={onZoomIn}>
|
|
58
|
+
<IconButton overlay label={t('Zoom in', { note: 'ui.canvas.zoom_in' })} onClick={onZoomIn}>
|
|
59
59
|
<ZoomIn className="size-3.5" />
|
|
60
60
|
</IconButton>
|
|
61
|
-
<IconButton overlay label={t('ui.canvas.zoom_out')} onClick={onZoomOut}>
|
|
61
|
+
<IconButton overlay label={t('Zoom out', { note: 'ui.canvas.zoom_out' })} onClick={onZoomOut}>
|
|
62
62
|
<ZoomOut className="size-3.5" />
|
|
63
63
|
</IconButton>
|
|
64
64
|
{onFit ? (
|
|
65
65
|
<IconButton
|
|
66
66
|
overlay
|
|
67
|
-
label={fitLabel ?? t('ui.canvas.fit')}
|
|
67
|
+
label={fitLabel ?? t('Fit view', { note: 'ui.canvas.fit' })}
|
|
68
68
|
onClick={onFit}
|
|
69
69
|
>
|
|
70
70
|
<Maximize2 className="size-3.5" />
|
|
@@ -85,7 +85,7 @@ export function ViewerPagerControls({
|
|
|
85
85
|
<>
|
|
86
86
|
<IconButton
|
|
87
87
|
overlay
|
|
88
|
-
label={t('ui.canvas.prev')}
|
|
88
|
+
label={t('Previous', { note: 'ui.canvas.prev' })}
|
|
89
89
|
disabled={index === 0}
|
|
90
90
|
onClick={() => onSelect(index - 1)}
|
|
91
91
|
>
|
|
@@ -102,7 +102,7 @@ export function ViewerPagerControls({
|
|
|
102
102
|
</ButtonGroupText>
|
|
103
103
|
<IconButton
|
|
104
104
|
overlay
|
|
105
|
-
label={t('ui.canvas.next')}
|
|
105
|
+
label={t('Next', { note: 'ui.canvas.next' })}
|
|
106
106
|
disabled={index === count - 1}
|
|
107
107
|
onClick={() => onSelect(index + 1)}
|
|
108
108
|
>
|
|
@@ -146,7 +146,11 @@ export function ViewerExpandButton({ target }: ExpandButtonProps) {
|
|
|
146
146
|
<IconButton
|
|
147
147
|
overlay
|
|
148
148
|
aria-expanded={expanded}
|
|
149
|
-
label={
|
|
149
|
+
label={
|
|
150
|
+
expanded
|
|
151
|
+
? t('Exit fullscreen', { note: 'ui.canvas.exit_fullscreen' })
|
|
152
|
+
: t('Fullscreen', { note: 'ui.canvas.fullscreen' })
|
|
153
|
+
}
|
|
150
154
|
onClick={() => {
|
|
151
155
|
const el = target.current;
|
|
152
156
|
if (fallback) {
|
|
@@ -277,3 +281,4 @@ export function ViewerControls({
|
|
|
277
281
|
</>
|
|
278
282
|
);
|
|
279
283
|
}
|
|
284
|
+
|
|
@@ -9,7 +9,7 @@ import type * as PageTree from '@terrafirmagreg-team/translate/types/tree';
|
|
|
9
9
|
import Link from '@terrafirmagreg-team/ui/components/blocks/nav/link';
|
|
10
10
|
import { SidebarIcon } from 'lucide-react';
|
|
11
11
|
|
|
12
|
-
import { LayoutContext, useLayout } from '@terrafirmagreg-team/ui/hooks/layout';
|
|
12
|
+
import { LayoutContext, useLayout } from '@terrafirmagreg-team/ui/hooks/use-layout';
|
|
13
13
|
import { TreeContextProvider } from '@terrafirmagreg-team/ui/components/layouts/tree-context';
|
|
14
14
|
import {
|
|
15
15
|
SidebarProvider,
|
|
@@ -91,3 +91,4 @@ export function LayoutHeader({ className, ...props }: ComponentProps<'header'>)
|
|
|
91
91
|
</header>
|
|
92
92
|
);
|
|
93
93
|
}
|
|
94
|
+
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
3
3
|
import { buttonVariants } from '@terrafirmagreg-team/ui/components/ui/button';
|
|
4
4
|
import { SidebarIcon } from 'lucide-react';
|
|
5
|
-
import { useSidebar } from '@terrafirmagreg-team/ui/hooks/sidebar';
|
|
5
|
+
import { useSidebar } from '@terrafirmagreg-team/ui/hooks/use-sidebar';
|
|
6
6
|
import { SidebarCollapseTrigger } from '@terrafirmagreg-team/ui/components/blocks/sidebar';
|
|
7
7
|
|
|
8
8
|
export function LeftSidebarExpand() {
|
|
@@ -20,3 +20,4 @@ export function LeftSidebarExpand() {
|
|
|
20
20
|
</SidebarCollapseTrigger>
|
|
21
21
|
);
|
|
22
22
|
}
|
|
23
|
+
|
|
@@ -36,7 +36,7 @@ export function LeftSidebarLanguage() {
|
|
|
36
36
|
locales={locales}
|
|
37
37
|
selected={locale}
|
|
38
38
|
onSelect={(value) => onLocaleChange?.(value)}
|
|
39
|
-
label={t('ui.choose_language')}
|
|
39
|
+
label={t('Choose a language', { note: 'ui.choose_language' })}
|
|
40
40
|
/>
|
|
41
41
|
);
|
|
42
42
|
}
|
|
@@ -67,12 +67,12 @@ export function LeftSidebarPlate({
|
|
|
67
67
|
>
|
|
68
68
|
{children}
|
|
69
69
|
{(tools != null || theme != null) && (
|
|
70
|
-
<div className="ms-auto flex items-center">
|
|
70
|
+
<div className="ms-auto flex items-center gap-0.5">
|
|
71
71
|
{tools}
|
|
72
72
|
{tools != null && theme != null && (
|
|
73
73
|
<Separator
|
|
74
74
|
orientation="vertical"
|
|
75
|
-
className="mx-1
|
|
75
|
+
className="mx-1 h-5 self-center! bg-muted-foreground/30"
|
|
76
76
|
aria-hidden
|
|
77
77
|
/>
|
|
78
78
|
)}
|
|
@@ -97,9 +97,15 @@ export function LeftSidebarActions({
|
|
|
97
97
|
<LeftSidebarPlate
|
|
98
98
|
ref={plateRef}
|
|
99
99
|
tools={SettingsMenu ? <SettingsMenu anchor={plateRef} /> : undefined}
|
|
100
|
-
theme={{
|
|
100
|
+
theme={{
|
|
101
|
+
value: theme,
|
|
102
|
+
onValueChange: setTheme,
|
|
103
|
+
className: 'p-0',
|
|
104
|
+
buttonClassName: 'size-7',
|
|
105
|
+
}}
|
|
101
106
|
>
|
|
102
107
|
{children}
|
|
103
108
|
</LeftSidebarPlate>
|
|
104
109
|
);
|
|
105
110
|
}
|
|
111
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import type { ComponentProps } from 'react';
|
|
3
|
-
import { useFolderDepth, useSidebar } from '@terrafirmagreg-team/ui/hooks/sidebar';
|
|
3
|
+
import { useFolderDepth, useSidebar } from '@terrafirmagreg-team/ui/hooks/use-sidebar';
|
|
4
4
|
import { SidebarFolder, SidebarFolderContent as BaseFolderContent, SidebarFolderLink as BaseFolderLink, SidebarFolderTrigger as BaseFolderTrigger, SidebarItem as BaseItem } from '@terrafirmagreg-team/ui/components/blocks/sidebar';
|
|
5
5
|
import { Separator } from '@terrafirmagreg-team/ui/components/ui/separator';
|
|
6
6
|
|
|
@@ -100,3 +100,4 @@ export function LeftSidebarLabel({
|
|
|
100
100
|
/>
|
|
101
101
|
);
|
|
102
102
|
}
|
|
103
|
+
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
3
3
|
import type { ComponentProps, ReactNode } from 'react';
|
|
4
4
|
import { Search } from 'lucide-react';
|
|
5
|
-
import { useSearchContext } from '@terrafirmagreg-team/ui/hooks/search/provider';
|
|
5
|
+
import { useSearchContext } from '@terrafirmagreg-team/ui/hooks/search/use-provider';
|
|
6
6
|
import { useTranslations } from '@terrafirmagreg-team/translate/hooks/use-translations';
|
|
7
7
|
import { IconButton } from '@terrafirmagreg-team/ui/components/blocks/button/icon-button';
|
|
8
8
|
|
|
@@ -55,7 +55,7 @@ export function LeftSidebarSearch({
|
|
|
55
55
|
if (variant === 'icon')
|
|
56
56
|
return (
|
|
57
57
|
<IconButton
|
|
58
|
-
label={t('ui.search_trigger')}
|
|
58
|
+
label={t('Search', { note: 'ui.search_trigger' })}
|
|
59
59
|
variant="ghost"
|
|
60
60
|
size="icon-sm"
|
|
61
61
|
tooltip={false}
|
|
@@ -66,9 +66,10 @@ export function LeftSidebarSearch({
|
|
|
66
66
|
);
|
|
67
67
|
return (
|
|
68
68
|
<LeftSidebarSearchTrigger
|
|
69
|
-
label={t('ui.search_trigger')}
|
|
69
|
+
label={t('Search', { note: 'ui.search_trigger' })}
|
|
70
70
|
onClick={() => setOpenSearch(true)}
|
|
71
71
|
keys={hotKey?.map((k) => k.display)}
|
|
72
72
|
/>
|
|
73
73
|
);
|
|
74
74
|
}
|
|
75
|
+
|
|
@@ -3,9 +3,9 @@ import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
|
3
3
|
import { buttonVariants } from '@terrafirmagreg-team/ui/components/ui/button';
|
|
4
4
|
import type { ComponentProps, ReactNode } from 'react';
|
|
5
5
|
import { SidebarIcon } from 'lucide-react';
|
|
6
|
-
import { useSidebar } from '@terrafirmagreg-team/ui/hooks/sidebar';
|
|
6
|
+
import { useSidebar } from '@terrafirmagreg-team/ui/hooks/use-sidebar';
|
|
7
7
|
import { SidebarCollapseTrigger, SidebarDrawerContent, SidebarDrawerOverlay, SidebarTrigger } from '@terrafirmagreg-team/ui/components/blocks/sidebar';
|
|
8
|
-
import { useLayout } from '@terrafirmagreg-team/ui/hooks/layout';
|
|
8
|
+
import { useLayout } from '@terrafirmagreg-team/ui/hooks/use-layout';
|
|
9
9
|
import { LeftSidebarContent } from './content';
|
|
10
10
|
import { LeftSidebarFooter } from './footer';
|
|
11
11
|
import type { LeftSidebarParts } from './context';
|
|
@@ -186,3 +186,4 @@ function LeftSidebarRegions({
|
|
|
186
186
|
</>
|
|
187
187
|
);
|
|
188
188
|
}
|
|
189
|
+
|
|
@@ -176,9 +176,10 @@ function CopyButton({ id }: { id: string }) {
|
|
|
176
176
|
{checked ? <Check className="size-3.5" /> : <LinkIcon className="size-3.5" />}
|
|
177
177
|
<span className="sr-only">
|
|
178
178
|
{checked
|
|
179
|
-
? t('
|
|
180
|
-
: t('
|
|
179
|
+
? t('Copied Link', { note: 'ui.copied_link' })
|
|
180
|
+
: t('Copy Link', { note: 'ui.copy_link' })}
|
|
181
181
|
</span>
|
|
182
182
|
</button>
|
|
183
183
|
);
|
|
184
184
|
}
|
|
185
|
+
|