Skip to content

Commit

Permalink
feat(devexp): highlight components by group
Browse files Browse the repository at this point in the history
  • Loading branch information
matthieu-crouzet committed Jan 2, 2025
1 parent f4e354c commit 23305f5
Show file tree
Hide file tree
Showing 6 changed files with 508 additions and 0 deletions.
41 changes: 41 additions & 0 deletions packages/@o3r/components/src/devkit/components-devkit.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import type {
import type {
PlaceholderMode,
} from '../stores';
import {
GroupInfo,
} from './highlight/models';
import {
OtterLikeComponentInfo,
} from './inspector';
Expand All @@ -32,6 +35,40 @@ export interface ToggleInspectorMessage extends OtterMessageContent<'toggleInspe
isRunning: boolean;
}

/**
* Message to toggle the highlight
*/
export interface ToggleHighlightMessage extends OtterMessageContent<'toggleHighlight'> {
/** Is the highlight displayed */
isRunning: boolean;
}

/**
* Message the change the configuration of the `HighlightService`
*/
export interface ChangeHighlightConfiguration extends OtterMessageContent<'changeHighlightConfiguration'> {
/**
* Minimum width of HTMLElement to be considered
*/
elementMinWidth?: number;
/**
* Minimum height of HTMLElement to be considered
*/
elementMinHeight?: number;
/**
* Throttle interval
*/
throttleInterval?: number;
/**
* Group information to detect elements
*/
groupsInfo?: Record<string, GroupInfo>;
/**
* Maximum number of ancestors
*/
maxDepth?: number;
}

/**
* Message to toggle the placeholder mode
*/
Expand All @@ -51,6 +88,8 @@ type ComponentsMessageContents =
| IsComponentSelectionAvailableMessage
| SelectedComponentInfoMessage
| ToggleInspectorMessage
| ToggleHighlightMessage
| ChangeHighlightConfiguration
| PlaceholderModeMessage;

/** List of possible DataTypes for Components messages */
Expand All @@ -74,5 +113,7 @@ export const isComponentsMessage = (message: any): message is AvailableComponent
|| message.dataType === 'isComponentSelectionAvailable'
|| message.dataType === 'placeholderMode'
|| message.dataType === 'toggleInspector'
|| message.dataType === 'toggleHighlight'
|| message.dataType === 'changeHighlightConfiguration'
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ import {
OTTER_COMPONENTS_DEVTOOLS_DEFAULT_OPTIONS,
OTTER_COMPONENTS_DEVTOOLS_OPTIONS,
} from './components-devtools.token';
import {
HighlightService,
} from './highlight/highlight.service';
import {
OtterInspectorService,
OtterLikeComponentInfo,
Expand All @@ -51,6 +54,7 @@ import {
export class ComponentsDevtoolsMessageService implements DevtoolsServiceInterface {
private readonly options: ComponentsDevtoolsServiceOptions;
private readonly inspectorService: OtterInspectorService;
private readonly highlightService: HighlightService;
private readonly sendMessage = sendOtterMessage<AvailableComponentsMessageContents>;
private readonly destroyRef = inject(DestroyRef);

Expand All @@ -65,6 +69,8 @@ export class ComponentsDevtoolsMessageService implements DevtoolsServiceInterfac
};

this.inspectorService = new OtterInspectorService();
this.highlightService = new HighlightService();

Check warning on line 72 in packages/@o3r/components/src/devkit/components-devtools.message.service.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/components-devtools.message.service.ts#L72

Added line #L72 was not covered by tests

if (this.options.isActivatedOnBootstrap) {
this.activate();
}
Expand Down Expand Up @@ -130,6 +136,36 @@ export class ComponentsDevtoolsMessageService implements DevtoolsServiceInterfac
this.inspectorService.toggleInspector(message.isRunning);
break;
}
case 'toggleHighlight': {
if (message.isRunning) {
this.highlightService.start();

Check warning on line 141 in packages/@o3r/components/src/devkit/components-devtools.message.service.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/components-devtools.message.service.ts#L141

Added line #L141 was not covered by tests
} else {
this.highlightService.stop();

Check warning on line 143 in packages/@o3r/components/src/devkit/components-devtools.message.service.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/components-devtools.message.service.ts#L143

Added line #L143 was not covered by tests
}
break;

Check warning on line 145 in packages/@o3r/components/src/devkit/components-devtools.message.service.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/components-devtools.message.service.ts#L145

Added line #L145 was not covered by tests
}
case 'changeHighlightConfiguration': {
if (message.elementMinWidth) {
this.highlightService.elementMinWidth = message.elementMinWidth;

Check warning on line 149 in packages/@o3r/components/src/devkit/components-devtools.message.service.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/components-devtools.message.service.ts#L149

Added line #L149 was not covered by tests
}
if (message.elementMinHeight) {
this.highlightService.elementMinHeight = message.elementMinHeight;

Check warning on line 152 in packages/@o3r/components/src/devkit/components-devtools.message.service.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/components-devtools.message.service.ts#L152

Added line #L152 was not covered by tests
}
if (message.throttleInterval) {
this.highlightService.throttleInterval = message.throttleInterval;

Check warning on line 155 in packages/@o3r/components/src/devkit/components-devtools.message.service.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/components-devtools.message.service.ts#L155

Added line #L155 was not covered by tests
}
if (message.groupsInfo) {
this.highlightService.groupsInfo = message.groupsInfo;

Check warning on line 158 in packages/@o3r/components/src/devkit/components-devtools.message.service.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/components-devtools.message.service.ts#L158

Added line #L158 was not covered by tests
}
if (message.maxDepth) {
this.highlightService.maxDepth = message.maxDepth;

Check warning on line 161 in packages/@o3r/components/src/devkit/components-devtools.message.service.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/components-devtools.message.service.ts#L161

Added line #L161 was not covered by tests
}
if (this.highlightService.isRunning()) {
// Re-start to recompute the highlight with the new configuration
this.highlightService.start();

Check warning on line 165 in packages/@o3r/components/src/devkit/components-devtools.message.service.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/components-devtools.message.service.ts#L165

Added line #L165 was not covered by tests
}
break;

Check warning on line 167 in packages/@o3r/components/src/devkit/components-devtools.message.service.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/components-devtools.message.service.ts#L167

Added line #L167 was not covered by tests
}
case 'placeholderMode': {
this.store.dispatch(togglePlaceholderModeTemplate({ mode: message.mode }));
break;
Expand Down
32 changes: 32 additions & 0 deletions packages/@o3r/components/src/devkit/highlight/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Class applied on the wrapper of highlight elements
*/
export const HIGHLIGHT_WRAPPER_CLASS = 'highlight-wrapper';

/**
* Class applied on the overlay elements
*/
export const HIGHLIGHT_OVERLAY_CLASS = 'highlight-overlay';

/**
* Class applied on the chip elements
*/
export const HIGHLIGHT_CHIP_CLASS = 'highlight-chip';

/**
* Default value for maximum number of ancestors
*/
export const DEFAULT_MAX_DEPTH = 10;

/**
* Default value for element min height
*/
export const DEFAULT_ELEMENT_MIN_HEIGHT = 30;
/**
* Default value for element min width
*/
export const DEFAULT_ELEMENT_MIN_WIDTH = 60;
/**
* Default value for throttle interval
*/
export const DEFAULT_THROTTLE_INTERVAL = 500;
154 changes: 154 additions & 0 deletions packages/@o3r/components/src/devkit/highlight/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import {
HIGHLIGHT_CHIP_CLASS,
HIGHLIGHT_OVERLAY_CLASS,
HIGHLIGHT_WRAPPER_CLASS,
} from './constants';
import {
ElementWithGroupInfo,
} from './models';

/**
* Retrieve the identifier of the element
* @param element
*/
export function getIdentifier(element: ElementWithGroupInfo): string {
const { tagName, attributes, classList } = element.htmlElement;
const regexp = new RegExp(element.regexp, 'i');

Check warning on line 16 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L15-L16

Added lines #L15 - L16 were not covered by tests
if (!regexp.test(tagName)) {
const attribute = Array.from(attributes).find((att) => regexp.test(att.name));

Check warning on line 18 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L18

Added line #L18 was not covered by tests
if (attribute) {
return `${attribute.name}${attribute.value ? `="${attribute.value}"` : ''}`;
}
const className = Array.from(classList).find((cName) => regexp.test(cName));

Check warning on line 22 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L22

Added line #L22 was not covered by tests
if (className) {
return className;

Check warning on line 24 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L24

Added line #L24 was not covered by tests
}
}
return tagName;

Check warning on line 27 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L27

Added line #L27 was not covered by tests
}

/**
* Compute the number of ancestors of a given element based on a list of elements
* @param element
* @param elementList
*/
export function computeNumberOfAncestors(element: HTMLElement, elementList: HTMLElement[]) {
return elementList.filter((el: HTMLElement) => el.contains(element)).length;

Check warning on line 36 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L36

Added line #L36 was not covered by tests
}

/**
* Throttle {@link fn} with a {@link delay}
* @param fn method to run
* @param delay given in ms
*/
export function throttle<T extends (...args: any[]) => any>(fn: T, delay: number): (...args: Parameters<T>) => void {
let timerFlag: ReturnType<typeof setTimeout> | null = null;

Check warning on line 45 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L45

Added line #L45 was not covered by tests

const throttleFn = (...args: Parameters<T>) => {

Check warning on line 47 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L47

Added line #L47 was not covered by tests
if (timerFlag === null) {
fn(...args);
timerFlag = setTimeout(() => {
fn(...args);
timerFlag = null;

Check warning on line 52 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L49-L52

Added lines #L49 - L52 were not covered by tests
}, delay);
}
};
return throttleFn;

Check warning on line 56 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L56

Added line #L56 was not covered by tests
}

/**
* Run {@link refreshFn} if {@link mutations} implies to refresh elements inside {@link highlightWrapper}
* @param mutations
* @param highlightWrapper
* @param refreshFn
*/
export function runRefreshIfNeeded(mutations: MutationRecord[], highlightWrapper: Element | null, refreshFn: () => void) {
if (
mutations.some((mutation) =>

Check warning on line 67 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L67

Added line #L67 was not covered by tests
mutation.target !== highlightWrapper
|| (
mutation.target === document.body
&& Array.from<HTMLElement>(mutation.addedNodes.values() as any)
.concat(...mutation.removedNodes.values() as any)
.some((node) => !node.classList.contains(HIGHLIGHT_WRAPPER_CLASS))

Check warning on line 73 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L73

Added line #L73 was not covered by tests
)
)
) {
refreshFn();

Check warning on line 77 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L77

Added line #L77 was not covered by tests
}
}

/**
* Options to create an overlay element
*/
export interface CreateOverlayOptions {
top: string;
left: string;
position: string;
width: string;
height: string;
backgroundColor: string;
}

/**
* Create an overlay element
* @param doc HTML Document
* @param opts
*/
export function createOverlay(doc: Document, opts: CreateOverlayOptions) {
const overlay = doc.createElement('div');
overlay.classList.add(HIGHLIGHT_OVERLAY_CLASS);

Check warning on line 100 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L99-L100

Added lines #L99 - L100 were not covered by tests
// All static style could be moved in a <style>
overlay.style.top = opts.top;
overlay.style.left = opts.left;
overlay.style.width = opts.width;
overlay.style.height = opts.height;
overlay.style.border = `1px solid ${opts.backgroundColor}`;
overlay.style.zIndex = '10000';
overlay.style.position = opts.position;
overlay.style.pointerEvents = 'none';
return overlay;

Check warning on line 110 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L102-L110

Added lines #L102 - L110 were not covered by tests
}

/**
* Options to create a chip element
*/
export interface CreateChipOptions {
displayName: string;
depth: number;
top: string;
left: string;
position: string;
backgroundColor: string;
color?: string;
name: string;
}

/**
* Create a chip element
* @param doc HTML Document
* @param opts
*/
export function createChip(doc: Document, opts: CreateChipOptions) {
const chip = doc.createElement('div');
chip.classList.add(HIGHLIGHT_CHIP_CLASS);
chip.textContent = `${opts.displayName} ${opts.depth}`;

Check warning on line 135 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L133-L135

Added lines #L133 - L135 were not covered by tests
// All static style could be moved in a <style>
chip.style.top = opts.top;
chip.style.left = opts.left;
chip.style.backgroundColor = opts.backgroundColor;

Check warning on line 139 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L137-L139

Added lines #L137 - L139 were not covered by tests
chip.style.color = opts.color ?? '#FFF';
chip.style.position = opts.position;
chip.style.display = 'inline-block';
chip.style.padding = '2px 4px';
chip.style.borderRadius = '0 0 4px';
chip.style.cursor = 'pointer';
chip.style.zIndex = '10000';
chip.style.textWrap = 'no-wrap';
chip.title = opts.name;
chip.addEventListener('click', () => {

Check warning on line 149 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L141-L149

Added lines #L141 - L149 were not covered by tests
// Should we log in the console as well ?
void navigator.clipboard.writeText(opts.name);

Check warning on line 151 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L151

Added line #L151 was not covered by tests
});
return chip;

Check warning on line 153 in packages/@o3r/components/src/devkit/highlight/helpers.ts

View check run for this annotation

Codecov / codecov/patch

packages/@o3r/components/src/devkit/highlight/helpers.ts#L153

Added line #L153 was not covered by tests
}
Loading

0 comments on commit 23305f5

Please sign in to comment.