diff --git a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx index 4dffbae7253..c35202bd641 100644 --- a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx +++ b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx @@ -1,6 +1,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; import type HadronDocument from 'hadron-document'; -import { Element } from 'hadron-document'; +import { DocumentEvents, ElementEvents } from 'hadron-document'; +import type { Element } from 'hadron-document'; import { Button } from '../leafygreen'; import { css } from '@leafygreen-ui/emotion'; import { palette } from '@leafygreen-ui/palette'; @@ -165,34 +166,40 @@ function useHadronDocumentStatus( updateStatus('DeleteError', err, errorDetails); }; - doc.on(Element.Events.Added, onUpdate); - doc.on(Element.Events.Edited, onUpdate); - doc.on(Element.Events.Removed, onUpdate); - doc.on(Element.Events.Reverted, onUpdate); - doc.on(Element.Events.Invalid, onElementInvalid); - doc.on(Element.Events.Valid, onElementValid); - doc.on('update-start', onUpdateStart); - doc.on('update-blocked', onUpdateBlocked); - doc.on('update-success', onUpdateSuccess); - doc.on('update-error', onUpdateError); - doc.on('remove-start', onRemoveStart); - doc.on('remove-success', onRemoveSuccess); - doc.on('remove-error', onRemoveError); + const onEditingFinished = () => { + updateStatus('Initial'); + }; + + doc.on(ElementEvents.Added, onUpdate); + doc.on(ElementEvents.Edited, onUpdate); + doc.on(ElementEvents.Removed, onUpdate); + doc.on(ElementEvents.Reverted, onUpdate); + doc.on(ElementEvents.Invalid, onElementInvalid); + doc.on(ElementEvents.Valid, onElementValid); + doc.on(DocumentEvents.UpdateStarted, onUpdateStart); + doc.on(DocumentEvents.UpdateBlocked, onUpdateBlocked); + doc.on(DocumentEvents.UpdateSuccess, onUpdateSuccess); + doc.on(DocumentEvents.UpdateError, onUpdateError); + doc.on(DocumentEvents.RemoveStarted, onRemoveStart); + doc.on(DocumentEvents.RemoveSuccess, onRemoveSuccess); + doc.on(DocumentEvents.RemoveError, onRemoveError); + doc.on(DocumentEvents.EditingFinished, onEditingFinished); return () => { - doc.on(Element.Events.Added, onUpdate); - doc.off(Element.Events.Edited, onUpdate); - doc.off(Element.Events.Removed, onUpdate); - doc.off(Element.Events.Reverted, onUpdate); - doc.off(Element.Events.Invalid, onElementInvalid); - doc.off(Element.Events.Valid, onElementValid); - doc.off('update-start', onUpdateStart); - doc.off('update-blocked', onUpdateBlocked); - doc.off('update-success', onUpdateSuccess); - doc.off('update-error', onUpdateError); - doc.off('remove-start', onRemoveStart); - doc.off('remove-success', onRemoveSuccess); - doc.off('remove-error', onRemoveError); + doc.off(ElementEvents.Added, onUpdate); + doc.off(ElementEvents.Edited, onUpdate); + doc.off(ElementEvents.Removed, onUpdate); + doc.off(ElementEvents.Reverted, onUpdate); + doc.off(ElementEvents.Invalid, onElementInvalid); + doc.off(ElementEvents.Valid, onElementValid); + doc.off(DocumentEvents.UpdateStarted, onUpdateStart); + doc.off(DocumentEvents.UpdateBlocked, onUpdateBlocked); + doc.off(DocumentEvents.UpdateSuccess, onUpdateSuccess); + doc.off(DocumentEvents.UpdateError, onUpdateError); + doc.off(DocumentEvents.RemoveStarted, onRemoveStart); + doc.off(DocumentEvents.RemoveSuccess, onRemoveSuccess); + doc.off(DocumentEvents.RemoveError, onRemoveError); + doc.off(DocumentEvents.EditingFinished, onEditingFinished); }; }, [doc, updateStatus]); diff --git a/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx b/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx index 5aecf2391f7..673a3c8ecd1 100644 --- a/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx +++ b/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx @@ -7,7 +7,7 @@ import { import type Document from 'hadron-document'; import type { CellEditorProps } from './cell-editor'; import type { GridActions } from '../../stores/grid-store'; -import type { Element } from 'hadron-document'; +import { DocumentEvents, type Element } from 'hadron-document'; import type { BSONObject, CrudActions } from '../../stores/crud-store'; export type FullWidthCellRendererProps = Pick< @@ -51,16 +51,22 @@ class FullWidthCellRenderer extends React.Component< * Subscribe to the update store on mount. */ componentDidMount() { - this.doc.on('remove-success', this.handleRemoveSuccess); - this.doc.on('update-success', this.handleUpdateSuccess); + this.doc.on(DocumentEvents.RemoveSuccess, this.handleRemoveSuccess); + this.doc.on(DocumentEvents.UpdateSuccess, this.handleUpdateSuccess); } /** * Unsubscribe from the update store on unmount. */ componentWillUnmount() { - this.doc.removeListener('remove-success', this.handleRemoveSuccess); - this.doc.removeListener('update-success', this.handleUpdateSuccess); + this.doc.removeListener( + DocumentEvents.RemoveSuccess, + this.handleRemoveSuccess + ); + this.doc.removeListener( + DocumentEvents.UpdateSuccess, + this.handleUpdateSuccess + ); } /** diff --git a/packages/compass-crud/src/components/use-document-item-context-menu.spec.tsx b/packages/compass-crud/src/components/use-document-item-context-menu.spec.tsx index 9ea97f94761..d35b1f724ca 100644 --- a/packages/compass-crud/src/components/use-document-item-context-menu.spec.tsx +++ b/packages/compass-crud/src/components/use-document-item-context-menu.spec.tsx @@ -103,7 +103,7 @@ describe('useDocumentItemContextMenu', function () { userEvent.click(screen.getByTestId('test-container'), { button: 2 }); // Should show "Stop editing" when editing - expect(screen.getByText('Stop editing')).to.exist; + expect(screen.getByText('Cancel editing')).to.exist; expect(screen.queryByText('Edit document')).to.not.exist; // But show other operations expect(screen.getByText('Expand all fields')).to.exist; @@ -182,7 +182,7 @@ describe('useDocumentItemContextMenu', function () { // Should show "Edit document" when not editing expect(screen.getByText('Edit document')).to.exist; - expect(screen.queryByText('Stop editing')).to.not.exist; + expect(screen.queryByText('Cancel editing')).to.not.exist; // Click edit userEvent.click(screen.getByText('Edit document'), undefined, { @@ -207,11 +207,11 @@ describe('useDocumentItemContextMenu', function () { userEvent.click(screen.getByTestId('test-container'), { button: 2 }); // Should show "Stop editing" when editing - expect(screen.getByText('Stop editing')).to.exist; + expect(screen.getByText('Cancel editing')).to.exist; expect(screen.queryByText('Edit document')).to.not.exist; // Click stop editing - userEvent.click(screen.getByText('Stop editing'), undefined, { + userEvent.click(screen.getByText('Cancel editing'), undefined, { skipPointerEventsCheck: true, }); diff --git a/packages/compass-crud/src/components/use-document-item-context-menu.tsx b/packages/compass-crud/src/components/use-document-item-context-menu.tsx index d88d1a70076..67d9689ee1c 100644 --- a/packages/compass-crud/src/components/use-document-item-context-menu.tsx +++ b/packages/compass-crud/src/components/use-document-item-context-menu.tsx @@ -1,5 +1,5 @@ import type HadronDocument from 'hadron-document'; -import { useContextMenuItems } from '@mongodb-js/compass-components'; +import { useContextMenuGroups } from '@mongodb-js/compass-components'; import type { DocumentProps } from './document'; @@ -15,57 +15,61 @@ export function useDocumentItemContextMenu({ openInsertDocumentDialog, }: UseDocumentItemContextMenuProps) { const { expanded: isExpanded, editing: isEditing } = doc; - return useContextMenuItems( + return useContextMenuGroups( () => [ - { - label: isExpanded ? 'Collapse all fields' : 'Expand all fields', - onAction: () => { - if (isExpanded) { - doc.collapse(); - } else { - doc.expand(); - } - }, - }, - ...(isEditable - ? [ - { - label: isEditing ? 'Stop editing' : 'Edit document', - onAction: () => { - if (isEditing) { - doc.finishEditing(); - } else { - doc.startEditing(); - } + [ + ...(isEditable + ? [ + { + label: isEditing ? 'Cancel editing' : 'Edit document', + onAction: () => { + if (isEditing) { + doc.finishEditing(); + } else { + doc.startEditing(); + } + }, }, - }, - ] - : []), - { - label: 'Copy document', - onAction: () => { - copyToClipboard?.(doc); + ] + : []), + ], + [ + { + label: isExpanded ? 'Collapse all fields' : 'Expand all fields', + onAction: () => { + if (isExpanded) { + doc.collapse(); + } else { + doc.expand(); + } + }, + }, + { + label: 'Copy document', + onAction: () => { + copyToClipboard?.(doc); + }, }, - }, - ...(isEditable - ? [ - { - label: 'Clone document...', - onAction: () => { - const clonedDoc = doc.generateObject({ - excludeInternalFields: true, - }); - void openInsertDocumentDialog?.(clonedDoc, true); + ...(isEditable + ? [ + { + label: 'Clone document...', + onAction: () => { + const clonedDoc = doc.generateObject({ + excludeInternalFields: true, + }); + void openInsertDocumentDialog?.(clonedDoc, true); + }, }, - }, - { - label: 'Delete document', - onAction: () => { - doc.markForDeletion(); + { + label: 'Delete document', + onAction: () => { + doc.markForDeletion(); + }, }, - }, - ] - : []), + ] + : []), + ], ], [ doc, diff --git a/packages/compass-crud/src/stores/crud-store.spec.ts b/packages/compass-crud/src/stores/crud-store.spec.ts index 6a16a83cc8e..4768f398903 100644 --- a/packages/compass-crud/src/stores/crud-store.spec.ts +++ b/packages/compass-crud/src/stores/crud-store.spec.ts @@ -4,8 +4,13 @@ import { connect } from 'mongodb-data-service'; import AppRegistry, { createActivateHelpers, } from '@mongodb-js/compass-app-registry'; -import HadronDocument, { Element } from 'hadron-document'; +import HadronDocument, { + DocumentEvents, + Element, + type DocumentEventsType, +} from 'hadron-document'; import { MongoDBInstance } from 'mongodb-instance-model'; +import type { EventEmitter } from 'events'; import { once } from 'events'; import sinon from 'sinon'; import chai, { expect } from 'chai'; @@ -110,6 +115,15 @@ function waitForState(store, cb, timeout?: number) { return waitForStates(store, [cb], timeout); } +function onceDocumentEvent( + doc: HadronDocument, + event: DocumentEventsType +): Promise { + // The once function was not meant for strongly typed events, so we need to + // do some additional type casting. + return once(doc as unknown as EventEmitter, event as string); +} + const mockFieldStoreService = { updateFieldsFromDocuments() {}, updateFieldsFromSchema() {}, @@ -503,7 +517,7 @@ describe('store', function () { }); it('sets the error for the document', function (done) { - hadronDoc.on('remove-error', ({ message }) => { + hadronDoc.on(DocumentEvents.RemoveError, ({ message }) => { expect(message).to.equal('error happened'); done(); }); @@ -547,11 +561,11 @@ describe('store', function () { done(); }, store); - hadronDoc.on('update-blocked', () => { + hadronDoc.on(DocumentEvents.UpdateBlocked, () => { done(new Error("Didn't expect update to be blocked.")); }); - hadronDoc.on('update-error', (errorMessage) => { + hadronDoc.on(DocumentEvents.UpdateError, (errorMessage) => { done( new Error( `Didn't expect update to error. Errored with message: ${errorMessage}` @@ -586,11 +600,11 @@ describe('store', function () { setTimeout(() => done(), 100); }, store); - hadronDoc.on('update-blocked', () => { + hadronDoc.on(DocumentEvents.UpdateBlocked, () => { done(new Error("Didn't expect update to be blocked.")); }); - hadronDoc.on('update-error', (errorMessage) => { + hadronDoc.on(DocumentEvents.UpdateError, (errorMessage) => { done( new Error( `Didn't expect update to error. Errored with message: ${errorMessage}` @@ -613,7 +627,7 @@ describe('store', function () { }); it('sets the error for the document', function (done) { - hadronDoc.on('update-error', ({ message }) => { + hadronDoc.on(DocumentEvents.UpdateError, ({ message }) => { expect(message).to.equal( 'Unable to update, no changes have been made.' ); @@ -636,7 +650,7 @@ describe('store', function () { }); it('sets the error for the document', function (done) { - hadronDoc.on('update-error', ({ message }) => { + hadronDoc.on(DocumentEvents.UpdateError, ({ message }) => { expect(message).to.equal('error happened'); done(); }); @@ -655,7 +669,7 @@ describe('store', function () { }); it('sets the update blocked for the document', function (done) { - hadronDoc.on('update-blocked', () => { + hadronDoc.on(DocumentEvents.UpdateBlocked, () => { done(); }); @@ -728,7 +742,7 @@ describe('store', function () { const invalidHadronDoc = new HadronDocument(doc); (invalidHadronDoc as any).getId = null; - invalidHadronDoc.on('update-error', ({ message }) => { + invalidHadronDoc.on(DocumentEvents.UpdateError, ({ message }) => { expect(message).to.equal( 'An error occured when attempting to update the document: this.getId is not a function' ); @@ -765,7 +779,10 @@ describe('store', function () { }); it('rejects the update and emits update-error', async function () { - const updateErrorEvent = once(hadronDoc, 'update-error'); + const updateErrorEvent = onceDocumentEvent( + hadronDoc, + DocumentEvents.UpdateError + ); await store.updateDocument(hadronDoc); expect((await updateErrorEvent)[0]).to.match(/Update blocked/); @@ -998,7 +1015,7 @@ describe('store', function () { }); it('sets the error for the document', function (done) { - hadronDoc.on('update-error', ({ message }) => { + hadronDoc.on(DocumentEvents.UpdateError, ({ message }) => { expect(message).to.equal('error happened'); done(); }); @@ -1085,7 +1102,10 @@ describe('store', function () { }); it('rejects the update and emits update-error', async function () { - const updateErrorEvent = once(hadronDoc, 'update-error'); + const updateErrorEvent = onceDocumentEvent( + hadronDoc, + DocumentEvents.UpdateError + ); await store.replaceDocument(hadronDoc); expect((await updateErrorEvent)[0]).to.match(/Update blocked/); diff --git a/packages/hadron-document/src/document-events.ts b/packages/hadron-document/src/document-events.ts new file mode 100644 index 00000000000..4cd6ea8024e --- /dev/null +++ b/packages/hadron-document/src/document-events.ts @@ -0,0 +1,25 @@ +'use strict'; +/** + * The event constant. + */ + +export const DocumentEvents = { + Cancel: 'Document::Cancel', + Expanded: 'Document::Expanded', + Collapsed: 'Document::Collapsed', + VisibleElementsChanged: 'Document::VisibleElementsChanged', + EditingStarted: 'Document::EditingStarted', + EditingFinished: 'Document::EditingFinished', + MarkedForDeletion: 'Document::MarkedForDeletion', + DeletionFinished: 'Document::DeletionFinished', + UpdateStarted: 'Document::UpdateStarted', + UpdateSuccess: 'Document::UpdateSuccess', + UpdateBlocked: 'Document::UpdateBlocked', + UpdateError: 'Document::UpdateError', + RemoveStarted: 'Document::RemoveStarted', + RemoveSuccess: 'Document::RemoveSuccess', + RemoveError: 'Document::RemoveError', +} as const; + +export type DocumentEventsType = + typeof DocumentEvents[keyof typeof DocumentEvents]; diff --git a/packages/hadron-document/src/document.ts b/packages/hadron-document/src/document.ts index 50899a7c6fb..3c6afa84bf5 100644 --- a/packages/hadron-document/src/document.ts +++ b/packages/hadron-document/src/document.ts @@ -1,6 +1,6 @@ 'use strict'; -import type { Element } from './element'; -import { ElementList, Events as ElementEvents } from './element'; +import type { Element, ElementEventsType } from './element'; +import { ElementList } from './element'; import EventEmitter from 'eventemitter3'; import { EJSON, UUID } from 'bson'; import type { @@ -11,22 +11,9 @@ import ObjectGenerator from './object-generator'; import type { BSONArray, BSONObject, BSONValue } from './utils'; import { objectToIdiomaticEJSON } from './utils'; import type { HadronEJSONOptions } from './utils'; -import { DocumentEvents } from '.'; import type { Binary, MongoServerError } from 'mongodb'; - -/** - * The event constant. - */ -export const Events = { - Cancel: 'Document::Cancel', - Expanded: 'Document::Expanded', - Collapsed: 'Document::Collapsed', - VisibleElementsChanged: 'Document::VisibleElementsChanged', - EditingStarted: 'Document::EditingStarted', - EditingFinished: 'Document::EditingFinished', - MarkedForDeletion: 'Document::MarkedForDeletion', - DeletionFinished: 'Document::DeletionFinished', -}; +import { DocumentEvents, type DocumentEventsType } from './document-events'; +import { ElementEvents } from './element-events'; /** * The id field. @@ -38,7 +25,9 @@ export const DEFAULT_VISIBLE_ELEMENTS = 25; /** * Represents a document. */ -export class Document extends EventEmitter { +export class Document extends EventEmitter< + DocumentEventsType | ElementEventsType +> { uuid: string; doc: BSONObject; cloned: boolean; @@ -64,7 +53,7 @@ export class Document extends EventEmitter { for (const element of Array.from(this.elements)) { element.cancel(); } - this.emit(Events.Cancel); + this.emit(DocumentEvents.Cancel); } /** @@ -277,7 +266,7 @@ export class Document extends EventEmitter { insertBeginning(key: string | number, value: BSONValue): Element { const newElement = this.elements.insertBeginning(key, value); newElement._bubbleUp(ElementEvents.Added, newElement, this); - this.emit(Events.VisibleElementsChanged, this); + this.emit(DocumentEvents.VisibleElementsChanged, this); return newElement; } @@ -292,7 +281,7 @@ export class Document extends EventEmitter { insertEnd(key: string | number, value: BSONValue): Element { const newElement = this.elements.insertEnd(key, value); newElement._bubbleUp(ElementEvents.Added, newElement, this); - this.emit(Events.VisibleElementsChanged, this); + this.emit(DocumentEvents.VisibleElementsChanged, this); return newElement; } @@ -312,7 +301,7 @@ export class Document extends EventEmitter { ): Element | undefined { const newElement = this.elements.insertAfter(element, key, value); newElement?._bubbleUp(ElementEvents.Added, newElement, this); - this.emit(Events.VisibleElementsChanged, this); + this.emit(DocumentEvents.VisibleElementsChanged, this); return newElement; } @@ -369,8 +358,8 @@ export class Document extends EventEmitter { /** * @deprecated Use DocumentEvents import instead */ - static get Events(): typeof Events { - return Events; + static get Events(): typeof DocumentEvents { + return DocumentEvents; } /** @@ -416,8 +405,8 @@ export class Document extends EventEmitter { for (const element of this.elements) { element.expand(true); } - this.emit(Events.Expanded); - this.emit(Events.VisibleElementsChanged, this); + this.emit(DocumentEvents.Expanded); + this.emit(DocumentEvents.VisibleElementsChanged, this); } /** @@ -428,8 +417,8 @@ export class Document extends EventEmitter { for (const element of this.elements) { element.collapse(); } - this.emit(Events.Collapsed); - this.emit(Events.VisibleElementsChanged, this); + this.emit(DocumentEvents.Collapsed); + this.emit(DocumentEvents.VisibleElementsChanged, this); } getVisibleElements() { @@ -438,7 +427,7 @@ export class Document extends EventEmitter { setMaxVisibleElementsCount(newCount: number) { this.maxVisibleElementsCount = newCount; - this.emit(Events.VisibleElementsChanged, this); + this.emit(DocumentEvents.VisibleElementsChanged, this); } getTotalVisibleElementsCount() { @@ -490,20 +479,24 @@ export class Document extends EventEmitter { } onUpdateStart() { - this.emit('update-start'); + this.emit(DocumentEvents.UpdateStarted); } onUpdateSuccess(doc: Record) { - this.emit('update-success', doc); + this.emit(DocumentEvents.UpdateSuccess, doc); this.finishEditing(); } onUpdateBlocked() { - this.emit('update-blocked'); + this.emit(DocumentEvents.UpdateBlocked); } onUpdateError(error: Error) { - this.emit('update-error', error, (error as MongoServerError).errInfo); + this.emit( + DocumentEvents.UpdateError, + error, + (error as MongoServerError).errInfo + ); } markForDeletion() { @@ -521,21 +514,23 @@ export class Document extends EventEmitter { } onRemoveStart() { - this.emit('remove-start'); + this.emit(DocumentEvents.RemoveStarted); } onRemoveSuccess() { - this.emit('remove-success'); + this.emit(DocumentEvents.RemoveSuccess); this.finishDeletion(); } onRemoveError(error: Error) { - this.emit('remove-error', error, (error as MongoServerError).errInfo); + this.emit( + DocumentEvents.RemoveError, + error, + (error as MongoServerError).errInfo + ); } setModifiedEJSONString(ejson: string | null) { this.modifiedEJSONString = ejson; } } - -export default Document; diff --git a/packages/hadron-document/src/editor/date.ts b/packages/hadron-document/src/editor/date.ts index bb680c6757a..246faa15b27 100644 --- a/packages/hadron-document/src/editor/date.ts +++ b/packages/hadron-document/src/editor/date.ts @@ -1,9 +1,9 @@ import TypeChecker from 'hadron-type-checker'; -import Events from '../element-events'; +import { ElementEvents } from '../element-events'; import type { BSONValue } from '../utils'; import { fieldStringLen } from '../utils'; import StandardEditor from './standard'; -import type Element from '../element'; +import type { Element } from '../element'; /** * CRUD editor for date values. @@ -46,7 +46,7 @@ export default class DateEditor extends StandardEditor { } else { this.element.currentValue = value; this.element.setValid(); - this.element._bubbleUp(Events.Edited, this.element); + this.element._bubbleUp(ElementEvents.Edited, this.element); } } catch (e: any) { this.element.setInvalid(value, this.element.currentType, e.message); diff --git a/packages/hadron-document/src/editor/decimal128.ts b/packages/hadron-document/src/editor/decimal128.ts index 1194af9132f..1d895685853 100644 --- a/packages/hadron-document/src/editor/decimal128.ts +++ b/packages/hadron-document/src/editor/decimal128.ts @@ -1,7 +1,7 @@ import TypeChecker from 'hadron-type-checker'; -import Events from '../element-events'; +import { ElementEvents } from '../element-events'; import StandardEditor from './standard'; -import type Element from '../element'; +import type { Element } from '../element'; import type { BSONValue } from '../utils'; /** @@ -41,7 +41,7 @@ export default class Decimal128Editor extends StandardEditor { TypeChecker.cast(value, 'Decimal128'); this.element.currentValue = value; this.element.setValid(); - this.element._bubbleUp(Events.Edited, this.element); + this.element._bubbleUp(ElementEvents.Edited, this.element); } catch (error: any) { this.element.setInvalid(value, this.element.currentType, error.message); } diff --git a/packages/hadron-document/src/editor/double.ts b/packages/hadron-document/src/editor/double.ts index 217852c1ea7..a572d48051c 100644 --- a/packages/hadron-document/src/editor/double.ts +++ b/packages/hadron-document/src/editor/double.ts @@ -1,9 +1,9 @@ import TypeChecker from 'hadron-type-checker'; -import Events from '../element-events'; +import { ElementEvents } from '../element-events'; import type { BSONValue } from '../utils'; import { fieldStringLen } from '../utils'; import StandardEditor from './standard'; -import type Element from '../element'; +import type { Element } from '../element'; /** * CRUD editor for double values. @@ -47,7 +47,7 @@ export default class DoubleEditor extends StandardEditor { } else { this.element.currentValue = value; this.element.setValid(); - this.element._bubbleUp(Events.Edited, this.element); + this.element._bubbleUp(ElementEvents.Edited, this.element); } } catch (error: any) { this.element.setInvalid(value, this.element.currentType, error.message); diff --git a/packages/hadron-document/src/editor/index.ts b/packages/hadron-document/src/editor/index.ts index c67b15edb6f..673c971aa11 100644 --- a/packages/hadron-document/src/editor/index.ts +++ b/packages/hadron-document/src/editor/index.ts @@ -8,7 +8,7 @@ import DateEditor from './date'; import NullEditor from './null'; import UndefinedEditor from './undefined'; import ObjectIdEditor from './objectid'; -import type Element from '../element'; +import type { Element } from '../element'; const init = (element: Element) => ({ Standard: new StandardEditor(element), @@ -23,7 +23,7 @@ const init = (element: Element) => ({ ObjectId: new ObjectIdEditor(element), }); -export default Object.assign(init, { +export const ElementEditor = Object.assign(init, { DateEditor, StandardEditor, StringEditor, diff --git a/packages/hadron-document/src/editor/int32.ts b/packages/hadron-document/src/editor/int32.ts index 0c0eed08c71..100692acf46 100644 --- a/packages/hadron-document/src/editor/int32.ts +++ b/packages/hadron-document/src/editor/int32.ts @@ -1,6 +1,6 @@ import { fieldStringLen } from '../utils'; import StandardEditor from './standard'; -import type Element from '../element'; +import type { Element } from '../element'; /** * CRUD editor for int32 values. diff --git a/packages/hadron-document/src/editor/int64.ts b/packages/hadron-document/src/editor/int64.ts index c7f8be9d334..00b336344c1 100644 --- a/packages/hadron-document/src/editor/int64.ts +++ b/packages/hadron-document/src/editor/int64.ts @@ -1,7 +1,7 @@ import TypeChecker from 'hadron-type-checker'; -import Events from '../element-events'; +import { ElementEvents } from '../element-events'; import StandardEditor from './standard'; -import type Element from '../element'; +import type { Element } from '../element'; import type { BSONValue } from '../utils'; /** @@ -39,7 +39,7 @@ export default class Int64Editor extends StandardEditor { TypeChecker.cast(value, 'Int64'); this.element.currentValue = value; this.element.setValid(); - this.element._bubbleUp(Events.Edited, this.element); + this.element._bubbleUp(ElementEvents.Edited, this.element); } catch (error: any) { this.element.setInvalid(value, this.element.currentType, error.message); } diff --git a/packages/hadron-document/src/editor/null.ts b/packages/hadron-document/src/editor/null.ts index ab08457bf84..1504a24c174 100644 --- a/packages/hadron-document/src/editor/null.ts +++ b/packages/hadron-document/src/editor/null.ts @@ -1,5 +1,5 @@ import StandardEditor from './standard'; -import type Element from '../element'; +import type { Element } from '../element'; /** * Null is always 'null' diff --git a/packages/hadron-document/src/editor/objectid.ts b/packages/hadron-document/src/editor/objectid.ts index 22f71e92e08..a9eef1ed747 100644 --- a/packages/hadron-document/src/editor/objectid.ts +++ b/packages/hadron-document/src/editor/objectid.ts @@ -1,7 +1,7 @@ import TypeChecker from 'hadron-type-checker'; -import Events from '../element-events'; +import { ElementEvents } from '../element-events'; import StandardEditor from './standard'; -import type Element from '../element'; +import type { Element } from '../element'; import type { BSONValue } from '../utils'; /** @@ -40,7 +40,7 @@ export default class ObjectIdEditor extends StandardEditor { TypeChecker.cast(value, 'ObjectId'); this.element.currentValue = value; this.element.setValid(); - this.element._bubbleUp(Events.Edited, this.element); + this.element._bubbleUp(ElementEvents.Edited, this.element); } catch (e: any) { this.element.setInvalid(value, this.element.currentType, e.message); } diff --git a/packages/hadron-document/src/editor/standard.ts b/packages/hadron-document/src/editor/standard.ts index 27ce0a1d0c0..22a8fbf3c26 100644 --- a/packages/hadron-document/src/editor/standard.ts +++ b/packages/hadron-document/src/editor/standard.ts @@ -1,9 +1,9 @@ import type { TypeCastTypes } from 'hadron-type-checker'; import TypeChecker from 'hadron-type-checker'; -import Events from '../element-events'; +import { ElementEvents } from '../element-events'; import type { BSONValue } from '../utils'; import { fieldStringLen } from '../utils'; -import type Element from '../element'; +import type { Element } from '../element'; /** * Regex to match an array or object string. @@ -52,7 +52,7 @@ export default class StandardEditor { paste(value: string): void { if (ARRAY_OR_OBJECT.exec(value)) { this.edit(JSON.parse(value)); - this.element._bubbleUp(Events.Converted, this.element); + this.element._bubbleUp(ElementEvents.Converted, this.element); } else { this.edit(value); } diff --git a/packages/hadron-document/src/editor/string.ts b/packages/hadron-document/src/editor/string.ts index 32ad8689e9b..e4f2bbd0826 100644 --- a/packages/hadron-document/src/editor/string.ts +++ b/packages/hadron-document/src/editor/string.ts @@ -1,5 +1,5 @@ import StandardEditor from './standard'; -import type Element from '../element'; +import type { Element } from '../element'; export const STRING_TYPE = 'String'; diff --git a/packages/hadron-document/src/editor/undefined.ts b/packages/hadron-document/src/editor/undefined.ts index 92cb68ae1d4..5185afd9179 100644 --- a/packages/hadron-document/src/editor/undefined.ts +++ b/packages/hadron-document/src/editor/undefined.ts @@ -1,5 +1,5 @@ import StandardEditor from './standard'; -import type Element from '../element'; +import type { Element } from '../element'; /** * Undefined is always 'undefined' diff --git a/packages/hadron-document/src/element-events.ts b/packages/hadron-document/src/element-events.ts index 0bba0d3d342..2d3025a69db 100644 --- a/packages/hadron-document/src/element-events.ts +++ b/packages/hadron-document/src/element-events.ts @@ -1,7 +1,7 @@ /** * The event constant. */ -export default { +export const ElementEvents = { Added: 'Element::Added', Edited: 'Element::Edited', Removed: 'Element::Removed', @@ -13,3 +13,6 @@ export default { Collapsed: 'Element::Collapsed', VisibleElementsChanged: 'Element::VisibleElementsChanged', } as const; + +export type ElementEventsType = + typeof ElementEvents[keyof typeof ElementEvents]; diff --git a/packages/hadron-document/src/element.ts b/packages/hadron-document/src/element.ts index 17e2534f096..62e65ae1600 100644 --- a/packages/hadron-document/src/element.ts +++ b/packages/hadron-document/src/element.ts @@ -7,8 +7,8 @@ import ObjectGenerator from './object-generator'; import TypeChecker from 'hadron-type-checker'; import { UUID } from 'bson'; import DateEditor from './editor/date'; -import Events from './element-events'; -import type Document from './document'; +import { ElementEvents, type ElementEventsType } from './element-events'; +import type { Document } from './document'; import type { TypeCastTypes } from 'hadron-type-checker'; import type { Binary, ObjectId } from 'bson'; import type { @@ -18,10 +18,11 @@ import type { HadronEJSONOptions, } from './utils'; import { getDefaultValueForType, objectToIdiomaticEJSON } from './utils'; -import { DocumentEvents, ElementEvents } from '.'; +import { DocumentEvents, type DocumentEventsType } from './document-events'; export const DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss.SSS'; -export { Events }; + +export { ElementEvents, type ElementEventsType }; /** * Id field constant. @@ -196,7 +197,7 @@ export class Element extends EventEmitter { this.currentValue = value; } this.setValid(); - this._bubbleUp(Events.Edited, this); + this._bubbleUp(ElementEvents.Edited, this); } changeType(newType: TypeCastTypes): void { @@ -256,7 +257,7 @@ export class Element extends EventEmitter { */ rename(key: string | number): void { this.currentKey = key; - this._bubbleUp(Events.Edited, this); + this._bubbleUp(ElementEvents.Edited, this); } /** @@ -333,7 +334,7 @@ export class Element extends EventEmitter { throw new Error('Cannot insert values on non-array/non-object elements'); } const newElement = this.elements.insertAfter(element, key, value); - newElement!._bubbleUp(Events.Added, newElement, this); + newElement!._bubbleUp(ElementEvents.Added, newElement, this); this.emitVisibleElementsChanged(); return newElement!; } @@ -351,7 +352,7 @@ export class Element extends EventEmitter { throw new Error('Cannot insert values on non-array/non-object elements'); } const newElement = this.elements.insertEnd(key, value, true); - this._bubbleUp(Events.Added, newElement); + this._bubbleUp(ElementEvents.Added, newElement); this.emitVisibleElementsChanged(); return newElement; } @@ -416,7 +417,7 @@ export class Element extends EventEmitter { setValid(): void { this.currentTypeValid = true; this.invalidTypeMessage = undefined; - this._bubbleUp(Events.Valid, this); + this._bubbleUp(ElementEvents.Valid, this); } /** @@ -431,7 +432,7 @@ export class Element extends EventEmitter { this.currentType = newType; this.currentTypeValid = false; this.invalidTypeMessage = message; - this._bubbleUp(Events.Invalid, this); + this._bubbleUp(ElementEvents.Invalid, this); } /** @@ -728,7 +729,7 @@ export class Element extends EventEmitter { this.revert(); this.removed = true; if (this.parent) { - this._bubbleUp(Events.Removed, this, this.parent); + this._bubbleUp(ElementEvents.Removed, this, this.parent); this.emitVisibleElementsChanged(this.parent); } } @@ -739,7 +740,7 @@ export class Element extends EventEmitter { revert(): void { if (this.isAdded()) { this.parent?.elements?.remove(this); - this._bubbleUp(Events.Removed, this, this.parent); + this._bubbleUp(ElementEvents.Removed, this, this.parent); if (this.parent) { this.emitVisibleElementsChanged(this.parent); } @@ -761,7 +762,7 @@ export class Element extends EventEmitter { this.removed = false; } this.setValid(); - this._bubbleUp(Events.Reverted, this); + this._bubbleUp(ElementEvents.Reverted, this); } /** @@ -808,12 +809,12 @@ export class Element extends EventEmitter { * @param evt - The event. * @paramdata - Optional. */ - _bubbleUp(evt: typeof Events[keyof typeof Events], ...data: BSONArray): void { + _bubbleUp(evt: ElementEventsType, ...data: BSONArray): void { this.emit(evt, ...data); const element = this.parent; if (element) { if (element.isRoot()) { - element.emit(evt, ...data); + element.emit(evt as DocumentEventsType, ...data); } else { element._bubbleUp(evt, ...data); } @@ -934,7 +935,7 @@ export class Element extends EventEmitter { targetElement.emit(DocumentEvents.VisibleElementsChanged, targetElement); } else if (targetElement.expanded) { targetElement._bubbleUp( - Events.VisibleElementsChanged, + ElementEvents.VisibleElementsChanged, targetElement, targetElement.getRoot() ); @@ -1171,5 +1172,3 @@ export class ElementList implements Iterable { yield* this.elements; } } - -export default Element; diff --git a/packages/hadron-document/src/index.ts b/packages/hadron-document/src/index.ts index 08d3710f04f..8f58a7a0227 100644 --- a/packages/hadron-document/src/index.ts +++ b/packages/hadron-document/src/index.ts @@ -1,31 +1,22 @@ -import Document, { - Events as DocumentEvents, +import { Document } from './document'; +export default Document; +export { + Document, DEFAULT_VISIBLE_ELEMENTS as DEFAULT_VISIBLE_DOCUMENT_ELEMENTS, } from './document'; -import Element, { - Events as ElementEvents, +export { DocumentEvents, type DocumentEventsType } from './document-events'; + +export { + Element, isInternalFieldPath, DEFAULT_VISIBLE_ELEMENTS, } from './element'; -import ElementEditor from './editor'; -import type { Editor } from './editor'; -import { - getDefaultValueForType, - objectToIdiomaticEJSON, - type BSONValue, -} from './utils'; +export { ElementEvents, type ElementEventsType } from './element-events'; + +export { ElementEditor, type Editor } from './editor'; -export default Document; -export type { Editor, BSONValue }; export { - Document, - DocumentEvents, - DEFAULT_VISIBLE_DOCUMENT_ELEMENTS, - Element, - ElementEvents, - DEFAULT_VISIBLE_ELEMENTS, - ElementEditor, - isInternalFieldPath, getDefaultValueForType, objectToIdiomaticEJSON, -}; + type BSONValue, +} from './utils';