@drakkar.software/octospaces-ui 0.2.1 → 0.4.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/dist/index.d.ts +353 -2
- package/dist/index.js +715 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +22 -0
- package/src/lightbox/Lightbox.tsx +132 -0
- package/src/sidebar/Sidebar.tsx +101 -0
- package/src/sidebar/SidebarActionButton.tsx +86 -0
- package/src/sidebar/SidebarHeader.tsx +111 -0
- package/src/sidebar/SidebarItem.tsx +148 -0
- package/src/sidebar/SpacesRail.tsx +590 -0
- package/src/sidebar/index.ts +11 -0
- package/src/sidebar/tile-state.test.ts +126 -0
- package/src/sidebar/tile-state.ts +100 -0
- package/src/sidebar/types.ts +25 -0
- package/src/theme/helpers.test.ts +1 -0
- package/src/theme/types.ts +1 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { View, StyleProp, ViewStyle } from 'react-native';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Theme contract for `@drakkar.software/octospaces-ui`.
|
|
@@ -26,6 +27,7 @@ interface Palette {
|
|
|
26
27
|
surfaceModal: string;
|
|
27
28
|
surfaceInput: string;
|
|
28
29
|
sidebar: string;
|
|
30
|
+
sidebarPanel: string;
|
|
29
31
|
sidebarActive: string;
|
|
30
32
|
border: string;
|
|
31
33
|
borderSubtle: string;
|
|
@@ -349,4 +351,353 @@ interface DiscoverScreenProps {
|
|
|
349
351
|
}
|
|
350
352
|
declare function DiscoverScreen({ loadEntries, renderIcon, onOpen, title, emptyMessage, emptySearchMessage, searchEnabled, reloadRef, }: DiscoverScreenProps): React.JSX.Element;
|
|
351
353
|
|
|
352
|
-
|
|
354
|
+
/**
|
|
355
|
+
* Shared types for the SpacesRail component.
|
|
356
|
+
*
|
|
357
|
+
* Structurally compatible with `Space` from `@drakkar.software/octochat-sdk` /
|
|
358
|
+
* `@drakkar.software/octospaces-sdk` — apps can pass their domain objects directly
|
|
359
|
+
* without a runtime conversion step.
|
|
360
|
+
*/
|
|
361
|
+
/** A minimal space descriptor for the rail: id + display info + badge state. */
|
|
362
|
+
interface RailSpace {
|
|
363
|
+
/** Unique space identifier. */
|
|
364
|
+
id: string;
|
|
365
|
+
/** Short display name or initials shown as a monogram when no image is available. */
|
|
366
|
+
short: string;
|
|
367
|
+
/** Optional image URI (data URI or URL) rendered as the tile background via
|
|
368
|
+
* `SpacesRailProps.renderTileImage`. */
|
|
369
|
+
image?: string;
|
|
370
|
+
/** Unread-message count shown as a badge overlay. */
|
|
371
|
+
unread?: number;
|
|
372
|
+
/** Whether the space is muted (shows a mute-corner icon when `renderIcon` is provided). */
|
|
373
|
+
muted?: boolean;
|
|
374
|
+
}
|
|
375
|
+
/** Named icon slots injected into the rail via `SpacesRailProps.renderIcon`. */
|
|
376
|
+
type RailIconName = 'dm' | 'lock' | 'mute' | 'add';
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Headless, abstractly-themed vertical spaces rail.
|
|
380
|
+
*
|
|
381
|
+
* The component reads the injected {@link Theme} via `useOctoSpacesTheme()` and
|
|
382
|
+
* delegates all app-specific concerns to props:
|
|
383
|
+
*
|
|
384
|
+
* - Icons are rendered by `renderIcon` (keeps `@expo/vector-icons` out of this package).
|
|
385
|
+
* - Space tile images are rendered by `renderTileImage` (keeps `expo-image` out too).
|
|
386
|
+
* - Unread badges are rendered by `renderBadge`.
|
|
387
|
+
* - The rail foot (account avatar + menu) is rendered by `renderFoot`.
|
|
388
|
+
* - Web drag-reorder is wired via the `useTileDnd` hook prop (see below).
|
|
389
|
+
*
|
|
390
|
+
* All React Native primitives used here ship with the `react-native` peer dep.
|
|
391
|
+
*/
|
|
392
|
+
|
|
393
|
+
interface SpacesRailProps {
|
|
394
|
+
/** The spaces to show in the scrollable column. */
|
|
395
|
+
spaces: RailSpace[];
|
|
396
|
+
/** The currently-active space id (or null / undefined for none). */
|
|
397
|
+
activeId?: string | null;
|
|
398
|
+
/** Called when the user selects a space tile. */
|
|
399
|
+
onSelect?: (id: string) => void;
|
|
400
|
+
/** Called when the user taps the "add" tile. */
|
|
401
|
+
onAdd?: () => void;
|
|
402
|
+
/** When provided, renders a leading DM-home tile. */
|
|
403
|
+
onSelectDms?: () => void;
|
|
404
|
+
/** Whether the DM-home tile is the active selection. */
|
|
405
|
+
dmsActive?: boolean;
|
|
406
|
+
/** Unread count for the DM-home tile badge. */
|
|
407
|
+
dmUnread?: number;
|
|
408
|
+
/** Accessibility label for the DM-home tile (default: "Direct messages"). */
|
|
409
|
+
dmLabel?: string;
|
|
410
|
+
/** Accessibility label for the add-space tile (default: "Create or join a space"). */
|
|
411
|
+
addLabel?: string;
|
|
412
|
+
/**
|
|
413
|
+
* Render a named icon at the given size and color. Used for the DM tile icon
|
|
414
|
+
* (`'dm'`), the E2EE lock corner (`'lock'`), the mute corner (`'mute'`),
|
|
415
|
+
* and the add tile icon (`'add'`). Return `null` to suppress the icon slot.
|
|
416
|
+
* If omitted, all icon slots render nothing.
|
|
417
|
+
*/
|
|
418
|
+
renderIcon?: (name: RailIconName, size: number, color: string) => React.ReactNode;
|
|
419
|
+
/**
|
|
420
|
+
* Render an image filling the tile background. Only called when `space.image`
|
|
421
|
+
* is set. The component must fill its parent (`StyleSheet.absoluteFill` or
|
|
422
|
+
* equivalent). If omitted, the short-name monogram is shown instead.
|
|
423
|
+
*/
|
|
424
|
+
renderTileImage?: (space: RailSpace) => React.ReactNode;
|
|
425
|
+
/**
|
|
426
|
+
* Render an unread badge. Only called when `space.unread > 0`.
|
|
427
|
+
* If omitted, badges are not shown.
|
|
428
|
+
*/
|
|
429
|
+
renderBadge?: (count: number) => React.ReactNode;
|
|
430
|
+
/**
|
|
431
|
+
* When `true`, each tile shows a small E2EE-lock corner badge (bottom-right).
|
|
432
|
+
* Requires `renderIcon` to be provided (otherwise the corner renders nothing).
|
|
433
|
+
* Default: `false`.
|
|
434
|
+
*/
|
|
435
|
+
showLockCorner?: boolean;
|
|
436
|
+
/**
|
|
437
|
+
* Render the pinned rail foot (e.g. the account avatar and popover).
|
|
438
|
+
* The host app owns this entirely — identity state stays out of the package.
|
|
439
|
+
*/
|
|
440
|
+
renderFoot?: () => React.ReactNode;
|
|
441
|
+
/**
|
|
442
|
+
* **Hook injection for web drag-reorder.** When provided, each space tile is
|
|
443
|
+
* wrapped in a `DndTile` that calls `useTileDnd(spaceId)` unconditionally at
|
|
444
|
+
* the top of its render — treat this prop as a React hook and keep it stable
|
|
445
|
+
* for the lifetime of a `SpacesRail` mount (always provided or always absent).
|
|
446
|
+
* Omit on native / in apps that don't need DnD.
|
|
447
|
+
*/
|
|
448
|
+
useTileDnd?: (spaceId: string) => {
|
|
449
|
+
ref?: React.Ref<View>;
|
|
450
|
+
over?: boolean;
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Vertical spaces rail — a 64px-wide column of square space tiles, a DM-home tile,
|
|
455
|
+
* an add-space tile, and a pinned foot for the account widget.
|
|
456
|
+
*
|
|
457
|
+
* Styled entirely from the injected {@link Theme} via `useOctoSpacesTheme()`.
|
|
458
|
+
* All icons, images, badges, and the account foot are provided by the host app.
|
|
459
|
+
*/
|
|
460
|
+
declare function SpacesRail({ spaces, activeId, onSelect, onAdd, onSelectDms, dmsActive, dmUnread, dmLabel, addLabel, renderIcon, renderTileImage, renderBadge, showLockCorner, renderFoot, useTileDnd, }: SpacesRailProps): React.JSX.Element;
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Headless themed sidebar panel shell.
|
|
464
|
+
*
|
|
465
|
+
* Renders the 240–248px wide panel: a background surface (colors.sidebarPanel),
|
|
466
|
+
* a right border (colors.border), an optional header slot, the item list
|
|
467
|
+
* (scrollable by default), and an optional footer slot.
|
|
468
|
+
*
|
|
469
|
+
* All content is delegated to the host via slots — only the chrome (bg, border,
|
|
470
|
+
* width) and the ScrollView wrapper are shared.
|
|
471
|
+
*
|
|
472
|
+
* ```tsx
|
|
473
|
+
* <Sidebar
|
|
474
|
+
* header={
|
|
475
|
+
* <SidebarHeader
|
|
476
|
+
* leading={<SpaceSwitcher variant="sidebar" />}
|
|
477
|
+
* actions={<>
|
|
478
|
+
* <IconButton name="search" ... />
|
|
479
|
+
* <IconButton name="plus" ... />
|
|
480
|
+
* </>}
|
|
481
|
+
* />
|
|
482
|
+
* }
|
|
483
|
+
* contentContainerStyle={{ paddingHorizontal: 8 }}
|
|
484
|
+
* >
|
|
485
|
+
* <WorkObjects ... />
|
|
486
|
+
* </Sidebar>
|
|
487
|
+
* ```
|
|
488
|
+
*/
|
|
489
|
+
|
|
490
|
+
interface SidebarProps {
|
|
491
|
+
/** Header slot — render a {@link SidebarHeader} or custom content above the list. */
|
|
492
|
+
header?: React.ReactNode;
|
|
493
|
+
/** Footer slot — pinned below the scroll area. */
|
|
494
|
+
footer?: React.ReactNode;
|
|
495
|
+
/** The item list. Wrapped in a ScrollView unless `scrollable` is `false`. */
|
|
496
|
+
children: React.ReactNode;
|
|
497
|
+
/** Panel width in pixels. Defaults to `theme.layout.sidebarWidth ?? 248`. */
|
|
498
|
+
width?: number;
|
|
499
|
+
/**
|
|
500
|
+
* When `false`, children are rendered in a plain `View` instead of a `ScrollView`.
|
|
501
|
+
* Use when the host manages its own scroll (e.g. multiple independent lists).
|
|
502
|
+
* @default true
|
|
503
|
+
*/
|
|
504
|
+
scrollable?: boolean;
|
|
505
|
+
/**
|
|
506
|
+
* Passed to the `ScrollView`'s `contentContainerStyle` (or to the body `View`
|
|
507
|
+
* style when `scrollable` is `false`).
|
|
508
|
+
*/
|
|
509
|
+
contentContainerStyle?: StyleProp<ViewStyle>;
|
|
510
|
+
/**
|
|
511
|
+
* Override the panel background color.
|
|
512
|
+
* Defaults to `colors.sidebarPanel` from the injected theme.
|
|
513
|
+
*/
|
|
514
|
+
background?: string;
|
|
515
|
+
}
|
|
516
|
+
declare function Sidebar({ header, footer, children, width, scrollable, contentContainerStyle, background, }: SidebarProps): React.JSX.Element;
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Headless themed sidebar header strip.
|
|
520
|
+
*
|
|
521
|
+
* Row 1: a `leading` slot (flex:1, typically a space selector or name) + an
|
|
522
|
+
* optional `actions` row (right-aligned command buttons). An optional `extra`
|
|
523
|
+
* slot below row 1 accepts additional controls (OctoChat uses this for its
|
|
524
|
+
* `ModeSwitcher` + jump-to search bar). An optional bottom hairline divider.
|
|
525
|
+
*
|
|
526
|
+
* ```tsx
|
|
527
|
+
* // OctoVault — space switcher + command icons
|
|
528
|
+
* <SidebarHeader
|
|
529
|
+
* leading={<SpaceSwitcher variant="sidebar" />}
|
|
530
|
+
* actions={<>
|
|
531
|
+
* <IconButton name="search" onPress={openSearch} tooltip="Search" shortcut="⌘K" />
|
|
532
|
+
* <IconButton name="plus" onPress={newPage} tooltip="New page" shortcut="⌘N" />
|
|
533
|
+
* <IconButton name="sidebar" onPress={collapse} tooltip="Hide sidebar" shortcut="⌘\\" />
|
|
534
|
+
* </>}
|
|
535
|
+
* />
|
|
536
|
+
*
|
|
537
|
+
* // OctoChat — space menu + mode switcher + jump-to bar
|
|
538
|
+
* <SidebarHeader
|
|
539
|
+
* leading={<Pressable onPress={onOpenSpaceMenu}>…</Pressable>}
|
|
540
|
+
* extra={<><ModeSwitcher /><JumpToBar /></>}
|
|
541
|
+
* divider
|
|
542
|
+
* />
|
|
543
|
+
* ```
|
|
544
|
+
*/
|
|
545
|
+
|
|
546
|
+
interface SidebarHeaderProps {
|
|
547
|
+
/** Leading content (flex:1) — typically the space selector / space name. */
|
|
548
|
+
leading: React.ReactNode;
|
|
549
|
+
/**
|
|
550
|
+
* Right-aligned action buttons row.
|
|
551
|
+
* OctoVault passes its own `IconButton` components here (with tooltips +
|
|
552
|
+
* keyboard shortcuts). For simpler headless consumers use `SidebarActionButton`.
|
|
553
|
+
*/
|
|
554
|
+
actions?: React.ReactNode;
|
|
555
|
+
/**
|
|
556
|
+
* Extra slot rendered below the leading+actions row.
|
|
557
|
+
* Use for secondary controls like a mode switcher or a search bar.
|
|
558
|
+
*/
|
|
559
|
+
extra?: React.ReactNode;
|
|
560
|
+
/**
|
|
561
|
+
* Render a hairline bottom divider in `colors.borderSubtle`.
|
|
562
|
+
* @default false
|
|
563
|
+
*/
|
|
564
|
+
divider?: boolean;
|
|
565
|
+
/**
|
|
566
|
+
* Style applied to the outer container. Use to set host-specific padding,
|
|
567
|
+
* gap, background override, etc.
|
|
568
|
+
*/
|
|
569
|
+
style?: StyleProp<ViewStyle>;
|
|
570
|
+
}
|
|
571
|
+
declare function SidebarHeader({ leading, actions, extra, divider, style, }: SidebarHeaderProps): React.JSX.Element;
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Headless themed icon-button primitive for sidebar action slots.
|
|
575
|
+
*
|
|
576
|
+
* Renders a square `Pressable` with hover/press wash from the injected theme.
|
|
577
|
+
* The icon is provided by the host app as a `ReactNode` — this keeps the package
|
|
578
|
+
* free of `@expo/vector-icons`, `Tooltip`, and keyboard-shortcut concerns.
|
|
579
|
+
*
|
|
580
|
+
* Host apps with richer icon buttons (OctoVault's `IconButton` has tooltips,
|
|
581
|
+
* keyboard-shortcut labels, and haptics) can slot those directly into
|
|
582
|
+
* `SidebarHeader.actions` instead — this primitive is for simpler headless
|
|
583
|
+
* consumers.
|
|
584
|
+
*
|
|
585
|
+
* ```tsx
|
|
586
|
+
* <SidebarActionButton
|
|
587
|
+
* icon={<Icon name="search" size={15} color={theme.colors.textSecondary} />}
|
|
588
|
+
* onPress={openSearch}
|
|
589
|
+
* accessibilityLabel="Search"
|
|
590
|
+
* />
|
|
591
|
+
* ```
|
|
592
|
+
*/
|
|
593
|
+
|
|
594
|
+
interface SidebarActionButtonProps {
|
|
595
|
+
/** Icon element to render — the host provides the icon component, size, and color. */
|
|
596
|
+
icon: React.ReactNode;
|
|
597
|
+
onPress: () => void;
|
|
598
|
+
accessibilityLabel: string;
|
|
599
|
+
/**
|
|
600
|
+
* Width and height of the pressable target in pixels.
|
|
601
|
+
* @default 32
|
|
602
|
+
*/
|
|
603
|
+
size?: number;
|
|
604
|
+
}
|
|
605
|
+
declare function SidebarActionButton({ icon, onPress, accessibilityLabel, size, }: SidebarActionButtonProps): React.JSX.Element;
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Generic themed sidebar navigation row.
|
|
609
|
+
*
|
|
610
|
+
* The headless analog of `DiscoverRow` for the sidebar panel. Suitable for
|
|
611
|
+
* simple link-style rows (explore, threads, pinned, …). Complex item lists
|
|
612
|
+
* (OctoVault's `ObjectTree`, OctoChat's `RoomCategoryList`) use their own row
|
|
613
|
+
* components — this primitive is for straightforward nav items.
|
|
614
|
+
*
|
|
615
|
+
* Active rows are highlighted with `colors.sidebarActive`. Hovered rows receive
|
|
616
|
+
* a subtle `colors.primarySubtle` wash.
|
|
617
|
+
*
|
|
618
|
+
* ```tsx
|
|
619
|
+
* <SidebarItem
|
|
620
|
+
* label="Threads"
|
|
621
|
+
* icon={<Icon name="thread" size={15} color={colors.textSecondary} />}
|
|
622
|
+
* active={threadsActive}
|
|
623
|
+
* onPress={onOpenThreads}
|
|
624
|
+
* />
|
|
625
|
+
* ```
|
|
626
|
+
*/
|
|
627
|
+
|
|
628
|
+
interface SidebarItemProps {
|
|
629
|
+
label: string;
|
|
630
|
+
/** Leading icon element — the host provides the icon component. */
|
|
631
|
+
icon?: React.ReactNode;
|
|
632
|
+
/** Highlight the row as the current destination. */
|
|
633
|
+
active?: boolean;
|
|
634
|
+
/** Badge shown at the trailing edge — a number or short string. */
|
|
635
|
+
badge?: number | string;
|
|
636
|
+
onPress: () => void;
|
|
637
|
+
onLongPress?: () => void;
|
|
638
|
+
/** Additional trailing element (e.g. an action button). */
|
|
639
|
+
trailing?: React.ReactNode;
|
|
640
|
+
/**
|
|
641
|
+
* Left indentation level for nested items. Each level adds 16 px.
|
|
642
|
+
* @default 0
|
|
643
|
+
*/
|
|
644
|
+
indent?: number;
|
|
645
|
+
}
|
|
646
|
+
declare function SidebarItem({ label, icon, active, badge, onPress, onLongPress, trailing, indent, }: SidebarItemProps): React.JSX.Element;
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Full-screen scrim overlay that centers its content. Tapping the backdrop, the
|
|
650
|
+
* close button, the Escape key (web) or the hardware back (Android) dismisses it.
|
|
651
|
+
*
|
|
652
|
+
* All interactive chrome (close button, action buttons) is injected via render
|
|
653
|
+
* props so this package remains free of @expo/vector-icons, expo-image, and
|
|
654
|
+
* reanimated. The host app renders its own icon buttons and images.
|
|
655
|
+
*
|
|
656
|
+
* @example
|
|
657
|
+
* ```tsx
|
|
658
|
+
* import { Lightbox } from '@drakkar.software/octospaces-ui';
|
|
659
|
+
*
|
|
660
|
+
* <Lightbox
|
|
661
|
+
* visible={zoomed}
|
|
662
|
+
* onClose={() => setZoomed(false)}
|
|
663
|
+
* renderCloseButton={(onClose) => (
|
|
664
|
+
* <IconButton name="x" color={colors.onScrim} onPress={onClose} />
|
|
665
|
+
* )}
|
|
666
|
+
* renderActions={() => (
|
|
667
|
+
* <IconButton name="share" color={colors.onScrim} onPress={handleShare} />
|
|
668
|
+
* )}
|
|
669
|
+
* >
|
|
670
|
+
* <Image source={{ uri }} style={{ width: w * 0.92, height: h * 0.82 }} />
|
|
671
|
+
* </Lightbox>
|
|
672
|
+
* ```
|
|
673
|
+
*/
|
|
674
|
+
|
|
675
|
+
interface LightboxProps {
|
|
676
|
+
visible: boolean;
|
|
677
|
+
onClose: () => void;
|
|
678
|
+
/** Centered content — the host renders the full-size image here. */
|
|
679
|
+
children: ReactNode;
|
|
680
|
+
/** Accessible label for the backdrop tap-to-close. Default: "Close preview". */
|
|
681
|
+
closeLabel?: string;
|
|
682
|
+
/**
|
|
683
|
+
* Render the close affordance pinned to the top-right corner.
|
|
684
|
+
* Receives `onClose` so the button can dismiss the overlay.
|
|
685
|
+
* If omitted, tapping the backdrop or hardware back still closes it.
|
|
686
|
+
*/
|
|
687
|
+
renderCloseButton?: (onClose: () => void) => ReactNode;
|
|
688
|
+
/**
|
|
689
|
+
* Render additional action(s) pinned to the bottom-right corner,
|
|
690
|
+
* e.g. a save/share button. Return `null` to show nothing.
|
|
691
|
+
*/
|
|
692
|
+
renderActions?: () => ReactNode;
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Full-screen scrim overlay that centers its children. Headless: all buttons
|
|
696
|
+
* are injected via render props; the package has no icon or image dependencies.
|
|
697
|
+
*
|
|
698
|
+
* Dismissal: backdrop tap · `renderCloseButton` · hardware back (Android) ·
|
|
699
|
+
* Escape key (web).
|
|
700
|
+
*/
|
|
701
|
+
declare function Lightbox({ visible, onClose, children, closeLabel, renderCloseButton, renderActions, }: LightboxProps): React.JSX.Element;
|
|
702
|
+
|
|
703
|
+
export { type ColorScheme, type DiscoverEntry, DiscoverList, type DiscoverListProps, DiscoverRow, type DiscoverRowProps, DiscoverScreen, type DiscoverScreenProps, type Easing, type Fonts, type LabelTracking, type Layers, type Layout, Lightbox, type LightboxProps, type Motion, type MotionToken, OctoSpacesThemeProvider, type OctoSpacesThemeProviderProps, type Opacity, type Palette, type Radii, type RailIconName, type RailSpace, type ShadowToken, type Shadows, Sidebar, SidebarActionButton, type SidebarActionButtonProps, SidebarHeader, type SidebarHeaderProps, SidebarItem, type SidebarItemProps, type SidebarProps, SpacesRail, type SpacesRailProps, type Spacing, type Swatches, type Theme, type TypeScale, type Typography, avatarTint, filterDiscoverEntries, focusRingStyle, glowShadow, paperBorder, presenceColor, sortDiscoverEntries, statusColor, swatch, useOctoSpacesTheme, verificationColor };
|