-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(FEC-13945): move core controls to upper bar #59
Changes from all commits
28ed032
0436723
a843b1e
fe26a7b
526d6d4
12beb17
7a4aab5
75caae1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,8 +14,9 @@ export class IconModel { | |
public componentRef: RefObject<IconWrapper>; | ||
public onClick: (e: MouseEvent | KeyboardEvent) => void; | ||
public component: ComponentClass<Record<string, never>> | FunctionalComponent<Record<string, never>>; | ||
public svgIcon: SvgIcon; | ||
public svgIcon: SvgIcon | (() => SvgIcon); | ||
public presets: PlaykitUI.ReservedPresetName[]; | ||
public shouldHandleOnClick: boolean; | ||
constructor(item: IconDto) { | ||
this.id = ++IconModel.nextId; | ||
this.displayName = item.displayName; | ||
|
@@ -27,6 +28,7 @@ export class IconModel { | |
this.componentRef = createRef(); | ||
this.presets = | ||
item.presets && item.presets.length > 0 ? item.presets : [ReservedPresetNames.Playback, ReservedPresetNames.Live]; | ||
this.shouldHandleOnClick = typeof item.shouldHandleOnClick === 'boolean' ? item.shouldHandleOnClick : true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we need this ? why just check if on click is supplied or not There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the new another explanation: |
||
} | ||
|
||
public update(): void { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { KalturaPlayer, Logger } from '@playkit-js/kaltura-player-js'; | ||
import { UpperBarManager } from './upper-bar-manager'; | ||
import { IconDto } from './models/icon-dto'; | ||
|
||
export class MoveControlsManager { | ||
private readonly player: KalturaPlayer; | ||
private readonly logger: Logger; | ||
private store: any; | ||
private upperBarManager: UpperBarManager; | ||
private currentState: any; | ||
private iconIds: Map<string, number>; | ||
|
||
constructor(player: KalturaPlayer, logger: Logger, upperBarManager: UpperBarManager, redux: any) { | ||
this.player = player; | ||
this.logger = logger; | ||
this.store = redux.useStore(); | ||
this.upperBarManager = upperBarManager; | ||
this.store.subscribe(this.handleStoreChange.bind(this)); | ||
this.currentState = this.store.getState(); | ||
this.iconIds = new Map(); | ||
} | ||
|
||
private get bottomBarRegistryManager(): any { | ||
return (this.player.getService('bottomBarRegistryManager') as any) || undefined; | ||
SivanA-Kaltura marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
private get state(): any { | ||
return this.store.getState(); | ||
} | ||
|
||
private handleStoreChange(): void { | ||
const newState = this.state; | ||
const bottomBarRegistryManager = this.bottomBarRegistryManager; | ||
if (bottomBarRegistryManager && this.currentState.bottomBar !== newState.bottomBar) { | ||
SivanA-Kaltura marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this.logger.debug('Removing core controls from upper bar'); | ||
SivanA-Kaltura marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// remove all the core icons and clear map | ||
[...this.iconIds.values()].forEach((iconId) => this.upperBarManager.remove(iconId)); | ||
this.iconIds.clear(); | ||
|
||
const { controlsToMove } = newState.bottomBar; | ||
if (controlsToMove.length > 0) { | ||
this.logger.debug('Adding core controls to upper bar: ', controlsToMove); | ||
controlsToMove.forEach((componentName: string) => { | ||
const componentToMove: IconDto = bottomBarRegistryManager.getComponentItem(componentName); | ||
if (componentToMove) { | ||
const iconId = this.upperBarManager.add(componentToMove); | ||
if (typeof iconId === 'number') { | ||
SivanA-Kaltura marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this.iconIds.set(componentName, iconId); | ||
} | ||
} | ||
}); | ||
} | ||
this.currentState = newState; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
.right-upper-bar-wrapper-container { | ||
direction: ltr; | ||
display: flex; | ||
align-items: center; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
@import '~@playkit-js/playkit-js-ui'; | ||
|
||
.dropdown-item { | ||
border-radius: 4px; | ||
padding: 4px 12px 4px 15px; | ||
display: flex; | ||
margin: 4px 0; | ||
cursor: pointer; | ||
align-items: center; | ||
|
||
.icon { | ||
width: 24px; | ||
height: 24px; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
|
||
i { | ||
display: inline-block; | ||
} | ||
} | ||
|
||
&:hover { | ||
background-color: $tone-6-color; | ||
} | ||
|
||
.dropdown-item-description { | ||
flex: 1; | ||
font-size: 14px; | ||
font-weight: 700; | ||
padding-left: 11px; | ||
overflow: hidden; | ||
white-space: nowrap; | ||
|
||
&.trim-text { | ||
text-overflow: ellipsis; | ||
} | ||
} | ||
|
||
.comparison-text { | ||
position: absolute; | ||
font-size: 14px; | ||
font-weight: 700; | ||
left: 0; | ||
padding: 0; | ||
} | ||
} | ||
|
||
.more-item-tooltip { | ||
z-index: 1; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { h, Fragment, VNode } from 'preact'; | ||
import { useState, useRef, useLayoutEffect } from 'preact/hooks'; | ||
import * as styles from './dropdown-bar-item.scss'; | ||
import { ui } from '@playkit-js/kaltura-player-js'; | ||
import { A11yWrapper } from '@playkit-js/common/dist/hoc/a11y-wrapper'; | ||
import { SvgIcon } from '../../models/svg-icon'; | ||
const { Icon, Tooltip } = ui.Components; | ||
|
||
type DropdownBarItemProps = { | ||
displayName: string; | ||
text: string; | ||
icon: SvgIcon; | ||
onClick: (e: KeyboardEvent | MouseEvent) => void; | ||
onDropdownClick: () => void; | ||
tooltipPosition: string; | ||
}; | ||
|
||
const PADDING = 11; | ||
|
||
const DropdownBarItem = ({ displayName, text, icon, onClick, onDropdownClick, tooltipPosition }: DropdownBarItemProps) => { | ||
const comparisonTextRef = useRef<HTMLSpanElement | null>(null); | ||
const textRef = useRef<HTMLSpanElement | null>(null); | ||
|
||
const [showTooltip, setShowTooltip] = useState(false); | ||
const [isFinalized, setIsFinalized] = useState(false); | ||
SivanA-Kaltura marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
useLayoutEffect(() => { | ||
if (!isFinalized && textRef?.current && comparisonTextRef?.current) { | ||
setIsFinalized(true); | ||
const textWidth = textRef?.current.getBoundingClientRect().width - PADDING; | ||
const comparisonTextWidth = comparisonTextRef?.current.getBoundingClientRect().width; | ||
setShowTooltip(comparisonTextWidth > textWidth); | ||
} | ||
}); | ||
|
||
const renderIcon = (): VNode => { | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
return <Icon type={icon.type} id={displayName} path={icon.path} viewBox={icon.viewBox || '0 0 32 32'} />; | ||
}; | ||
|
||
const textElement = ( | ||
<span className={[styles.dropdownItemDescription, showTooltip ? styles.trimText : ''].join(' ')} ref={textRef}> | ||
{text} | ||
</span> | ||
); | ||
const comparisonTextElement = ( | ||
<span ref={comparisonTextRef} className={styles.comparisonText}> | ||
{text} | ||
</span> | ||
); | ||
const content = !isFinalized ? ( | ||
<> | ||
{textElement} | ||
{comparisonTextElement} | ||
</> | ||
) : ( | ||
textElement | ||
); | ||
|
||
const renderContent = (): VNode => { | ||
return ( | ||
<A11yWrapper | ||
onClick={(e): void => { | ||
onClick(e); | ||
onDropdownClick(); | ||
}} | ||
role="menuitem" | ||
> | ||
<div className={styles.dropdownItem} tabIndex={0} aria-label={text}> | ||
<div className={styles.icon}>{renderIcon()}</div> | ||
{content} | ||
</div> | ||
</A11yWrapper> | ||
); | ||
}; | ||
return ( | ||
<Fragment> | ||
{showTooltip ? ( | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
<Tooltip label={text} type={tooltipPosition} className={styles.moreItemTooltip}> | ||
{renderContent()} | ||
</Tooltip> | ||
) : ( | ||
renderContent() | ||
)} | ||
</Fragment> | ||
); | ||
}; | ||
|
||
export { DropdownBarItem }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why it should be a function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the controls that I handled (PiP, CC, AAD, etc..) have 2 possible icons, which depends on the system's state. Hence, I added to the type a function that returns the SvgIcon.