@moldable-ai/ui 0.2.20 → 0.2.22
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/catalog/catalog-coverage.d.ts +15 -0
- package/dist/catalog/catalog-coverage.d.ts.map +1 -0
- package/dist/catalog/catalog-coverage.js +44 -0
- package/dist/catalog/catalog-manifest.d.ts.map +1 -1
- package/dist/catalog/catalog-manifest.js +35 -23
- package/dist/catalog/catalog-types.d.ts +17 -0
- package/dist/catalog/catalog-types.d.ts.map +1 -1
- package/dist/catalog/catalog-types.js +7 -0
- package/dist/catalog/component-catalog.d.ts.map +1 -1
- package/dist/catalog/component-catalog.js +16 -6
- package/dist/catalog/core-catalog-exclusions.d.ts +8 -0
- package/dist/catalog/core-catalog-exclusions.d.ts.map +1 -0
- package/dist/catalog/core-catalog-exclusions.js +155 -0
- package/dist/catalog/core-catalog.d.ts +0 -4
- package/dist/catalog/core-catalog.d.ts.map +1 -1
- package/dist/catalog/core-catalog.js +12 -98
- package/dist/catalog/core-collection-stories.d.ts +3 -0
- package/dist/catalog/core-collection-stories.d.ts.map +1 -0
- package/dist/catalog/core-collection-stories.js +42 -0
- package/dist/catalog/core-control-stories.d.ts +3 -0
- package/dist/catalog/core-control-stories.d.ts.map +1 -0
- package/dist/catalog/core-control-stories.js +97 -0
- package/dist/catalog/core-feedback-stories.d.ts +3 -0
- package/dist/catalog/core-feedback-stories.d.ts.map +1 -0
- package/dist/catalog/core-feedback-stories.js +50 -0
- package/dist/catalog/core-foundation-stories.d.ts +3 -0
- package/dist/catalog/core-foundation-stories.d.ts.map +1 -0
- package/dist/catalog/core-foundation-stories.js +53 -0
- package/dist/catalog/core-navigation-stories.d.ts +3 -0
- package/dist/catalog/core-navigation-stories.d.ts.map +1 -0
- package/dist/catalog/core-navigation-stories.js +25 -0
- package/dist/catalog/core-overlay-stories.d.ts +3 -0
- package/dist/catalog/core-overlay-stories.d.ts.map +1 -0
- package/dist/catalog/core-overlay-stories.js +20 -0
- package/dist/catalog/index.d.ts +3 -1
- package/dist/catalog/index.d.ts.map +1 -1
- package/dist/catalog/index.js +3 -1
- package/dist/components/app-error-boundary.d.ts.map +1 -1
- package/dist/components/app-error-boundary.js +3 -2
- package/dist/components/chat/chat-panel.d.ts +0 -1
- package/dist/components/chat/chat-panel.d.ts.map +1 -1
- package/dist/components/chat/chat-panel.js +7 -11
- package/dist/components/ui/alert-dialog.d.ts +10 -4
- package/dist/components/ui/alert-dialog.d.ts.map +1 -1
- package/dist/components/ui/alert-dialog.js +14 -6
- package/dist/components/ui/alert.d.ts +19 -3
- package/dist/components/ui/alert.d.ts.map +1 -1
- package/dist/components/ui/alert.js +25 -8
- package/dist/components/ui/app-frame.d.ts +9 -3
- package/dist/components/ui/app-frame.d.ts.map +1 -1
- package/dist/components/ui/app-frame.js +45 -6
- package/dist/components/ui/button.d.ts +6 -4
- package/dist/components/ui/button.d.ts.map +1 -1
- package/dist/components/ui/button.js +40 -8
- package/dist/components/ui/collapsible.d.ts +9 -1
- package/dist/components/ui/collapsible.d.ts.map +1 -1
- package/dist/components/ui/collapsible.js +13 -2
- package/dist/components/ui/collection-navigation.d.ts +8 -0
- package/dist/components/ui/collection-navigation.d.ts.map +1 -0
- package/dist/components/ui/collection-navigation.js +19 -0
- package/dist/components/ui/command.d.ts +5 -1
- package/dist/components/ui/command.d.ts.map +1 -1
- package/dist/components/ui/command.js +13 -1
- package/dist/components/ui/confirm-dialog.d.ts +19 -0
- package/dist/components/ui/confirm-dialog.d.ts.map +1 -0
- package/dist/components/ui/confirm-dialog.js +45 -0
- package/dist/components/ui/date-picker.d.ts +24 -0
- package/dist/components/ui/date-picker.d.ts.map +1 -0
- package/dist/components/ui/date-picker.js +92 -0
- package/dist/components/ui/dialog-primary-action.d.ts +4 -0
- package/dist/components/ui/dialog-primary-action.d.ts.map +1 -0
- package/dist/components/ui/dialog-primary-action.js +37 -0
- package/dist/components/ui/dialog.d.ts +8 -2
- package/dist/components/ui/dialog.d.ts.map +1 -1
- package/dist/components/ui/dialog.js +15 -5
- package/dist/components/ui/empty.d.ts +7 -2
- package/dist/components/ui/empty.d.ts.map +1 -1
- package/dist/components/ui/empty.js +20 -7
- package/dist/components/ui/error-boundary-view.d.ts +12 -0
- package/dist/components/ui/error-boundary-view.d.ts.map +1 -0
- package/dist/components/ui/error-boundary-view.js +8 -0
- package/dist/components/ui/grid.d.ts.map +1 -1
- package/dist/components/ui/grid.js +70 -46
- package/dist/components/ui/icon-button.d.ts.map +1 -1
- package/dist/components/ui/icon-button.js +1 -2
- package/dist/components/ui/index.d.ts +5 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +5 -0
- package/dist/components/ui/input-group.d.ts +1 -1
- package/dist/components/ui/list.d.ts +5 -1
- package/dist/components/ui/list.d.ts.map +1 -1
- package/dist/components/ui/list.js +27 -16
- package/dist/components/ui/material.d.ts +34 -0
- package/dist/components/ui/material.d.ts.map +1 -0
- package/dist/components/ui/material.js +34 -0
- package/dist/components/ui/message-scroller-controller.d.ts +162 -0
- package/dist/components/ui/message-scroller-controller.d.ts.map +1 -0
- package/dist/components/ui/message-scroller-controller.js +888 -0
- package/dist/components/ui/message-scroller-policy.d.ts +74 -0
- package/dist/components/ui/message-scroller-policy.d.ts.map +1 -0
- package/dist/components/ui/message-scroller-policy.js +129 -0
- package/dist/components/ui/message-scroller-primitive.d.ts +55 -0
- package/dist/components/ui/message-scroller-primitive.d.ts.map +1 -0
- package/dist/components/ui/message-scroller-primitive.js +226 -0
- package/dist/components/ui/message-scroller.d.ts +2 -2
- package/dist/components/ui/message-scroller.d.ts.map +1 -1
- package/dist/components/ui/message-scroller.js +3 -15
- package/dist/components/ui/navigation-button-group.d.ts +16 -0
- package/dist/components/ui/navigation-button-group.d.ts.map +1 -0
- package/dist/components/ui/navigation-button-group.js +9 -0
- package/dist/components/ui/popover.d.ts +9 -2
- package/dist/components/ui/popover.d.ts.map +1 -1
- package/dist/components/ui/popover.js +13 -3
- package/dist/components/ui/scroll-area.d.ts +8 -2
- package/dist/components/ui/scroll-area.d.ts.map +1 -1
- package/dist/components/ui/scroll-area.js +6 -3
- package/dist/components/ui/search-field.d.ts.map +1 -1
- package/dist/components/ui/segmented-control.d.ts.map +1 -1
- package/dist/components/ui/segmented-control.js +6 -5
- package/dist/components/ui/sheet.d.ts +4 -2
- package/dist/components/ui/sheet.d.ts.map +1 -1
- package/dist/components/ui/sheet.js +12 -9
- package/dist/components/ui/sonner.d.ts +1 -1
- package/dist/components/ui/sonner.d.ts.map +1 -1
- package/dist/components/ui/spinner.d.ts.map +1 -1
- package/dist/components/ui/spinner.js +1 -1
- package/dist/components/ui/table.d.ts.map +1 -1
- package/dist/components/ui/table.js +7 -2
- package/dist/components/ui/toggle.d.ts +12 -4
- package/dist/components/ui/toggle.d.ts.map +1 -1
- package/dist/components/ui/toggle.js +13 -2
- package/dist/components/ui/toolbar.d.ts +9 -2
- package/dist/components/ui/toolbar.d.ts.map +1 -1
- package/dist/components/ui/toolbar.js +23 -4
- package/dist/components/ui/tooltip.d.ts +8 -3
- package/dist/components/ui/tooltip.d.ts.map +1 -1
- package/dist/components/ui/tooltip.js +11 -6
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/lib/frame-lifecycle.d.ts +23 -0
- package/dist/lib/frame-lifecycle.d.ts.map +1 -1
- package/dist/lib/frame-lifecycle.js +30 -3
- package/dist/lib/host-date-time-picker.d.ts +36 -0
- package/dist/lib/host-date-time-picker.d.ts.map +1 -0
- package/dist/lib/host-date-time-picker.js +162 -0
- package/dist/lib/host-file-dialog.d.ts +66 -0
- package/dist/lib/host-file-dialog.d.ts.map +1 -0
- package/dist/lib/host-file-dialog.js +221 -0
- package/dist/lib/host-native-menu.d.ts +75 -0
- package/dist/lib/host-native-menu.d.ts.map +1 -0
- package/dist/lib/host-native-menu.js +187 -0
- package/dist/lib/host-services.d.ts +13 -0
- package/dist/lib/host-services.d.ts.map +1 -0
- package/dist/lib/host-services.js +161 -0
- package/dist/lib/host-share.d.ts +39 -0
- package/dist/lib/host-share.d.ts.map +1 -0
- package/dist/lib/host-share.js +161 -0
- package/dist/lib/theme.d.ts.map +1 -1
- package/dist/lib/theme.js +2 -1
- package/docs/README.md +4 -1
- package/docs/adaptive-materials.md +79 -0
- package/docs/app-anatomy.md +3 -1
- package/docs/component-catalog.md +26 -3
- package/docs/design-tokens.md +17 -4
- package/docs/foundations.md +5 -3
- package/docs/standalone-app-windows.md +8 -0
- package/docs/visual-regression.md +78 -0
- package/package.json +12 -4
- package/src/components/ui/alert-dialog.md +58 -0
- package/src/components/ui/alert.md +28 -6
- package/src/components/ui/app-frame.md +5 -0
- package/src/components/ui/button.md +10 -3
- package/src/components/ui/collapsible.md +52 -0
- package/src/components/ui/combobox.md +0 -1
- package/src/components/ui/command.md +54 -0
- package/src/components/ui/date-field.md +1 -1
- package/src/components/ui/date-picker.md +55 -0
- package/src/components/ui/dialog.md +59 -0
- package/src/components/ui/empty.md +8 -3
- package/src/components/ui/error-boundary-view.md +98 -0
- package/src/components/ui/grid.md +8 -4
- package/src/components/ui/icon-button.md +2 -2
- package/src/components/ui/list.md +35 -2
- package/src/components/ui/material.md +51 -0
- package/src/components/ui/navigation-button-group.md +46 -0
- package/src/components/ui/popover.md +31 -0
- package/src/components/ui/scroll-area.md +11 -7
- package/src/components/ui/search-field.md +1 -1
- package/src/components/ui/segmented-control.md +2 -5
- package/src/components/ui/sheet.md +30 -0
- package/src/components/ui/table.md +4 -2
- package/src/components/ui/toggle-button.md +48 -0
- package/src/components/ui/toolbar.md +5 -0
- package/src/components/ui/tooltip.md +31 -0
- package/src/styles/index.css +213 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure scroll-ownership policy for the chat transcript.
|
|
3
|
+
*
|
|
4
|
+
* One rule outranks everything: the reader's scroll intent wins instantly.
|
|
5
|
+
* Programmatic motion is explicit — every scroll the primitive itself starts
|
|
6
|
+
* declares a target — so any viewport movement that was not commanded is, by
|
|
7
|
+
* definition, the user. That includes native scrollbar drags, which emit no
|
|
8
|
+
* wheel, pointer, or key events at all.
|
|
9
|
+
*
|
|
10
|
+
* The previous stack (upstream shadcn primitive + a local layout-stability
|
|
11
|
+
* patch) ran two competing controllers with grace timers and an
|
|
12
|
+
* "autoscrolling" veto window; a scrollbar drag during streaming could not
|
|
13
|
+
* release follow mode, so the next content growth yanked the viewport back
|
|
14
|
+
* down. This module replaces both with a single mode machine, mirroring the
|
|
15
|
+
* MessageScrollerPolicy used by the iOS transcript.
|
|
16
|
+
*/
|
|
17
|
+
export type MessageScrollerMode =
|
|
18
|
+
/** Pinned to the newest output; content growth keeps the end in view. */
|
|
19
|
+
'following-end'
|
|
20
|
+
/** The reader owns the position; the scroller never moves it. */
|
|
21
|
+
| 'free'
|
|
22
|
+
/** A turn anchor is pinned near the viewport top while output streams. */
|
|
23
|
+
| 'anchored'
|
|
24
|
+
/** A commanded jump is in flight; resolves on arrival or user motion. */
|
|
25
|
+
| 'settling-jump';
|
|
26
|
+
export type MessageScrollerResizeAction = 'stick-to-end' | 'reanchor' | 'preserve-reading-position' | 'none';
|
|
27
|
+
export interface MessageScrollerScrollEvent {
|
|
28
|
+
scrollTop: number;
|
|
29
|
+
lastScrollTop: number;
|
|
30
|
+
atEnd: boolean;
|
|
31
|
+
}
|
|
32
|
+
export declare class MessageScrollerPolicy {
|
|
33
|
+
mode: MessageScrollerMode;
|
|
34
|
+
autoScroll: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* The destination of the programmatic scroll currently in flight, if any.
|
|
37
|
+
* Smooth scrolls pass through many intermediate positions; they count as
|
|
38
|
+
* our own motion only while they keep approaching this target.
|
|
39
|
+
*/
|
|
40
|
+
programmaticTarget: number | null;
|
|
41
|
+
private lastDistanceToTarget;
|
|
42
|
+
constructor(autoScroll: boolean);
|
|
43
|
+
get followsEnd(): boolean;
|
|
44
|
+
/** Declares a programmatic scroll about to start toward `target`. */
|
|
45
|
+
beginProgrammaticScroll(target: number): void;
|
|
46
|
+
/**
|
|
47
|
+
* Abandons an in-flight programmatic scroll that will never arrive (the
|
|
48
|
+
* animation was dropped or its target became unreachable). Subsequent
|
|
49
|
+
* movement is classified as the reader's.
|
|
50
|
+
*/
|
|
51
|
+
cancelProgrammaticScroll(): void;
|
|
52
|
+
/**
|
|
53
|
+
* Classifies a scroll event and returns true when it was the reader's own
|
|
54
|
+
* motion. Programmatic motion is recognized by monotonic approach to the
|
|
55
|
+
* declared target; everything else — wheel, trackpad, keyboard, scrollbar
|
|
56
|
+
* drag, momentum — is user motion and takes ownership immediately.
|
|
57
|
+
*/
|
|
58
|
+
handleScrollEvent(event: MessageScrollerScrollEvent): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Explicit input intent (wheel, touch, scroll keys). Upward intent releases
|
|
61
|
+
* immediately — before the scroll event lands — so a resize racing the
|
|
62
|
+
* same frame cannot pull the viewport down first.
|
|
63
|
+
*/
|
|
64
|
+
handleUserIntent(direction: 'up' | 'down' | 'unknown'): void;
|
|
65
|
+
/** How a content size change may move the viewport in the current mode. */
|
|
66
|
+
resizeAction(): MessageScrollerResizeAction;
|
|
67
|
+
/** A new turn anchor was appended and commanded into view. */
|
|
68
|
+
anchorToTurn(): void;
|
|
69
|
+
beginJump(): void;
|
|
70
|
+
jumpToEnd(): void;
|
|
71
|
+
jumpToStart(): void;
|
|
72
|
+
release(): void;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=message-scroller-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-scroller-policy.d.ts","sourceRoot":"","sources":["../../../src/components/ui/message-scroller-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,mBAAmB;AAC7B,yEAAyE;AACvE,eAAe;AACjB,iEAAiE;GAC/D,MAAM;AACR,0EAA0E;GACxE,UAAU;AACZ,yEAAyE;GACvE,eAAe,CAAA;AAEnB,MAAM,MAAM,2BAA2B,GACnC,cAAc,GACd,UAAU,GACV,2BAA2B,GAC3B,MAAM,CAAA;AAKV,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,KAAK,EAAE,OAAO,CAAA;CACf;AAED,qBAAa,qBAAqB;IAChC,IAAI,EAAE,mBAAmB,CAAA;IACzB,UAAU,EAAE,OAAO,CAAA;IACnB;;;;OAIG;IACH,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAO;IACxC,OAAO,CAAC,oBAAoB,CAA2B;gBAE3C,UAAU,EAAE,OAAO;IAK/B,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,qEAAqE;IACrE,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAK7C;;;;OAIG;IACH,wBAAwB,IAAI,IAAI;IAOhC;;;;;OAKG;IACH,iBAAiB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO;IA6B7D;;;;OAIG;IACH,gBAAgB,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI;IAK5D,2EAA2E;IAC3E,YAAY,IAAI,2BAA2B;IAgB3C,8DAA8D;IAC9D,YAAY,IAAI,IAAI;IAIpB,SAAS,IAAI,IAAI;IAIjB,SAAS,IAAI,IAAI;IAIjB,WAAW,IAAI,IAAI;IAInB,OAAO,IAAI,IAAI;CAGhB"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure scroll-ownership policy for the chat transcript.
|
|
3
|
+
*
|
|
4
|
+
* One rule outranks everything: the reader's scroll intent wins instantly.
|
|
5
|
+
* Programmatic motion is explicit — every scroll the primitive itself starts
|
|
6
|
+
* declares a target — so any viewport movement that was not commanded is, by
|
|
7
|
+
* definition, the user. That includes native scrollbar drags, which emit no
|
|
8
|
+
* wheel, pointer, or key events at all.
|
|
9
|
+
*
|
|
10
|
+
* The previous stack (upstream shadcn primitive + a local layout-stability
|
|
11
|
+
* patch) ran two competing controllers with grace timers and an
|
|
12
|
+
* "autoscrolling" veto window; a scrollbar drag during streaming could not
|
|
13
|
+
* release follow mode, so the next content growth yanked the viewport back
|
|
14
|
+
* down. This module replaces both with a single mode machine, mirroring the
|
|
15
|
+
* MessageScrollerPolicy used by the iOS transcript.
|
|
16
|
+
*/
|
|
17
|
+
const SCROLL_EPSILON_PX = 0.5;
|
|
18
|
+
const TARGET_ARRIVAL_PX = 1;
|
|
19
|
+
export class MessageScrollerPolicy {
|
|
20
|
+
mode;
|
|
21
|
+
autoScroll;
|
|
22
|
+
/**
|
|
23
|
+
* The destination of the programmatic scroll currently in flight, if any.
|
|
24
|
+
* Smooth scrolls pass through many intermediate positions; they count as
|
|
25
|
+
* our own motion only while they keep approaching this target.
|
|
26
|
+
*/
|
|
27
|
+
programmaticTarget = null;
|
|
28
|
+
lastDistanceToTarget = Number.POSITIVE_INFINITY;
|
|
29
|
+
constructor(autoScroll) {
|
|
30
|
+
this.autoScroll = autoScroll;
|
|
31
|
+
this.mode = autoScroll ? 'following-end' : 'free';
|
|
32
|
+
}
|
|
33
|
+
get followsEnd() {
|
|
34
|
+
return this.mode === 'following-end';
|
|
35
|
+
}
|
|
36
|
+
/** Declares a programmatic scroll about to start toward `target`. */
|
|
37
|
+
beginProgrammaticScroll(target) {
|
|
38
|
+
this.programmaticTarget = target;
|
|
39
|
+
this.lastDistanceToTarget = Number.POSITIVE_INFINITY;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Abandons an in-flight programmatic scroll that will never arrive (the
|
|
43
|
+
* animation was dropped or its target became unreachable). Subsequent
|
|
44
|
+
* movement is classified as the reader's.
|
|
45
|
+
*/
|
|
46
|
+
cancelProgrammaticScroll() {
|
|
47
|
+
if (this.programmaticTarget === null)
|
|
48
|
+
return;
|
|
49
|
+
this.programmaticTarget = null;
|
|
50
|
+
this.lastDistanceToTarget = Number.POSITIVE_INFINITY;
|
|
51
|
+
if (this.mode === 'settling-jump')
|
|
52
|
+
this.mode = 'free';
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Classifies a scroll event and returns true when it was the reader's own
|
|
56
|
+
* motion. Programmatic motion is recognized by monotonic approach to the
|
|
57
|
+
* declared target; everything else — wheel, trackpad, keyboard, scrollbar
|
|
58
|
+
* drag, momentum — is user motion and takes ownership immediately.
|
|
59
|
+
*/
|
|
60
|
+
handleScrollEvent(event) {
|
|
61
|
+
if (this.programmaticTarget !== null) {
|
|
62
|
+
const distance = Math.abs(event.scrollTop - this.programmaticTarget);
|
|
63
|
+
if (distance <= TARGET_ARRIVAL_PX) {
|
|
64
|
+
this.programmaticTarget = null;
|
|
65
|
+
this.lastDistanceToTarget = Number.POSITIVE_INFINITY;
|
|
66
|
+
if (this.mode === 'settling-jump') {
|
|
67
|
+
this.mode = event.atEnd && this.autoScroll ? 'following-end' : 'free';
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
if (distance <= this.lastDistanceToTarget + SCROLL_EPSILON_PX) {
|
|
72
|
+
this.lastDistanceToTarget = distance;
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
// Motion away from our own target: the reader grabbed the viewport
|
|
76
|
+
// mid-flight. The jump loses.
|
|
77
|
+
this.programmaticTarget = null;
|
|
78
|
+
this.lastDistanceToTarget = Number.POSITIVE_INFINITY;
|
|
79
|
+
}
|
|
80
|
+
const movedUp = event.scrollTop < event.lastScrollTop - SCROLL_EPSILON_PX;
|
|
81
|
+
const movedDown = event.scrollTop > event.lastScrollTop + SCROLL_EPSILON_PX;
|
|
82
|
+
if (!movedUp && !movedDown)
|
|
83
|
+
return false;
|
|
84
|
+
this.mode = event.atEnd && this.autoScroll ? 'following-end' : 'free';
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Explicit input intent (wheel, touch, scroll keys). Upward intent releases
|
|
89
|
+
* immediately — before the scroll event lands — so a resize racing the
|
|
90
|
+
* same frame cannot pull the viewport down first.
|
|
91
|
+
*/
|
|
92
|
+
handleUserIntent(direction) {
|
|
93
|
+
if (this.mode === 'following-end' && direction === 'down')
|
|
94
|
+
return;
|
|
95
|
+
this.mode = 'free';
|
|
96
|
+
}
|
|
97
|
+
/** How a content size change may move the viewport in the current mode. */
|
|
98
|
+
resizeAction() {
|
|
99
|
+
switch (this.mode) {
|
|
100
|
+
case 'following-end':
|
|
101
|
+
return 'stick-to-end';
|
|
102
|
+
case 'anchored':
|
|
103
|
+
return 'reanchor';
|
|
104
|
+
case 'settling-jump':
|
|
105
|
+
return 'none';
|
|
106
|
+
case 'free':
|
|
107
|
+
// A collapsing disclosure above the reader must not shift what they
|
|
108
|
+
// are looking at. Native scroll anchoring usually handles it; the
|
|
109
|
+
// caller re-pins the first visible stable leaf when it does not.
|
|
110
|
+
return 'preserve-reading-position';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/** A new turn anchor was appended and commanded into view. */
|
|
114
|
+
anchorToTurn() {
|
|
115
|
+
this.mode = 'anchored';
|
|
116
|
+
}
|
|
117
|
+
beginJump() {
|
|
118
|
+
this.mode = 'settling-jump';
|
|
119
|
+
}
|
|
120
|
+
jumpToEnd() {
|
|
121
|
+
this.mode = this.autoScroll ? 'following-end' : 'free';
|
|
122
|
+
}
|
|
123
|
+
jumpToStart() {
|
|
124
|
+
this.mode = 'free';
|
|
125
|
+
}
|
|
126
|
+
release() {
|
|
127
|
+
this.mode = 'free';
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type MessageScrollerDefaultScrollPosition, type MessageScrollerScrollAlign, type MessageScrollerScrollOptions, type MessageScrollerScrollable, type MessageScrollerVisibilityState } from './message-scroller-controller';
|
|
3
|
+
type MessageScrollerProviderProps = {
|
|
4
|
+
children?: React.ReactNode;
|
|
5
|
+
autoScroll?: boolean;
|
|
6
|
+
defaultScrollPosition?: MessageScrollerDefaultScrollPosition;
|
|
7
|
+
scrollEdgeThreshold?: number;
|
|
8
|
+
scrollPreviousItemPeek?: number;
|
|
9
|
+
scrollMargin?: number;
|
|
10
|
+
};
|
|
11
|
+
declare function MessageScrollerProvider({ autoScroll, children, defaultScrollPosition, scrollEdgeThreshold, scrollPreviousItemPeek, scrollMargin, }: MessageScrollerProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
type MessageScrollerProps = React.ComponentProps<'div'>;
|
|
13
|
+
declare function MessageScrollerRoot({ children, ...props }: MessageScrollerProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
type MessageScrollerViewportProps = React.ComponentProps<'div'> & {
|
|
15
|
+
preserveScrollOnPrepend?: boolean;
|
|
16
|
+
};
|
|
17
|
+
declare function MessageScrollerViewport({ 'aria-label': ariaLabel, children, onKeyDown, onScroll, onTouchMove, onWheel, preserveScrollOnPrepend, ref, role, tabIndex, ...props }: MessageScrollerViewportProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
type MessageScrollerContentProps = React.ComponentProps<'div'> & {
|
|
19
|
+
spacerClassName?: string;
|
|
20
|
+
};
|
|
21
|
+
declare function MessageScrollerContent({ 'aria-relevant': ariaRelevant, children, ref, role, spacerClassName, ...props }: MessageScrollerContentProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
type MessageScrollerItemProps = React.ComponentProps<'div'> & {
|
|
23
|
+
messageId?: string;
|
|
24
|
+
scrollAnchor?: boolean;
|
|
25
|
+
};
|
|
26
|
+
declare function MessageScrollerItem({ messageId, ref, scrollAnchor, ...props }: MessageScrollerItemProps): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
type MessageScrollerButtonDirection = 'start' | 'end';
|
|
28
|
+
type MessageScrollerButtonRenderState = {
|
|
29
|
+
active: boolean;
|
|
30
|
+
direction: MessageScrollerButtonDirection;
|
|
31
|
+
};
|
|
32
|
+
type MessageScrollerButtonProps = Omit<React.ComponentProps<'button'>, 'children'> & {
|
|
33
|
+
behavior?: ScrollBehavior;
|
|
34
|
+
children?: React.ReactNode;
|
|
35
|
+
direction?: MessageScrollerButtonDirection;
|
|
36
|
+
render?: React.ReactElement<Record<string, unknown>> | ((props: Record<string, unknown>, state: MessageScrollerButtonRenderState) => React.ReactElement | null);
|
|
37
|
+
};
|
|
38
|
+
declare function MessageScrollerButton({ behavior, children, className, direction, onClick, render, tabIndex, type, ...props }: MessageScrollerButtonProps): React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | null;
|
|
39
|
+
declare function useMessageScroller(): {
|
|
40
|
+
scrollToEnd: (options?: MessageScrollerScrollOptions) => boolean;
|
|
41
|
+
scrollToMessage: (messageId: string, options?: MessageScrollerScrollOptions) => boolean;
|
|
42
|
+
scrollToStart: (options?: MessageScrollerScrollOptions) => boolean;
|
|
43
|
+
};
|
|
44
|
+
declare function useMessageScrollerScrollable(): MessageScrollerScrollable;
|
|
45
|
+
declare function useMessageScrollerVisibility(): MessageScrollerVisibilityState;
|
|
46
|
+
declare const MessageScroller: {
|
|
47
|
+
Provider: typeof MessageScrollerProvider;
|
|
48
|
+
Root: typeof MessageScrollerRoot;
|
|
49
|
+
Viewport: typeof MessageScrollerViewport;
|
|
50
|
+
Content: typeof MessageScrollerContent;
|
|
51
|
+
Item: typeof MessageScrollerItem;
|
|
52
|
+
Button: typeof MessageScrollerButton;
|
|
53
|
+
};
|
|
54
|
+
export { MessageScroller, useMessageScroller, useMessageScrollerScrollable, useMessageScrollerVisibility, type MessageScrollerDefaultScrollPosition, type MessageScrollerScrollAlign, type MessageScrollerScrollOptions, type MessageScrollerScrollable, type MessageScrollerVisibilityState, };
|
|
55
|
+
//# sourceMappingURL=message-scroller-primitive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-scroller-primitive.d.ts","sourceRoot":"","sources":["../../../src/components/ui/message-scroller-primitive.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { MessageScrollerController, } from './message-scroller-controller';
|
|
5
|
+
/**
|
|
6
|
+
* The transcript scroller primitive, owned in-repo. Drop-in compatible with
|
|
7
|
+
* the `@shadcn/react/message-scroller` surface it replaces, but built around
|
|
8
|
+
* a single scroll-ownership policy (`message-scroller-policy.ts`) shared in
|
|
9
|
+
* design with the iOS transcript. See that module for the rules; the React
|
|
10
|
+
* layer here only wires DOM events and observers to the controller.
|
|
11
|
+
*/
|
|
12
|
+
const SCROLL_KEYS = new Set([
|
|
13
|
+
'ArrowDown',
|
|
14
|
+
'ArrowUp',
|
|
15
|
+
'End',
|
|
16
|
+
'Home',
|
|
17
|
+
'PageDown',
|
|
18
|
+
'PageUp',
|
|
19
|
+
' ',
|
|
20
|
+
]);
|
|
21
|
+
const ControllerContext = React.createContext(null);
|
|
22
|
+
const RegisterMessageContext = React.createContext(null);
|
|
23
|
+
function useController() {
|
|
24
|
+
const controller = React.useContext(ControllerContext);
|
|
25
|
+
if (!controller) {
|
|
26
|
+
throw new Error('useMessageScroller must be used within a MessageScroller.');
|
|
27
|
+
}
|
|
28
|
+
return controller;
|
|
29
|
+
}
|
|
30
|
+
function MessageScrollerProvider({ autoScroll = false, children, defaultScrollPosition = 'end', scrollEdgeThreshold = 8, scrollPreviousItemPeek = 64, scrollMargin = 0, }) {
|
|
31
|
+
const options = {
|
|
32
|
+
autoScroll,
|
|
33
|
+
defaultScrollPosition,
|
|
34
|
+
scrollEdgeThreshold,
|
|
35
|
+
scrollPreviousItemPeek,
|
|
36
|
+
scrollMargin,
|
|
37
|
+
};
|
|
38
|
+
const controllerRef = React.useRef(null);
|
|
39
|
+
controllerRef.current ??= new MessageScrollerController(options);
|
|
40
|
+
const controller = controllerRef.current;
|
|
41
|
+
const previousDefaultPosition = React.useRef(defaultScrollPosition);
|
|
42
|
+
if (previousDefaultPosition.current !== defaultScrollPosition) {
|
|
43
|
+
previousDefaultPosition.current = defaultScrollPosition;
|
|
44
|
+
controller.resetDefaultPosition();
|
|
45
|
+
}
|
|
46
|
+
controller.updateOptions(options);
|
|
47
|
+
React.useEffect(() => () => controller.destroy(), [controller]);
|
|
48
|
+
const registerMessage = React.useCallback((messageId, element, previous) => controller.registerMessage(messageId, element, previous), [controller]);
|
|
49
|
+
return (_jsx(ControllerContext.Provider, { value: controller, children: _jsx(RegisterMessageContext.Provider, { value: registerMessage, children: children }) }));
|
|
50
|
+
}
|
|
51
|
+
function MessageScrollerRoot({ children, ...props }) {
|
|
52
|
+
const controller = useController();
|
|
53
|
+
const setRoot = React.useCallback((element) => controller.setRoot(element), [controller]);
|
|
54
|
+
return (_jsx("div", { ref: setRoot, ...props, children: children }));
|
|
55
|
+
}
|
|
56
|
+
function MessageScrollerViewport({ 'aria-label': ariaLabel, children, onKeyDown, onScroll, onTouchMove, onWheel, preserveScrollOnPrepend = true, ref, role, tabIndex, ...props }) {
|
|
57
|
+
const controller = useController();
|
|
58
|
+
controller.preserveScrollOnPrepend = preserveScrollOnPrepend;
|
|
59
|
+
const setViewport = React.useCallback((element) => {
|
|
60
|
+
controller.setViewport(element);
|
|
61
|
+
if (typeof ref === 'function')
|
|
62
|
+
ref(element);
|
|
63
|
+
else if (ref)
|
|
64
|
+
ref.current = element;
|
|
65
|
+
}, [controller, ref]);
|
|
66
|
+
React.useEffect(() => {
|
|
67
|
+
const viewport = controller.viewportElement;
|
|
68
|
+
if (!viewport || typeof ResizeObserver === 'undefined')
|
|
69
|
+
return;
|
|
70
|
+
let frame = 0;
|
|
71
|
+
const observer = new ResizeObserver(() => {
|
|
72
|
+
cancelAnimationFrame(frame);
|
|
73
|
+
frame = requestAnimationFrame(() => controller.handleResize());
|
|
74
|
+
});
|
|
75
|
+
observer.observe(viewport);
|
|
76
|
+
return () => {
|
|
77
|
+
cancelAnimationFrame(frame);
|
|
78
|
+
observer.disconnect();
|
|
79
|
+
};
|
|
80
|
+
}, [controller]);
|
|
81
|
+
return (_jsx("div", { ref: setViewport, role: role ?? 'region', "aria-label": ariaLabel ?? 'Messages', tabIndex: tabIndex ?? 0, onScroll: (event) => {
|
|
82
|
+
controller.handleScroll();
|
|
83
|
+
onScroll?.(event);
|
|
84
|
+
}, onWheel: (event) => {
|
|
85
|
+
controller.handleWheel(event.deltaY);
|
|
86
|
+
onWheel?.(event);
|
|
87
|
+
}, onTouchMove: (event) => {
|
|
88
|
+
controller.handleTouchMove();
|
|
89
|
+
onTouchMove?.(event);
|
|
90
|
+
}, onKeyDown: (event) => {
|
|
91
|
+
if (SCROLL_KEYS.has(event.key))
|
|
92
|
+
controller.handleScrollKey();
|
|
93
|
+
onKeyDown?.(event);
|
|
94
|
+
}, ...props, children: children }));
|
|
95
|
+
}
|
|
96
|
+
function MessageScrollerContent({ 'aria-relevant': ariaRelevant, children, ref, role, spacerClassName, ...props }) {
|
|
97
|
+
const controller = useController();
|
|
98
|
+
const contentRef = React.useRef(null);
|
|
99
|
+
const setContent = React.useCallback((element) => {
|
|
100
|
+
contentRef.current = element;
|
|
101
|
+
controller.setContent(element);
|
|
102
|
+
if (typeof ref === 'function')
|
|
103
|
+
ref(element);
|
|
104
|
+
else if (ref)
|
|
105
|
+
ref.current = element;
|
|
106
|
+
}, [controller, ref]);
|
|
107
|
+
const setSpacer = React.useCallback((element) => controller.setSpacer(element), [controller]);
|
|
108
|
+
React.useLayoutEffect(() => {
|
|
109
|
+
const content = contentRef.current;
|
|
110
|
+
if (!content)
|
|
111
|
+
return;
|
|
112
|
+
controller.handleContentMutation();
|
|
113
|
+
if (typeof MutationObserver === 'undefined')
|
|
114
|
+
return;
|
|
115
|
+
const observer = new MutationObserver(() => controller.handleContentMutation());
|
|
116
|
+
observer.observe(content, { childList: true });
|
|
117
|
+
return () => observer.disconnect();
|
|
118
|
+
}, [controller]);
|
|
119
|
+
React.useEffect(() => {
|
|
120
|
+
const content = contentRef.current;
|
|
121
|
+
if (!content || typeof ResizeObserver === 'undefined')
|
|
122
|
+
return;
|
|
123
|
+
let frame = 0;
|
|
124
|
+
const observer = new ResizeObserver(() => {
|
|
125
|
+
cancelAnimationFrame(frame);
|
|
126
|
+
frame = requestAnimationFrame(() => controller.handleResize());
|
|
127
|
+
});
|
|
128
|
+
observer.observe(content);
|
|
129
|
+
return () => {
|
|
130
|
+
cancelAnimationFrame(frame);
|
|
131
|
+
observer.disconnect();
|
|
132
|
+
};
|
|
133
|
+
}, [controller]);
|
|
134
|
+
return (_jsxs("div", { ref: setContent, role: role ?? 'log', "aria-relevant": ariaRelevant ?? 'additions', ...props, children: [children, _jsx("div", { ref: setSpacer, "aria-hidden": "true", "data-message-scroller-spacer": "", hidden: true, className: spacerClassName })] }));
|
|
135
|
+
}
|
|
136
|
+
function MessageScrollerItem({ messageId, ref, scrollAnchor = false, ...props }) {
|
|
137
|
+
const registerMessage = React.useContext(RegisterMessageContext);
|
|
138
|
+
if (!registerMessage) {
|
|
139
|
+
throw new Error('MessageScrollerItem must be used within a MessageScroller.');
|
|
140
|
+
}
|
|
141
|
+
const elementRef = React.useRef(null);
|
|
142
|
+
const setElement = React.useCallback((element) => {
|
|
143
|
+
const previous = elementRef.current;
|
|
144
|
+
elementRef.current = element;
|
|
145
|
+
if (messageId)
|
|
146
|
+
registerMessage(messageId, element, previous);
|
|
147
|
+
if (typeof ref === 'function')
|
|
148
|
+
ref(element);
|
|
149
|
+
else if (ref)
|
|
150
|
+
ref.current = element;
|
|
151
|
+
}, [messageId, ref, registerMessage]);
|
|
152
|
+
return (_jsx("div", { ref: setElement, "data-message-id": messageId, "data-scroll-anchor": scrollAnchor ? 'true' : 'false', ...props }));
|
|
153
|
+
}
|
|
154
|
+
function MessageScrollerButton({ behavior = 'smooth', children, className, direction = 'end', onClick, render, tabIndex, type = 'button', ...props }) {
|
|
155
|
+
const controller = useController();
|
|
156
|
+
const store = controller.scrollableStore;
|
|
157
|
+
const subscribe = React.useCallback((listener) => store.subscribe(listener), [store]);
|
|
158
|
+
const getActive = React.useCallback(() => {
|
|
159
|
+
const snapshot = store.getSnapshot();
|
|
160
|
+
return direction === 'start' ? snapshot.start : snapshot.end;
|
|
161
|
+
}, [direction, store]);
|
|
162
|
+
const active = React.useSyncExternalStore(subscribe, getActive, getActive);
|
|
163
|
+
const handleClick = (event) => {
|
|
164
|
+
if (!active)
|
|
165
|
+
return;
|
|
166
|
+
onClick?.(event);
|
|
167
|
+
if (event.defaultPrevented)
|
|
168
|
+
return;
|
|
169
|
+
event.currentTarget.blur();
|
|
170
|
+
if (direction === 'start')
|
|
171
|
+
controller.scrollToStart({ behavior });
|
|
172
|
+
else
|
|
173
|
+
controller.scrollToEnd({ behavior });
|
|
174
|
+
};
|
|
175
|
+
const mergedProps = {
|
|
176
|
+
type,
|
|
177
|
+
className,
|
|
178
|
+
inert: active ? undefined : true,
|
|
179
|
+
tabIndex: active ? tabIndex : -1,
|
|
180
|
+
'data-active': active ? 'true' : 'false',
|
|
181
|
+
onClick: handleClick,
|
|
182
|
+
children: children ?? _jsxs("span", { children: ["Scroll to ", direction] }),
|
|
183
|
+
...props,
|
|
184
|
+
};
|
|
185
|
+
const state = { active, direction };
|
|
186
|
+
if (!render)
|
|
187
|
+
return React.createElement('button', mergedProps);
|
|
188
|
+
if (typeof render === 'function')
|
|
189
|
+
return render(mergedProps, state);
|
|
190
|
+
if (!React.isValidElement(render))
|
|
191
|
+
return null;
|
|
192
|
+
const renderProps = render.props;
|
|
193
|
+
return React.cloneElement(render, {
|
|
194
|
+
...mergedProps,
|
|
195
|
+
className: [renderProps.className, className].filter(Boolean).join(' '),
|
|
196
|
+
children,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
function useMessageScroller() {
|
|
200
|
+
const controller = useController();
|
|
201
|
+
return React.useMemo(() => ({
|
|
202
|
+
scrollToEnd: controller.scrollToEnd,
|
|
203
|
+
scrollToMessage: controller.scrollToMessage,
|
|
204
|
+
scrollToStart: controller.scrollToStart,
|
|
205
|
+
}), [controller]);
|
|
206
|
+
}
|
|
207
|
+
function useMessageScrollerScrollable() {
|
|
208
|
+
const store = useController().scrollableStore;
|
|
209
|
+
const subscribe = React.useCallback((listener) => store.subscribe(listener), [store]);
|
|
210
|
+
return React.useSyncExternalStore(subscribe, store.getSnapshot, store.getSnapshot);
|
|
211
|
+
}
|
|
212
|
+
function useMessageScrollerVisibility() {
|
|
213
|
+
const controller = useController();
|
|
214
|
+
const store = controller.visibilityStore;
|
|
215
|
+
const subscribe = React.useCallback((listener) => store.subscribe(listener, () => controller.observeVisibility(), () => controller.unobserveVisibility()), [controller, store]);
|
|
216
|
+
return React.useSyncExternalStore(subscribe, store.getSnapshot, store.getSnapshot);
|
|
217
|
+
}
|
|
218
|
+
const MessageScroller = {
|
|
219
|
+
Provider: MessageScrollerProvider,
|
|
220
|
+
Root: MessageScrollerRoot,
|
|
221
|
+
Viewport: MessageScrollerViewport,
|
|
222
|
+
Content: MessageScrollerContent,
|
|
223
|
+
Item: MessageScrollerItem,
|
|
224
|
+
Button: MessageScrollerButton,
|
|
225
|
+
};
|
|
226
|
+
export { MessageScroller, useMessageScroller, useMessageScrollerScrollable, useMessageScrollerVisibility, };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { MessageScroller as MessageScrollerPrimitive, useMessageScroller, useMessageScrollerScrollable, useMessageScrollerVisibility } from '@shadcn/react/message-scroller';
|
|
2
1
|
import * as React from 'react';
|
|
3
2
|
import { Button } from './button';
|
|
3
|
+
import { MessageScroller as MessageScrollerPrimitive, useMessageScroller, useMessageScrollerScrollable, useMessageScrollerVisibility } from './message-scroller-primitive';
|
|
4
4
|
declare function MessageScrollerProvider(props: React.ComponentProps<typeof MessageScrollerPrimitive.Provider>): import("react/jsx-runtime").JSX.Element;
|
|
5
5
|
declare function MessageScroller({ className, ...props }: React.ComponentProps<typeof MessageScrollerPrimitive.Root>): import("react/jsx-runtime").JSX.Element;
|
|
6
|
-
declare function MessageScrollerViewport({ className,
|
|
6
|
+
declare function MessageScrollerViewport({ className, ...props }: React.ComponentProps<typeof MessageScrollerPrimitive.Viewport>): import("react/jsx-runtime").JSX.Element;
|
|
7
7
|
declare function MessageScrollerContent({ className, ...props }: React.ComponentProps<typeof MessageScrollerPrimitive.Content>): import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
declare function MessageScrollerItem({ className, scrollAnchor, ...props }: React.ComponentProps<typeof MessageScrollerPrimitive.Item>): import("react/jsx-runtime").JSX.Element;
|
|
9
9
|
declare function MessageScrollerButton({ direction, className, children, render, variant, size, ...props }: React.ComponentProps<typeof MessageScrollerPrimitive.Button> & Pick<React.ComponentProps<typeof Button>, 'variant' | 'size'>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-scroller.d.ts","sourceRoot":"","sources":["../../../src/components/ui/message-scroller.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"message-scroller.d.ts","sourceRoot":"","sources":["../../../src/components/ui/message-scroller.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EACL,eAAe,IAAI,wBAAwB,EAC3C,kBAAkB,EAClB,4BAA4B,EAC5B,4BAA4B,EAC7B,MAAM,8BAA8B,CAAA;AAErC,iBAAS,uBAAuB,CAC9B,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,wBAAwB,CAAC,QAAQ,CAAC,2CAGtE;AAED,iBAAS,eAAe,CAAC,EACvB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,wBAAwB,CAAC,IAAI,CAAC,2CAW5D;AAED,iBAAS,uBAAuB,CAAC,EAC/B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,wBAAwB,CAAC,QAAQ,CAAC,2CAWhE;AAED,iBAAS,sBAAsB,CAAC,EAC9B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,wBAAwB,CAAC,OAAO,CAAC,2CAQ/D;AAED,iBAAS,mBAAmB,CAAC,EAC3B,SAAS,EACT,YAAoB,EACpB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,wBAAwB,CAAC,IAAI,CAAC,2CAY5D;AAED,iBAAS,qBAAqB,CAAC,EAC7B,SAAiB,EACjB,SAAS,EACT,QAAQ,EACR,MAAM,EACN,OAAqB,EACrB,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,wBAAwB,CAAC,MAAM,CAAC,GAC7D,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC,2CAyB9D;AAED,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,EAClB,4BAA4B,EAC5B,4BAA4B,GAC7B,CAAA"}
|
|
@@ -1,29 +1,17 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { MessageScroller as MessageScrollerPrimitive, useMessageScroller, useMessageScrollerScrollable, useMessageScrollerVisibility, } from '@shadcn/react/message-scroller';
|
|
4
3
|
import { ArrowDown } from 'lucide-react';
|
|
5
|
-
import * as React from 'react';
|
|
6
4
|
import { cn } from '../../lib/utils';
|
|
7
5
|
import { Button } from './button';
|
|
8
|
-
import {
|
|
6
|
+
import { MessageScroller as MessageScrollerPrimitive, useMessageScroller, useMessageScrollerScrollable, useMessageScrollerVisibility, } from './message-scroller-primitive';
|
|
9
7
|
function MessageScrollerProvider(props) {
|
|
10
8
|
return _jsx(MessageScrollerPrimitive.Provider, { ...props });
|
|
11
9
|
}
|
|
12
10
|
function MessageScroller({ className, ...props }) {
|
|
13
11
|
return (_jsx(MessageScrollerPrimitive.Root, { "data-slot": "message-scroller", className: cn('group/message-scroller relative flex size-full min-h-0 flex-col overflow-hidden', className), ...props }));
|
|
14
12
|
}
|
|
15
|
-
function MessageScrollerViewport({ className,
|
|
16
|
-
|
|
17
|
-
const { scrollToEnd } = useMessageScroller();
|
|
18
|
-
useMessageScrollerLayoutStability(viewportRef, scrollToEnd);
|
|
19
|
-
const setViewportRef = React.useCallback((element) => {
|
|
20
|
-
viewportRef.current = element;
|
|
21
|
-
if (typeof ref === 'function')
|
|
22
|
-
ref(element);
|
|
23
|
-
else if (ref)
|
|
24
|
-
ref.current = element;
|
|
25
|
-
}, [ref]);
|
|
26
|
-
return (_jsx(MessageScrollerPrimitive.Viewport, { ref: setViewportRef, "data-slot": "message-scroller-viewport", className: cn('message-scroller-viewport size-full min-h-0 min-w-0 overflow-y-auto overscroll-contain [contain:content] [scrollbar-gutter:stable]', className), ...props }));
|
|
13
|
+
function MessageScrollerViewport({ className, ...props }) {
|
|
14
|
+
return (_jsx(MessageScrollerPrimitive.Viewport, { "data-slot": "message-scroller-viewport", className: cn('message-scroller-viewport size-full min-h-0 min-w-0 overflow-y-auto overscroll-contain [contain:content] [scrollbar-gutter:stable]', className), ...props }));
|
|
27
15
|
}
|
|
28
16
|
function MessageScrollerContent({ className, ...props }) {
|
|
29
17
|
return (_jsx(MessageScrollerPrimitive.Content, { "data-slot": "message-scroller-content", className: cn('flex h-max min-h-full flex-col', className), ...props }));
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ButtonGroup } from './button-group';
|
|
3
|
+
import { type IconButtonProps, type IconButtonSize } from './icon-button';
|
|
4
|
+
export interface NavigationButtonGroupProps extends Omit<React.ComponentProps<typeof ButtonGroup>, 'children' | 'orientation'> {
|
|
5
|
+
canGoBack?: boolean;
|
|
6
|
+
canGoForward?: boolean;
|
|
7
|
+
onBack: () => void;
|
|
8
|
+
onForward: () => void;
|
|
9
|
+
backLabel?: string;
|
|
10
|
+
forwardLabel?: string;
|
|
11
|
+
size?: IconButtonSize;
|
|
12
|
+
variant?: Extract<IconButtonProps['variant'], 'ghost' | 'outline'>;
|
|
13
|
+
}
|
|
14
|
+
declare function NavigationButtonGroup({ canGoBack, canGoForward, onBack, onForward, backLabel, forwardLabel, size, variant, 'aria-label': ariaLabel, ...props }: NavigationButtonGroupProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export { NavigationButtonGroup };
|
|
16
|
+
//# sourceMappingURL=navigation-button-group.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigation-button-group.d.ts","sourceRoot":"","sources":["../../../src/components/ui/navigation-button-group.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,eAAe,CAAA;AAEtB,MAAM,WAAW,0BACf,SAAQ,IAAI,CACV,KAAK,CAAC,cAAc,CAAC,OAAO,WAAW,CAAC,EACxC,UAAU,GAAG,aAAa,CAC3B;IACD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,IAAI,CAAC,EAAE,cAAc,CAAA;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC,CAAA;CACnE;AAED,iBAAS,qBAAqB,CAAC,EAC7B,SAAgB,EAChB,YAAmB,EACnB,MAAM,EACN,SAAS,EACT,SAAqB,EACrB,YAA2B,EAC3B,IAAW,EACX,OAAiB,EACjB,YAAY,EAAE,SAAwB,EACtC,GAAG,KAAK,EACT,EAAE,0BAA0B,2CAyB5B;AAED,OAAO,EAAE,qBAAqB,EAAE,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-react';
|
|
4
|
+
import { ButtonGroup } from './button-group';
|
|
5
|
+
import { IconButton, } from './icon-button';
|
|
6
|
+
function NavigationButtonGroup({ canGoBack = true, canGoForward = true, onBack, onForward, backLabel = 'Go back', forwardLabel = 'Go forward', size = 'sm', variant = 'ghost', 'aria-label': ariaLabel = 'Navigation', ...props }) {
|
|
7
|
+
return (_jsxs(ButtonGroup, { "aria-label": ariaLabel, orientation: "horizontal", ...props, children: [_jsx(IconButton, { label: backLabel, tooltip: true, size: size, variant: variant, disabled: !canGoBack, onClick: onBack, children: _jsx(ChevronLeftIcon, {}) }), _jsx(IconButton, { label: forwardLabel, tooltip: true, size: size, variant: variant, disabled: !canGoForward, onClick: onForward, children: _jsx(ChevronRightIcon, {}) })] }));
|
|
8
|
+
}
|
|
9
|
+
export { NavigationButtonGroup };
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
+
import { type MaterialVariant } from './material';
|
|
3
4
|
declare function Popover({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root>): import("react/jsx-runtime").JSX.Element;
|
|
4
5
|
declare function PopoverTrigger({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>): import("react/jsx-runtime").JSX.Element;
|
|
5
|
-
declare function
|
|
6
|
+
declare function PopoverPortal({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Portal>): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
declare function PopoverClose({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Close>): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
declare function PopoverContent({ className, align, material, sideOffset, size, ...props }: React.ComponentProps<typeof PopoverPrimitive.Content> & {
|
|
9
|
+
material?: MaterialVariant;
|
|
10
|
+
size?: 'content' | 'sm' | 'default' | 'lg';
|
|
11
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
6
12
|
declare function PopoverAnchor({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Anchor>): import("react/jsx-runtime").JSX.Element;
|
|
7
13
|
declare function PopoverHeader({ className, ...props }: React.ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
|
|
8
14
|
declare function PopoverTitle({ className, ...props }: React.ComponentProps<'h2'>): import("react/jsx-runtime").JSX.Element;
|
|
9
15
|
declare function PopoverDescription({ className, ...props }: React.ComponentProps<'p'>): import("react/jsx-runtime").JSX.Element;
|
|
10
|
-
|
|
16
|
+
declare function PopoverArrow({ className, ...props }: React.ComponentProps<typeof PopoverPrimitive.Arrow>): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export { Popover, PopoverAnchor, PopoverArrow, PopoverClose, PopoverContent, PopoverHeader, PopoverDescription, PopoverPortal, PopoverTitle, PopoverTrigger, };
|
|
11
18
|
//# sourceMappingURL=popover.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"popover.d.ts","sourceRoot":"","sources":["../../../src/components/ui/popover.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,gBAAgB,MAAM,yBAAyB,CAAA;AAC3D,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"popover.d.ts","sourceRoot":"","sources":["../../../src/components/ui/popover.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,gBAAgB,MAAM,yBAAyB,CAAA;AAC3D,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAY,KAAK,eAAe,EAAE,MAAM,YAAY,CAAA;AAE3D,iBAAS,OAAO,CAAC,EACf,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,IAAI,CAAC,2CAEpD;AAED,iBAAS,cAAc,CAAC,EACtB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,OAAO,CAAC,2CAEvD;AAED,iBAAS,aAAa,CAAC,EACrB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,MAAM,CAAC,2CAEtD;AAED,iBAAS,YAAY,CAAC,EACpB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,KAAK,CAAC,2CAErD;AAED,iBAAS,cAAc,CAAC,EACtB,SAAS,EACT,KAAgB,EAChB,QAAoB,EACpB,UAAc,EACd,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,OAAO,CAAC,GAAG;IACzD,QAAQ,CAAC,EAAE,eAAe,CAAA;IAC1B,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAAA;CAC3C,2CAsBA;AAED,iBAAS,aAAa,CAAC,EACrB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,MAAM,CAAC,2CAEtD;AAED,iBAAS,aAAa,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,2CAQ1E;AAED,iBAAS,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,2CAQxE;AAED,iBAAS,kBAAkB,CAAC,EAC1B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,2CAQ3B;AAED,iBAAS,YAAY,CAAC,EACpB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,KAAK,CAAC,2CAQrD;AAED,OAAO,EACL,OAAO,EACP,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,cAAc,GACf,CAAA"}
|