Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keep AI Assistant button visible when panel open, in active state #1106

Merged
merged 1 commit into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ export default class ResizablePanelGroup extends Component<Signature> {
(panelContext) => panelContext.lengthPx,
);

this.panelRatios = [];
for (let index = 0; index < panelLengths.length; index++) {
let panelLength = panelLengths[index];
if (panelLength == undefined) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { registerDestructor } from '@ember/destroyable';
import { action } from '@ember/object';
import { scheduleOnce } from '@ember/runloop';
import { htmlSafe } from '@ember/template';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { modifier } from 'ember-modifier';
import createRef from 'ember-ref-bucket/modifiers/create-ref';

import cssVars from '../../helpers/css-var.ts';
Expand All @@ -22,6 +24,7 @@ interface Signature {
Args: {
collapsible?: boolean; //default true
defaultLengthFraction: number;
isHidden?: boolean; //default false
isLastPanel: (panelId: number) => boolean;
lengthPx?: number;
minLengthPx?: number;
Expand All @@ -43,6 +46,16 @@ interface Signature {
Element: HTMLDivElement;
}

let managePanelRegistration = modifier(
(_element, [panel, isHidden]: [Panel, boolean | undefined]) => {
if (isHidden) {
scheduleOnce('afterRender', panel, panel.unregisterPanel);
} else {
scheduleOnce('afterRender', panel, panel.registerPanel);
}
},
);

export default class Panel extends Component<Signature> {
<template>
<div
Expand All @@ -60,6 +73,8 @@ export default class Panel extends Component<Signature> {
)
}}
{{createRef (@resizablePanelElId this.id) bucket=@panelGroupComponent}}
{{managePanelRegistration this @isHidden}}
...attributes
>
{{yield}}
</div>
Expand Down Expand Up @@ -90,16 +105,11 @@ export default class Panel extends Component<Signature> {

constructor(owner: any, args: any) {
super(owner, args);
scheduleOnce('afterRender', this, this.registerPanel);

registerDestructor(this, () => {
if (this.id) {
this.args.unregisterPanel(this.id);
}
});
registerDestructor(this, this.unregisterPanel);
}

private registerPanel() {
@action
registerPanel() {
if (this.id == undefined) {
this.id = this.args.registerPanel({
lengthPx: this.args.lengthPx,
Expand All @@ -110,6 +120,14 @@ export default class Panel extends Component<Signature> {
}
}

@action
unregisterPanel() {
if (this.id) {
this.args.unregisterPanel(this.id);
this.id = undefined;
}
}

get panelContext() {
if (this.id == undefined) {
return {
Expand All @@ -122,7 +140,9 @@ export default class Panel extends Component<Signature> {
}

get minLengthCssValue() {
if (this.panelContext?.minLengthPx !== undefined) {
if (this.args.isHidden) {
return htmlSafe('0px');
} else if (this.panelContext?.minLengthPx !== undefined) {
return htmlSafe(`${this.panelContext.minLengthPx}px`);
} else if (this.args.minLengthPx !== undefined) {
return htmlSafe(`${this.args.minLengthPx}px`);
Expand All @@ -133,7 +153,9 @@ export default class Panel extends Component<Signature> {
get lengthCssValue() {
let lengthPx = this.panelContext?.lengthPx;
let defaultLengthFraction = this.panelContext?.defaultLengthFraction;
if (lengthPx === -1 && defaultLengthFraction) {
if (this.args.isHidden) {
return htmlSafe('0px');
} else if (lengthPx === -1 && defaultLengthFraction) {
return htmlSafe(`${defaultLengthFraction * 100}%`);
} else if (lengthPx !== -1 && lengthPx !== undefined) {
return htmlSafe(`${lengthPx}px`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,40 +40,30 @@ export default class ResizablePanelUsage extends Component {
<:example>
<ResizablePanelGroup
@orientation='horizontal'
style={{cssVar
boxel-panel-resize-handler-height=this.boxelPanelResizeHandleHeight.value
boxel-panel-resize-handler-background-color=this.boxelPanelResizeHandleBackgroundColor.value
boxel-panel-resize-handler-hover-background-color=this.boxelPanelResizeHandleHoverBackgroundColor.value
}}
as |ResizablePanel ResizeHandle|
>
<ResizablePanel
@defaultLengthFraction={{this.horizontalPanel1DefaultWidthFraction}}
@minLengthPx={{this.horizontalPanel1MinWidthPx}}
style={{cssVar
boxel-panel-resize-handler-height=this.boxelPanelResizeHandleHeight.value
boxel-panel-resize-handler-background-color=this.boxelPanelResizeHandleBackgroundColor.value
boxel-panel-resize-handler-hover-background-color=this.boxelPanelResizeHandleHoverBackgroundColor.value
}}
>
Panel 1
</ResizablePanel>
<ResizeHandle />
<ResizablePanel
@defaultLengthFraction={{this.horizontalPanel2DefaultWidthFraction}}
@minLengthPx={{this.horizontalPanel2MinWidthPx}}
style={{cssVar
boxel-panel-resize-handler-height=this.boxelPanelResizeHandleHeight.value
boxel-panel-resize-handler-background-color=this.boxelPanelResizeHandleBackgroundColor.value
boxel-panel-resize-handler-hover-background-color=this.boxelPanelResizeHandleHoverBackgroundColor.value
}}
>
Panel 2
</ResizablePanel>
<ResizeHandle />
<ResizablePanel
@defaultLengthFraction={{this.horizontalPanel3DefaultWidthFraction}}
@minLengthPx={{this.horizontalPanel3MinWidthPx}}
style={{cssVar
boxel-panel-resize-handler-height=this.boxelPanelResizeHandleHeight.value
boxel-panel-resize-handler-background-color=this.boxelPanelResizeHandleBackgroundColor.value
boxel-panel-resize-handler-hover-background-color=this.boxelPanelResizeHandleHoverBackgroundColor.value
}}
>
Panel 3
</ResizablePanel>
Expand Down Expand Up @@ -158,28 +148,23 @@ export default class ResizablePanelUsage extends Component {
<ResizablePanelGroup
@orientation='vertical'
@reverseCollapse={{this.verticalReverseCollapse}}
style={{cssVar
boxel-panel-resize-handler-width=this.boxelPanelResizeHandleWidth.value
boxel-panel-resize-handler-background-color=this.boxelPanelResizeHandleBackgroundColor.value
boxel-panel-resize-handler-hover-background-color=this.boxelPanelResizeHandleHoverBackgroundColor.value
}}
as |ResizablePanel ResizeHandle|
>
<ResizablePanel
@defaultLengthFraction={{this.verticalPanel1DefaultHeightFraction}}
@minLengthPx={{this.verticalPanel1MinHeightPx}}
style={{cssVar
boxel-panel-resize-handler-width=this.boxelPanelResizeHandleWidth.value
boxel-panel-resize-handler-background-color=this.boxelPanelResizeHandleBackgroundColor.value
boxel-panel-resize-handler-hover-background-color=this.boxelPanelResizeHandleHoverBackgroundColor.value
}}
>
Panel 1
</ResizablePanel>
<ResizeHandle />
<ResizablePanel
@defaultLengthFraction={{this.verticalPanel2DefaultHeightFraction}}
@minLengthPx={{this.verticalPanel2MinHeightPx}}
style={{cssVar
boxel-panel-resize-handler-width=this.boxelPanelResizeHandleWidth.value
boxel-panel-resize-handler-background-color=this.boxelPanelResizeHandleBackgroundColor.value
boxel-panel-resize-handler-hover-background-color=this.boxelPanelResizeHandleHoverBackgroundColor.value
}}
>
Panel 2
</ResizablePanel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class RenderController {
@tracked containerStyle = '';
@tracked panel1LengthPx: number | undefined;
@tracked panel2LengthPx: number | undefined;
@tracked isPanel2Hidden = false;
panel1InnerContentStyle: string | undefined;
}

Expand Down Expand Up @@ -56,6 +57,7 @@ module('Integration | ResizablePanelGroup', function (hooks) {
@defaultLengthFraction={{0.4}}
@minLengthPx={{50}}
@lengthPx={{renderController.panel2LengthPx}}
@isHidden={{renderController.isPanel2Hidden}}
>
<div
class='panel-2-content'
Expand Down Expand Up @@ -210,4 +212,56 @@ module('Integration | ResizablePanelGroup', function (hooks) {
assert.hasNumericStyle('.panel-1-content', 'height', 360, 1);
assert.hasNumericStyle('.panel-2-content', 'height', 240, 1);
});

test('it excludes hidden panels from participating in layout', async function (this: MyTestContext, assert) {
this.renderController.isPanel2Hidden = true;
this.renderController.containerStyle =
'max-height: 100%; width: 200px; height: 218px;';
let { renderController } = this;

await render(<template>
{{! template-lint-disable no-inline-styles }}
<div id='test-container' style={{renderController.containerStyle}}>
<ResizablePanelGroup
@orientation='vertical'
@reverseCollapse={{true}}
as |ResizablePanel|
>
<ResizablePanel
@defaultLengthFraction={{0.6}}
@lengthPx={{renderController.panel1LengthPx}}
>
<div class='panel-1-content' style='height: 100%; overflow-y:auto'>
<div style={{renderController.panel1InnerContentStyle}}>
Panel 1
</div>
</div>
</ResizablePanel>
<ResizablePanel
@defaultLengthFraction={{0.4}}
@minLengthPx={{50}}
@lengthPx={{renderController.panel2LengthPx}}
@isHidden={{renderController.isPanel2Hidden}}
>
<div class='panel-2-content' style='height: 100%;'>
{{#unless renderController.isPanel2Hidden}}
Panel 2
{{/unless}}
</div>
</ResizablePanel>
</ResizablePanelGroup>
</div>
</template>);
await sleep(100); // let didResizeModifier run
assert.hasNumericStyle('.panel-1-content', 'height', 218, 1);
assert.hasNumericStyle('.panel-2-content', 'height', 0, 0);
this.renderController.isPanel2Hidden = false;
await sleep(100); // let didResizeModifier run
assert.hasNumericStyle('.panel-1-content', 'height', 156, 1);
assert.hasNumericStyle('.panel-2-content', 'height', 62, 1);
this.renderController.isPanel2Hidden = true;
await sleep(100); // let didResizeModifier run
assert.hasNumericStyle('.panel-1-content', 'height', 218, 1);
assert.hasNumericStyle('.panel-2-content', 'height', 0, 0);
});
});
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 27 additions & 3 deletions packages/host/app/components/ai-assistant/button.gts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import Component from '@glimmer/component';

import { cn } from '@cardstack/boxel-ui/helpers';

interface Signature {
Element: HTMLButtonElement;
Args: {
isActive: boolean;
};
}

export default class AiAssistantButton extends Component<Signature> {
<template>
<button
class='ai-assistant-button'
class={{cn 'ai-assistant-button' is-active=@isActive}}
data-test-open-ai-assistant
...attributes
/>
Expand All @@ -20,8 +25,8 @@ export default class AiAssistantButton extends Component<Signature> {
bottom: var(--boxel-sp);
right: var(--boxel-sp);
border-radius: var(--boxel-border-radius);
background-color: var(--boxel-ai-purple);
border: none;
background-color: var(--boxel-dark);
border: 1px solid rgba(255, 255, 255, 0.35);

background-image: image-set(
url('./ai-assist-icon.webp') 1x,
Expand All @@ -35,6 +40,25 @@ export default class AiAssistantButton extends Component<Signature> {
.ai-assistant-button:hover {
cursor: pointer;
}

.ai-assistant-button.is-active {
background-image: image-set(
url('./ai-assist-icon-bw.png') 1x,
url('./ai-assist-icon-bw@2x.png') 2x,
url('./ai-assist-icon-bw@3x.png')
),
image-set(
url('./ai-assist-button-active-bg.webp') 1x,
url('./ai-assist-button-active-bg@2x.webp') 2x,
url('./ai-assist-button-active-bg@3x.webp')
);
background-size:
26px 26px,
40px 40px;
background-position: center, center;
background-repeat: no-repeat, no-repeat;
border: 1px solid rgba(0, 0, 0, 0.35);
}
</style>
</template>
}
1 change: 1 addition & 0 deletions packages/host/app/components/ai-assistant/panel.gts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export default class AiAssistantPanel extends Component<Signature> {
grid-template-rows: auto 1fr;
background-color: var(--boxel-ai-purple);
border: none;
border-radius: 0;
color: var(--boxel-light);
height: 100%;
position: relative;
Expand Down
17 changes: 13 additions & 4 deletions packages/host/app/components/operator-mode/stack-item.gts
Original file line number Diff line number Diff line change
Expand Up @@ -319,10 +319,19 @@ export default class OperatorModeStackItem extends Component<Signature> {
});

private calculateLastSavedMsg() {
this.lastSavedMsg =
this.lastSaved != null
? `Saved ${formatDistanceToNow(this.lastSaved, { addSuffix: true })}`
: undefined;
// runs frequently, so only change a tracked property if the value has changed
if (this.lastSaved == null) {
if (this.lastSavedMsg) {
this.lastSavedMsg = undefined;
}
} else {
let savedMessage = `Saved ${formatDistanceToNow(this.lastSaved, {
addSuffix: true,
})}`;
if (this.lastSavedMsg != savedMessage) {
this.lastSavedMsg = savedMessage;
}
}
}

private doWithStableScroll = restartableTask(
Expand Down
Loading
Loading