Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
7 changes: 7 additions & 0 deletions src/actions/mover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ export class Mover {

private moveIndicator?: MoveIndicatorBubble;

private movingBlock:
| (IDraggable & IFocusableNode & IBoundedElement & ISelectable)
| null = null;

constructor(protected navigation: Navigation) {}

/**
Expand Down Expand Up @@ -154,6 +158,7 @@ export class Mover {
workspace.setKeyboardMoveInProgress(true);
const info = new MoveInfo(workspace, draggable, dragger, blurListener);
this.moves.set(workspace, info);
this.movingBlock = draggable;
// Begin drag.
dragger.onDragStart(info.fakePointerEvent('pointerdown'));
info.updateTotalDelta();
Expand Down Expand Up @@ -217,6 +222,7 @@ export class Mover {
*/
finishMove(workspace: WorkspaceSvg) {
const info = this.preDragEndCleanup(workspace);
this.movingBlock = null;

info.dragger.onDragEnd(
info.fakePointerEvent('pointerup'),
Expand Down Expand Up @@ -250,6 +256,7 @@ export class Mover {
// Prevent the strategy connecting the block so we just delete one block.
// @ts-expect-error Access to private property connectionCandidate.
dragStrategy.connectionCandidate = null;
this.movingBlock = null;

info.dragger.onDragEnd(
info.fakePointerEvent('pointerup'),
Expand Down
108 changes: 108 additions & 0 deletions src/aria.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

const ARIA_PREFIX = 'aria-';
const ROLE_ATTRIBUTE = 'role';

// TODO: Finalize this.
export enum Role {
GRID = 'grid',
GRIDCELL = 'gridcell',
GROUP = 'group',
LISTBOX = 'listbox',
MENU = 'menu',
MENUITEM = 'menuitem',
MENUITEMCHECKBOX = 'menuitemcheckbox',
OPTION = 'option',
PRESENTATION = 'presentation',
ROW = 'row',
TREE = 'tree',
TREEITEM = 'treeitem',
SEPARATOR = 'separator',
STATUS = 'status',
REGION = 'region',
IMAGE = 'image',
FIGURE = 'figure',
BUTTON = 'button',
CHECKBOX = 'checkbox',
TEXTBOX = 'textbox',
APPLICATION = 'application',
}

// TODO: Finalize this.
export enum State {
ACTIVEDESCENDANT = 'activedescendant',
COLCOUNT = 'colcount',
DISABLED = 'disabled',
EXPANDED = 'expanded',
INVALID = 'invalid',
LABEL = 'label',
LABELLEDBY = 'labelledby',
LEVEL = 'level',
ORIENTATION = 'orientation',
POSINSET = 'posinset',
ROWCOUNT = 'rowcount',
SELECTED = 'selected',
SETSIZE = 'setsize',
VALUEMAX = 'valuemax',
VALUEMIN = 'valuemin',
LIVE = 'live',
HIDDEN = 'hidden',
ROLEDESCRIPTION = 'roledescription',
ATOMIC = 'atomic',
OWNS = 'owns',
}

let isMutatingAriaProperty = false;

export function setRole(element: Element, roleName: Role | null) {

Check warning on line 61 in src/aria.ts

View workflow job for this annotation

GitHub Actions / Eslint check

Missing JSDoc comment
isMutatingAriaProperty = true;
if (roleName) {
element.setAttribute(ROLE_ATTRIBUTE, roleName);
} else element.removeAttribute(ROLE_ATTRIBUTE);
isMutatingAriaProperty = false;
}

export function getRole(element: Element): Role | null {

Check warning on line 69 in src/aria.ts

View workflow job for this annotation

GitHub Actions / Eslint check

Missing JSDoc comment
// This is an unsafe cast which is why it needs to be checked to ensure that
// it references a valid role.
const currentRoleName = element.getAttribute(ROLE_ATTRIBUTE) as Role;
if (Object.values(Role).includes(currentRoleName)) {
return currentRoleName;
}
return null;
}

export function setState(

Check warning on line 79 in src/aria.ts

View workflow job for this annotation

GitHub Actions / Eslint check

Missing JSDoc comment
element: Element,
stateName: State,
value: string | boolean | number | string[],
) {
isMutatingAriaProperty = true;
if (Array.isArray(value)) {
value = value.join(' ');
}
const attrStateName = ARIA_PREFIX + stateName;
element.setAttribute(attrStateName, `${value}`);
isMutatingAriaProperty = false;
}

export function getState(element: Element, stateName: State): string | null {

Check warning on line 93 in src/aria.ts

View workflow job for this annotation

GitHub Actions / Eslint check

Missing JSDoc comment
const attrStateName = ARIA_PREFIX + stateName;
return element.getAttribute(attrStateName);
}

export function announceDynamicAriaState(text: string) {

Check warning on line 98 in src/aria.ts

View workflow job for this annotation

GitHub Actions / Eslint check

Missing JSDoc comment
const ariaAnnouncementSpan = document.getElementById('blocklyAriaAnnounce');
if (!ariaAnnouncementSpan) {
throw new Error('Expected element with id blocklyAriaAnnounce to exist.');
}
ariaAnnouncementSpan.innerHTML = text;
}

export function isCurrentlyMutatingAriaProperty(): boolean {

Check warning on line 106 in src/aria.ts

View workflow job for this annotation

GitHub Actions / Eslint check

Missing JSDoc comment
return isMutatingAriaProperty;
}
Loading
Loading