Skip to content

Commit

Permalink
Webcomponents: Reactivity tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
andrey-sharapov-sc committed Feb 7, 2025
1 parent 23a478e commit a4094da
Show file tree
Hide file tree
Showing 12 changed files with 106 additions and 34 deletions.
24 changes: 22 additions & 2 deletions libraries/webcomponents-vue/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion libraries/webcomponents-vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"description": "Datagrok web components vue wrappers",
"dependencies": {
"@datagrok-libraries/compute-utils": "../compute-utils",
"@datagrok-libraries/dock-spawn-dg": "../webcomponents-vue",
"@datagrok-libraries/dock-spawn-dg": "../dock-spawn-dg",
"@datagrok-libraries/webcomponents": "../webcomponents",
"@vueuse/core": "^10.11.1",
"@vueuse/rxjs": "^11.0.3",
Expand Down
21 changes: 16 additions & 5 deletions libraries/webcomponents-vue/src/InputForm/InputForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export const InputForm = Vue.defineComponent({
type: Object as Vue.PropType<DG.FuncCall>,
required: true,
},
skipInit:{
type: Boolean,
default: true,
},
validationStates: {
type: Object as Vue.PropType<Record<string, ValidationResult>>,
},
Expand All @@ -41,14 +45,17 @@ export const InputForm = Vue.defineComponent({
},
emits: {
formReplaced: (a: DG.InputForm | undefined) => a,
inputChanged: (a: DG.EventData<DG.InputArgs>) => a,
validationChanged: (a: boolean) => a,
actionRequested: (actionUuid: string) => actionUuid,
consistencyReset: (ioName: string) => ioName,
},
setup(props, {emit}) {
const currentCall = Vue.computed(() => props.funcCall);
const currentCall = Vue.computed(() => Vue.markRaw(props.funcCall));
const validationStates = Vue.computed(() => props.validationStates);
const consistencyStates = Vue.computed(() => props.consistencyStates);
const isReadonly = Vue.computed(() => props.isReadonly);
const skipInit = Vue.computed(() => props.skipInit)

const states = Vue.reactive({
meta: {} as Record<string, any>,
Expand Down Expand Up @@ -106,9 +113,13 @@ export const InputForm = Vue.defineComponent({
currentForm.value = event.detail;
};

return () => <dg-input-form
funcCall={currentCall.value}
onFormReplaced={formReplacedCb}>
</dg-input-form>;
return () =>
<dg-input-form
funcCall={currentCall.value}
skipInit={skipInit.value}
onFormReplaced={formReplacedCb}
onInputChanged={(ev: DG.EventData<DG.InputArgs>) => emit('inputChanged', ev)}
onValidationChanged={(ev: boolean) => emit('validationChanged', ev)}>
</dg-input-form>;
},
});
2 changes: 1 addition & 1 deletion libraries/webcomponents-vue/src/RibbonMenu/RibbonMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const RibbonMenu = Vue.defineComponent({
setup(props, {slots}) {
const elements = Vue.reactive(new Map<number, HTMLElement>);

const currentView = Vue.shallowRef(props.view);
const currentView = Vue.computed(() => Vue.markRaw(props.view));

Vue.watch(elements, () => {
const elementsArray = [...elements.values()];
Expand Down
30 changes: 16 additions & 14 deletions libraries/webcomponents-vue/src/RibbonPanel/RibbonPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,32 @@ export const RibbonPanel = Vue.defineComponent({
}>,
setup(props, {slots}) {
const elements = Vue.reactive(new Map<number, HTMLElement>);

const currentView = Vue.shallowRef(props.view);
const currentView = Vue.computed(() => Vue.markRaw(props.view));

Vue.watch(elements, () => {
const elementsArray = [...elements.values()];
const filteredPanels = currentView.value
.getRibbonPanels()
.filter((panel) => !panel.some((ribbonItem) => elementsArray.includes(ribbonItem.children[0] as HTMLElement)));
currentView.value.setRibbonPanels(filteredPanels);

currentView.value.setRibbonPanels([
currentView.value.getRibbonPanels().flat(),
elementsArray,
]);

// Workaround for ui.comboPopup elements. It doesn't work if it is not a direct child of '.d4-ribbon-item'
elementsArray.forEach((elem) => {
elements.forEach((elem) => {
const content = ((elem.firstChild?.nodeType !== Node.TEXT_NODE) ? elem.firstChild: elem.firstChild.nextSibling) as HTMLElement | null;

if (content && content.tagName.toLowerCase().includes('dg-combo-popup')) {
content.classList.add('d4-ribbon-item');
elem.parentElement?.classList.remove('d4-ribbon-item');
}
});

const elementsArray = [...elements.values()];
const panels = currentView.value.getRibbonPanels();
const existingPanelIdx = panels.findIndex((panel) =>
panel.some((ribbonItem) => elementsArray.includes(ribbonItem.children[0] as HTMLElement)));

const panel = [...elements.values()];

if (existingPanelIdx >= 0)
panels.splice(existingPanelIdx, 1, panel);
else
panels.push(panel)

currentView.value.setRibbonPanels(panels);
});

const addElement = (el: Element | null | any, idx: number) => {
Expand Down
2 changes: 1 addition & 1 deletion libraries/webcomponents-vue/src/Viewer/Viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const Viewer = Vue.defineComponent({
viewerChanged: (v: DG.Viewer<any> | undefined) => v,
},
setup(props, {emit}) {
const currentDf = Vue.computed(() => props.dataFrame);
const currentDf = Vue.computed(() => props.dataFrame ? Vue.markRaw(props.dataFrame) : undefined);
const viewerChangedCb = (event: any) => {
emit('viewerChanged', event.detail);
};
Expand Down
30 changes: 29 additions & 1 deletion libraries/webcomponents/src/InputForm/InputForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,31 @@
import * as grok from 'datagrok-api/grok';
import * as ui from 'datagrok-api/ui';
import * as DG from 'datagrok-api/dg';
import {Subject} from 'rxjs';
import { distinctUntilChanged, map, mapTo, startWith, switchMap, takeUntil} from 'rxjs/operators';

export class InputForm extends HTMLElement {
private formInst?: DG.InputForm;
private skipDefaultInit = true;
private formChanges$ = new Subject<DG.InputForm>();

// TODO
private destroyed$ = new Subject<boolean>();

constructor() {
super();

this.formChanges$.pipe(
switchMap((form) => form.onInputChanged),
takeUntil(this.destroyed$)
).subscribe((ev) => this.dispatchEvent(new CustomEvent('input-changed', {detail: ev})));

this.formChanges$.pipe(
switchMap((form) => form.onValidationCompleted.pipe(mapTo(form), startWith(form))),
map((form) => form.isValid),
distinctUntilChanged(),
takeUntil(this.destroyed$)
).subscribe((ev) => this.dispatchEvent(new CustomEvent('validation-changed', {detail: ev})));
}

connectedCallback() {
Expand All @@ -25,13 +44,22 @@ export class InputForm extends HTMLElement {
this.replaceFunc(fc);
}

set skipInit(val: boolean) {
this.skipDefaultInit = val;
}

get skipInit() {
return this.skipDefaultInit;
}

private async replaceFunc(funcCall?: DG.FuncCall) {
if (!funcCall)
this.formInst = undefined;
else {
this.formInst = await DG.InputForm.forFuncCall(funcCall, {twoWayBinding: true, skipDefaultInit: true} as any);
this.formInst = await DG.InputForm.forFuncCall(funcCall, {twoWayBinding: true, skipDefaultInit: this.skipDefaultInit} as any);
this.appendChild(this.formInst.root);
}
this.formChanges$.next(this.formInst);
this.dispatchEvent(new CustomEvent('form-replaced', {detail: this.formInst}));
}
}
Expand Down
1 change: 1 addition & 0 deletions libraries/webcomponents/src/Viewer/Viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export class Viewer<T = any> extends HTMLElement {

private viewer$ = new BehaviorSubject<DG.Viewer<T> | undefined>(undefined);

// TODO
private destroyed$ = new Subject<boolean>();

constructor() {
Expand Down
2 changes: 1 addition & 1 deletion libraries/webcomponents/tsconfig.tsbuildinfo
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"root":["./index.ts"],"version":"5.7.2"}
{"root":["./index.ts"],"version":"5.7.3"}
1 change: 1 addition & 0 deletions packages/Compute2/src/apps/RFVApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const RFVApp = Vue.defineComponent({
onUpdate:funcCall={(chosenCall) => currentFuncCall.value = chosenCall}
onRunClicked={runFunc}
historyEnabled={true}
autorun={isRunningOnInput.value}
view={currentView.value}
/>
);
Expand Down
23 changes: 16 additions & 7 deletions packages/Compute2/src/components/RFV/RichFunctionView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,20 @@ export const RichFunctionView = Vue.defineComponent({
isReadonly: {
type: Boolean,
},
historyEnabled: {
autorun: {
type: Boolean,
default: false,
},
viewersHook: {
type: Function as Vue.PropType<ViewersHook>,
historyEnabled: {
type: Boolean,
default: false,
},
showStepNavigation: {
type: Boolean,
},
viewersHook: {
type: Function as Vue.PropType<ViewersHook>,
},
view: {
type: DG.ViewBase,
required: true,
Expand All @@ -167,7 +171,8 @@ export const RichFunctionView = Vue.defineComponent({
const {layoutDatabase} = useLayoutDb<ComputeSchema>(LAYOUT_DB_NAME, STORE_NAME);

const currentCall = Vue.computed(() => props.funcCall);
const currentView = Vue.shallowRef(props.view);
const currentView = Vue.computed(() => Vue.markRaw(props.view));
const currentUuid = Vue.computed(() => props.uuid);

const tabToPropertiesMap = Vue.computed(() => tabToProperties(currentCall.value.func));

Expand Down Expand Up @@ -381,8 +386,10 @@ export const RichFunctionView = Vue.defineComponent({
const isOutputOutdated = Vue.computed(() => props.callState?.isOutputOutdated);
const isRunning = Vue.computed(() => props.callState?.isRunning);
const isRunnable = Vue.computed(() => props.callState?.isRunnable);
const isReadonly = Vue.computed(() => props.isReadonly);

const validationState = Vue.computed(() => props.validationStates);
const consistencyState = Vue.computed(() => props.consistencyStates);

const currentFunc = Vue.computed(() => currentCall.value.func);

Expand Down Expand Up @@ -520,7 +527,7 @@ export const RichFunctionView = Vue.defineComponent({
/> }
</RibbonPanel>
<DockManager
key={props.uuid}
key={currentUuid.value}
onPanelClosed={handlePanelClose}
onInitFinished={handleDockInit}
ref={dockRef}
Expand Down Expand Up @@ -555,10 +562,12 @@ export const RichFunctionView = Vue.defineComponent({
funcCall={currentCall.value}
callMeta={callMeta.value}
validationStates={validationState.value}
consistencyStates={props.consistencyStates}
consistencyStates={consistencyState.value}
onActionRequested={(actionUuid) => emit('actionRequested', actionUuid)}
onConsistencyReset={(ioName) => emit('consistencyReset', ioName)}
isReadonly={props.isReadonly}
onInputChanged={(ev) => console.log(ev)}
onValidationChanged={(ev) => console.log(ev)}
isReadonly={isReadonly.value}
/>, [[ifOverlapping, isRunning.value, 'Recalculating...']])
}
<div class='flex sticky bottom-0 justify-end'>
Expand Down
2 changes: 1 addition & 1 deletion packages/Compute2/src/components/TreeWizard/TreeWizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export const TreeWizard = Vue.defineComponent({
chosenStepUuid.value = nextData.state.uuid;
};

const currentView = Vue.shallowRef(props.view);
const currentView = Vue.computed(() => Vue.markRaw(props.view));

const setViewName = (name: string = '') => {
if (props.view)
Expand Down

0 comments on commit a4094da

Please sign in to comment.