Skip to content

Commit

Permalink
Properties Panel (bbc/sofie-core #22)
Browse files Browse the repository at this point in the history
* wip: initial draft implementation of userEditPanel

* wip: userEditPanel grouping

* wip: userEditPanel cleanup remove schema as it's replaces by schemas

* wip: userEditPanel styling and use <select> for list

* wip: userEditPanel - make groupType optional to allow empty slots

* wip: userEditPanel - convert to FC

* wip: userEditPanel - uncomment context menu

* wip: UserEditPanel - Add DefaultUserOperationsTypes for revert buttons in panel

* wip: UserEditPanel - Default revert button on panel

* wip: UserEditPanel - tracker on part and segment

* wip: UserEditPanel - only render Segment or Part in panel

* wip: UserEditPanel - initial header for part and segment

* wip: UserEditPanel - comment for the usage of getTimePosition

* wip: UserEditPanel - add Icons in header

* wip: UserEditPanel - add button type in UserEditingDefinitions

* wip: UserEditPanel - rename revert button to "Revert Changes"

* wip: UserEditPanel - add support for SVG icons in grouping (e.g for split or DVE backgrounds)

* wip: UserEditPanel - center revert button

* wip: Renaming UserEditPanel to PropertiesPanel

* wip: UI selection - Context for SelectedElements

* wip: UI selection - refactor from useState() to useReducer()

* wip: UI selection - use type for SelectedElement

* wip: UI selection -Segment not updating upon selection

* feat: unit tests for SelectedElementsContext

* wip: UI selection - add isSelected to withSegmentTiming

* wip: fix props withSelection

* wip: fix props withSelection

* wip: remove componentDidUpdate - leftover from previous debugging

* fix: added listSelectedElements() instead of exposing the selectedElements

* fix: add assertNever() to selectionReducer

* fix: remove id and use ElementId as reference in context provider

* fix: clean up isSelected should parse boolean not function as props

* chore: update package with @testing-library/react

* fix: tests for SelecedElementsContext updated and type fixed

* fix: listSelectedElements is af function not an array

* wip: implement useSelection in PropertiesPanel

* wip: useSelection on Parts

* fix: missing rundown id in propertiespanel actions

* feat: add enabling of userediting in settings

* feat: doubleclick on part selection for properties panel

* fix: properties panel crash when no selection was made

* feat: segment selection

* feat: dbl click anywhere in segment header to select properties

* feat: select part by double clicking a piece

* feat: properties panel commit button for pending changes

* feat: select/deselect an element - fix flickering upen commit changes

* fix: render all userEditOperations when part is selected

* feat: move Properties panel out of notification center logic

* feat: use context.consumer instead of wrapping

* feat: refactor userEditing structure

* feat: properties panel with refactored data structure.

* feat: Schema selection implemented in commit button logic

* feat: implement Layer colors in group selectors

* feat: only parse the selected source on useraction commit

* feat: simplify structure for userEditing source

* fix: rerender group selection when selecting new part

* fix: read the SourceLayerType into UserEditingDefinition

* feat: selected element indicator

* feat: properties panel styling - is edited indication

* wip: add close propterties icon (sketch)

* wip: properties panel css dim commit+revert when not active

* wip: properties panel clean up first iteration selector

* wip: properties panel add close upper right

* fix: crash UI - remove transiongroup

* feat: properties panel animate in and shrink rundown view

* fix: cleanup using notificationpanel for properties panel

* wip: properties panel, rightbar icon color

* fix: re-run only if part.segmentId has changed

* wip: tests for properties panel

* wip: properties panel tests - useFaketimers and implement mock useTracker()

* wip: properties panel - element selection styling

* wip: properties panel styling edit pencil icon

* wip: properties panel close when notification is open

* wip: properties panel selection glow on segment header

* feat: StyledSchemaFormInPlace schema component

* fix: Properties panel - StyledSchemaFormInPlace schema for normal form

* feat: Properties panel is edited pencil styling

* feat: Properties header styling

* wip: Properties tests - more mocking to get correct rendering in test to work

* chore: fix unit tests

* feat: refactor form action to properties field

* chore: update styles

* chore: add actions back into property panel

* chore: support translations for properties panel

* chore: fix some build issues

* feat: allow editing piece properties

* chore: various review comments

* chore: fix tests

* chore: remove unused parameter

* chore: removed commented styles

---------

Co-authored-by: Mint de Wit <mint@superfly.tv>
  • Loading branch information
olzzon and mint-dewit authored Dec 13, 2024
1 parent 3a47bd1 commit deae44f
Show file tree
Hide file tree
Showing 50 changed files with 2,604 additions and 355 deletions.
2 changes: 2 additions & 0 deletions meteor/server/api/rest/v1/typeConversion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ export function studioSettingsFrom(apiStudioSettings: APIStudioSettings): Comple
enableQuickLoop: apiStudioSettings.enableQuickLoop,
forceQuickLoopAutoNext: forceQuickLoopAutoNextFrom(apiStudioSettings.forceQuickLoopAutoNext),
fallbackPartDuration: apiStudioSettings.fallbackPartDuration ?? DEFAULT_FALLBACK_PART_DURATION,
enableUserEdits: apiStudioSettings.enableUserEdits,
allowAdlibTestingSegment: apiStudioSettings.allowAdlibTestingSegment,
allowHold: apiStudioSettings.allowHold ?? true, // Backwards compatible
allowPieceDirectPlay: apiStudioSettings.allowPieceDirectPlay ?? true, // Backwards compatible
Expand All @@ -390,6 +391,7 @@ export function APIStudioSettingsFrom(settings: IStudioSettings): Complete<APISt
enableQuickLoop: settings.enableQuickLoop,
forceQuickLoopAutoNext: APIForceQuickLoopAutoNextFrom(settings.forceQuickLoopAutoNext),
fallbackPartDuration: settings.fallbackPartDuration,
enableUserEdits: settings.enableUserEdits,
allowAdlibTestingSegment: settings.allowAdlibTestingSegment,
allowHold: settings.allowHold,
allowPieceDirectPlay: settings.allowPieceDirectPlay,
Expand Down
1 change: 1 addition & 0 deletions meteor/server/lib/rest/v1/studios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ export interface APIStudioSettings {
forceQuickLoopAutoNext?: 'disabled' | 'enabled_when_valid_duration' | 'enabled_forcing_min_duration'
minimumTakeSpan?: number
fallbackPartDuration?: number
enableUserEdits?: boolean
allowAdlibTestingSegment?: boolean
allowHold?: boolean
allowPieceDirectPlay?: boolean
Expand Down
8 changes: 7 additions & 1 deletion packages/blueprints-integration/src/documents/part.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { UserEditingDefinition } from '../userEditing'
import { UserEditingDefinition, UserEditingProperties } from '../userEditing'
import type { NoteSeverity } from '../lib'
import type { ITranslatableMessage } from '../translations'

Expand Down Expand Up @@ -88,6 +88,12 @@ export interface IBlueprintMutatablePart<TPrivateData = unknown, TPublicData = u
* User editing definitions for this part
*/
userEditOperations?: UserEditingDefinition[]

/**
* Properties that are user editable from the properties panel in the Sofie UI, if the user saves changes to these
* it will trigger a user edit operation of type DefaultUserOperationEditProperties
*/
userEditProperties?: UserEditingProperties
}

export interface HackPartMediaObjectSubscription {
Expand Down
8 changes: 7 additions & 1 deletion packages/blueprints-integration/src/documents/piece.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { UserEditingDefinition } from '../userEditing'
import { UserEditingDefinition, UserEditingProperties } from '../userEditing'
import type { IBlueprintPieceGeneric } from './pieceGeneric'

/** Special types of pieces. Some are not always used in all circumstances */
Expand Down Expand Up @@ -36,6 +36,12 @@ export interface IBlueprintPiece<TPrivateData = unknown, TPublicData = unknown>
*/
userEditOperations?: UserEditingDefinition[]

/**
* Properties that are user editable from the properties panel in the Sofie UI, if the user saves changes to these
* it will trigger a user edit operation of type DefaultUserOperationEditProperties
*/
userEditProperties?: UserEditingProperties

/**
* Whether to stop this piece before the 'keepalive' period of the part
*/
Expand Down
8 changes: 7 additions & 1 deletion packages/blueprints-integration/src/documents/segment.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { UserEditingDefinition } from '../userEditing'
import { UserEditingDefinition, UserEditingProperties } from '../userEditing'

export enum SegmentDisplayMode {
Timeline = 'timeline',
Expand Down Expand Up @@ -52,6 +52,12 @@ export interface IBlueprintSegment<TPrivateData = unknown, TPublicData = unknown
* User editing definitions for this segment
*/
userEditOperations?: UserEditingDefinition[]

/**
* Properties that are user editable from the properties panel in the Sofie UI, if the user saves changes to these
* it will trigger a user edit operation of type DefaultUserOperationEditProperties
*/
userEditProperties?: UserEditingProperties
}
/** The Segment sent from Core */
export interface IBlueprintSegmentDB<TPrivateData = unknown, TPublicData = unknown>
Expand Down
34 changes: 32 additions & 2 deletions packages/blueprints-integration/src/ingest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,41 @@ export interface UserOperationTarget {
pieceExternalId: string | undefined
}

export type DefaultUserOperations = {
id: '__sofie-move-segment' // Future: define properly
export enum DefaultUserOperationsTypes {
REVERT_SEGMENT = '__sofie-revert-segment',
REVERT_PART = '__sofie-revert-part',
REVERT_RUNDOWN = '__sofie-revert-rundown',
UPDATE_PROPS = '__sofie-update-props',
}

export interface DefaultUserOperationRevertRundown {
id: DefaultUserOperationsTypes.REVERT_RUNDOWN
payload: Record<string, never>
}

export interface DefaultUserOperationRevertSegment {
id: DefaultUserOperationsTypes.REVERT_SEGMENT
payload: Record<string, never>
}

export interface DefaultUserOperationRevertPart {
id: DefaultUserOperationsTypes.REVERT_PART
}

export interface DefaultUserOperationEditProperties {
id: DefaultUserOperationsTypes.UPDATE_PROPS
payload: {
pieceTypeProperties: { type: string; value: Record<string, any> }
globalProperties: Record<string, any>
}
}

export type DefaultUserOperations =
| DefaultUserOperationRevertRundown
| DefaultUserOperationRevertSegment
| DefaultUserOperationRevertPart
| DefaultUserOperationEditProperties

export interface UserOperationChange<TCustomBlueprintOperations extends { id: string } = never> {
/** Indicate that this change is from user operations */
source: IngestChangeType.User
Expand Down
56 changes: 53 additions & 3 deletions packages/blueprints-integration/src/userEditing.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { JSONBlob } from '@sofie-automation/shared-lib/dist/lib/JSONBlob'
import type { ITranslatableMessage } from './translations'
import type { JSONSchema } from '@sofie-automation/shared-lib/dist/lib/JSONSchemaTypes'
import { JSONSchema } from '@sofie-automation/shared-lib/dist/lib/JSONSchemaTypes'
import { SourceLayerType } from './content'

/**
* Description of a user performed editing operation allowed on an document
Expand All @@ -16,8 +17,10 @@ export interface UserEditingDefinitionAction {
id: string
/** Label to show to the user for this operation */
label: ITranslatableMessage
/** Icon to show to when this action is 'active' */
/** Icon to show when this action is 'active' */
svgIcon?: string
/** Icon to show when this action is 'disabled' */
svgIconInactive?: string
/** Whether this action should be indicated as being active */
isActive?: boolean
}
Expand All @@ -40,6 +43,53 @@ export interface UserEditingDefinitionForm {
export enum UserEditingType {
/** Action */
ACTION = 'action',
/** Form of selections */
/** Form */
FORM = 'form',
}

export interface UserEditingSourceLayer {
sourceLayerLabel: string
sourceLayerType: SourceLayerType
schema: JSONBlob<JSONSchema>
defaultValue?: Record<string, any>
}

export interface UserEditingProperties {
/**
* These properties are dependent on the (primary) piece type, the user will get the option
* to select the type of piece (from the SourceLayerTypes i.e. Camera or Split etc.) and then
* be presented the corresponding form
*
* example:
* {
* schema: {
* camera: '{ "type": "object", "properties": { "input": { "type": "number" } } }',
* split: '{ "type": "object", ... }',
* },
* currentValue: {
* type: 'camera',
* value: {
* input: 3
* },
* }
* }
*/
pieceTypeProperties?: {
schema: Record<string, UserEditingSourceLayer>
currentValue: { type: string; value: Record<string, any> }
}

/**
* These are properties that are available to edit regardless of the piece type, examples
* could be whether it an element is locked from NRCS updates
*
* if you do not want the piece type to be changed, then use only this field.
*/
globalProperties?: { schema: JSONBlob<JSONSchema>; currentValue: Record<string, any> }

/**
* A list of id's of operations to be exposed on the properties panel as buttons. These operations
* must be available on the element
*/
operations?: UserEditingDefinitionAction[]
}
8 changes: 7 additions & 1 deletion packages/corelib/src/dataModel/Part.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ITranslatableMessage } from '../TranslatableMessage'
import { PartId, RundownId, SegmentId } from './Ids'
import { PartNote } from './Notes'
import { ReadonlyDeep } from 'type-fest'
import { CoreUserEditingDefinition } from './UserEditingDefinitions'
import { CoreUserEditingDefinition, CoreUserEditingProperties } from './UserEditingDefinitions'

export interface PartInvalidReason {
message: ITranslatableMessage
Expand Down Expand Up @@ -41,6 +41,12 @@ export interface DBPart extends Omit<IBlueprintPart, 'userEditOperations'> {
* User editing definitions for this part
*/
userEditOperations?: CoreUserEditingDefinition[]

/**
* Properties that are user editable from the properties panel in the Sofie UI, if the user saves changes to these
* it will trigger a user edit operation of type DefaultUserOperationEditProperties
*/
userEditProperties?: CoreUserEditingProperties
}

export function isPartPlayable(part: Pick<ReadonlyDeep<DBPart>, 'invalid' | 'floated'>): boolean {
Expand Down
12 changes: 10 additions & 2 deletions packages/corelib/src/dataModel/Piece.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from '@sofie-automation/blueprints-integration'
import { ProtectedString, protectString, unprotectString } from '../protectedString'
import { PieceId, RundownId, SegmentId, PartId } from './Ids'
import { CoreUserEditingDefinition } from './UserEditingDefinitions'
import { CoreUserEditingDefinition, CoreUserEditingProperties } from './UserEditingDefinitions'

/** A generic list of playback availability statuses for a Piece */
export enum PieceStatusCode {
Expand Down Expand Up @@ -50,7 +50,9 @@ export interface PieceGeneric extends Omit<IBlueprintPieceGeneric, 'content'> {
/** Stringified timelineObjects */
timelineObjectsString: PieceTimelineObjectsBlob
}
export interface Piece extends PieceGeneric, Omit<IBlueprintPieceDB, '_id' | 'content' | 'userEditOperations'> {
export interface Piece
extends PieceGeneric,
Omit<IBlueprintPieceDB, '_id' | 'content' | 'userEditOperations' | 'userEditProperties'> {
/**
* This is the id of the rundown this piece starts playing in.
* Currently this is the only rundown the piece could be playing in
Expand All @@ -77,6 +79,12 @@ export interface Piece extends PieceGeneric, Omit<IBlueprintPieceDB, '_id' | 'co
* User editing definitions for this piece
*/
userEditOperations?: CoreUserEditingDefinition[]

/**
* Properties that are user editable from the properties panel in the Sofie UI, if the user saves changes to these
* it will trigger a user edit operation of type DefaultUserOperationEditProperties
*/
userEditProperties?: CoreUserEditingProperties
}

export type PieceTimelineObjectsBlob = ProtectedString<'PieceTimelineObjectsBlob'>
Expand Down
8 changes: 7 additions & 1 deletion packages/corelib/src/dataModel/Segment.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { SegmentDisplayMode, SegmentTimingInfo } from '@sofie-automation/blueprints-integration'
import { SegmentId, RundownId } from './Ids'
import { SegmentNote } from './Notes'
import { CoreUserEditingDefinition } from './UserEditingDefinitions'
import { CoreUserEditingDefinition, CoreUserEditingProperties } from './UserEditingDefinitions'

export enum SegmentOrphanedReason {
/** Segment is deleted from the NRCS but we still need it */
Expand Down Expand Up @@ -51,4 +51,10 @@ export interface DBSegment {
* User editing definitions for this segment
*/
userEditOperations?: CoreUserEditingDefinition[]

/**
* Properties that are user editable from the properties panel in the Sofie UI, if the user saves changes to these
* it will trigger a user edit operation of type DefaultUserOperationEditProperties
*/
userEditProperties?: CoreUserEditingProperties
}
59 changes: 57 additions & 2 deletions packages/corelib/src/dataModel/UserEditingDefinitions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { UserEditingType, JSONBlob, JSONSchema } from '@sofie-automation/blueprints-integration'
import type {
UserEditingType,
JSONBlob,
JSONSchema,
UserEditingSourceLayer,
} from '@sofie-automation/blueprints-integration'
import type { ITranslatableMessage } from '../TranslatableMessage'

export type CoreUserEditingDefinition = CoreUserEditingDefinitionAction | CoreUserEditingDefinitionForm
Expand All @@ -9,12 +14,17 @@ export interface CoreUserEditingDefinitionAction {
id: string
/** Label to show to the user for this operation */
label: ITranslatableMessage
/** Icon to show to when this action is 'active' */
/** Icon to show when this action is 'active' */
svgIcon?: string
/** Icon to show when this action is 'disabled' */
svgIconInactive?: string
/** Whether this action should be indicated as being active */
isActive?: boolean
}

/**
* A simple form based operation
*/
export interface CoreUserEditingDefinitionForm {
type: UserEditingType.FORM
/** Id of this operation */
Expand All @@ -28,3 +38,48 @@ export interface CoreUserEditingDefinitionForm {
/** Translation namespaces to use when rendering this form */
translationNamespaces: string[]
}

export interface CoreUserEditingProperties {
/**
* These properties are dependent on the (primary) piece type, the user will get the option
* to select the type of piece (from the SourceLayerTypes i.e. Camera or Split etc.) and then
* be presented the corresponding form
*
* example:
* {
* schema: {
* camera: '{ "type": "object", "properties": { "input": { "type": "number" } } }',
* split: '{ "type": "object", ... }',
* },
* currentValue: {
* type: 'camera',
* value: {
* input: 3
* },
* }
* }
*/
pieceTypeProperties?: {
schema: Record<string, UserEditingSourceLayer>
currentValue: { type: string; value: Record<string, any> }
}

/**
* These are properties that are available to edit regardless of the piece type, examples
* could be whether it an element is locked from NRCS updates
*
* if you do not want the piece type to be changed, then use only this field.
*/
globalProperties?: { schema: JSONBlob<JSONSchema>; currentValue: Record<string, any> }

/**
* A list of id's of operations to be exposed on the properties panel as buttons. These operations
* must be available on the element
*
* note - perhaps these should have their own full definitions?
*/
operations?: CoreUserEditingDefinitionAction[]

/** Translation namespaces to use when rendering this form */
translationNamespaces: string[]
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ If an integer property, whether to treat it as zero-based
### `ui:displayType`

Override the presentation with a special mode.
Currently only valid for string properties. Valid values are 'json'.

Currently only valid for:

- object properties. Valid values are 'json'.
- string properties. Valid values are 'base64-image'.
- boolean properties. Valid values are 'switch'.

### `tsEnumNames`

Expand Down
Loading

0 comments on commit deae44f

Please sign in to comment.