diff --git a/docs/documentation/docs/assets/ModernAudioDarkLime.png b/docs/documentation/docs/assets/ModernAudioDarkLime.png new file mode 100644 index 000000000..714a11160 Binary files /dev/null and b/docs/documentation/docs/assets/ModernAudioDarkLime.png differ diff --git a/docs/documentation/docs/assets/ModernAudioDefault.png b/docs/documentation/docs/assets/ModernAudioDefault.png new file mode 100644 index 000000000..38d2b642e Binary files /dev/null and b/docs/documentation/docs/assets/ModernAudioDefault.png differ diff --git a/docs/documentation/docs/assets/ModernAudioInAction.gif b/docs/documentation/docs/assets/ModernAudioInAction.gif new file mode 100644 index 000000000..0fa3eb208 Binary files /dev/null and b/docs/documentation/docs/assets/ModernAudioInAction.gif differ diff --git a/docs/documentation/docs/controls/ModernAudio.md b/docs/documentation/docs/controls/ModernAudio.md new file mode 100644 index 000000000..6fe08c8b3 --- /dev/null +++ b/docs/documentation/docs/controls/ModernAudio.md @@ -0,0 +1,62 @@ +# Modern Audio + +This control renders an Audio Control in a modern and themable way. It is controllable with Fluent UI icons instead of old-fashioned standard HTML5 Audio control. + +!!! Note + Originally it's coming from the following community [Teams app sample](https://github.com/pnp/teams-dev-samples/tree/main/samples/tab-meeting-record-name). + +**Modern Audio control rendered with label and default label positioning** + +![Modern Audio](../assets/ModernAudioDefault.png) + +**Modern Audio control rendered with dark (lime) theme and label positioned BottomLeft** + +![Modern Audio dark](../assets/ModernAudioDarkLime.png) + +**Modern Audio control in action with label positioned at BottomCenter** + +![Modern Audio in action](../assets/ModernAudioInAction.gif) + +## How to use this control in your solutions + +- Check that you installed the `@pnp/spfx-controls-react` dependency. Check out the [getting started](../../#getting-started) page for more information about installing the dependency. +- Import the following modules to your component: + +```typescript +import { ModernAudio, ModernAudioLabelPosition } from "@pnp/spfx-controls-react/lib/ModernAudio"; +``` + +- Use the `ModernAudio` control in your code as follows: + +```typescript + +``` + + +## Implementation + +The Modern Audio control can be configured with the following properties: + +| Property | Type | Required | Description | Default | +| ---- | ---- | ---- | ---- | ---- | +| audioUrl | string | yes | Url to the audio src | | +| label | string | no | Label to use for the control. | blank | +| labelPosition | ModernAudioLabelPosition | no | Define position of label: TopLeft, TopCenter, BottomLeft, BottomCenter. | TopLeft | + +Enum `ModernAudioLabelPosition` + +The `ModernAudioLabelPosition` enum can be used to specify the types of information you want to query: User, Security groups, and/or SharePoint groups. + +| Name | Value | Position +| ---- | ---- | ---- | +| TopLeft | 1 | On top, left aligned +| TopCenter | 2 | On top, centered +| BottomLeft | 3 | At bottom, left aligned +| BottomCenter | 4 | At bottom, centered + + +![](https://telemetry.sharepointpnp.com/sp-dev-fx-controls-react/wiki/controls/ModernAudio) + diff --git a/src/ModernAudio.ts b/src/ModernAudio.ts new file mode 100644 index 000000000..45bd11af8 --- /dev/null +++ b/src/ModernAudio.ts @@ -0,0 +1 @@ +export * from './controls/modernAudio/index'; \ No newline at end of file diff --git a/src/controls/modernAudio/IModernAudioProps.ts b/src/controls/modernAudio/IModernAudioProps.ts new file mode 100644 index 000000000..7d4cd8b2d --- /dev/null +++ b/src/controls/modernAudio/IModernAudioProps.ts @@ -0,0 +1,7 @@ +import { ModernAudioLabelPosition } from "./ModernAudioLabelPosition"; + +export interface IModernAudioProps { + audioUrl: string; + label?: string; + labelPosition?: ModernAudioLabelPosition; +} \ No newline at end of file diff --git a/src/controls/modernAudio/ModernAudio.module.scss b/src/controls/modernAudio/ModernAudio.module.scss new file mode 100644 index 000000000..7d7ebd8c5 --- /dev/null +++ b/src/controls/modernAudio/ModernAudio.module.scss @@ -0,0 +1,13 @@ +.modernAudio { + display: table-cell; + padding: 8px; + .audioPanel { + border: 1px dotted; + border-radius: 8px; + padding: 8px; + display: inline-block; + } + .audioIcon { + cursor: pointer; + } +} \ No newline at end of file diff --git a/src/controls/modernAudio/ModernAudio.tsx b/src/controls/modernAudio/ModernAudio.tsx new file mode 100644 index 000000000..1cc51155c --- /dev/null +++ b/src/controls/modernAudio/ModernAudio.tsx @@ -0,0 +1,78 @@ +import * as React from "react"; +import styles from './ModernAudio.module.scss'; +import * as strings from 'ControlStrings'; +import { IModernAudioProps } from "./IModernAudioProps"; +import { IconButton } from "office-ui-fabric-react"; +import { ModernAudioLabelPosition } from './ModernAudioLabelPosition'; + +export const ModernAudio: React.FC = (props: IModernAudioProps) => { + const audioComp = React.useRef(new Audio(props.audioUrl)); + const [muted, setMuted] = React.useState(false); + const [playing, setPlaying] = React.useState(false); + + React.useEffect(() => { + audioComp.current.onended = () => { setPlaying(false); }; + }, []); + + const playAudio = () => { + setPlaying(true); + audioComp.current.play(); + }; + const pauseAudio = () => { + setPlaying(false); + audioComp.current.pause(); + }; + const incVolume = () => { + if (audioComp.current.volume <= 0.9) { + audioComp.current.volume += 0.1; + } + else { + audioComp.current.volume = 1; + } + if (audioComp.current.muted) { + audioComp.current.muted = false; + setMuted(false); + } + }; + const decVolume = () => { + audioComp.current.volume -= 0.1; + if (audioComp.current.volume < 0.1) { + audioComp.current.volume = 0; + audioComp.current.muted = true; + setMuted(true); + } + }; + const muteAudio = () => { + audioComp.current.muted = !muted; + setMuted(!muted); + }; + return ( +
+ {props.labelPosition === ModernAudioLabelPosition.TopLeft && + props.label !== "" && +
} + {props.labelPosition === ModernAudioLabelPosition.TopCenter && + props.label !== "" && +
} +
+ {props.audioUrl !== "" && } + + + + + +
+ {props.labelPosition === ModernAudioLabelPosition.BottomLeft && + props.label !== "" && +
} + {props.labelPosition === ModernAudioLabelPosition.BottomCenter && + props.label !== "" && +
} +
+ ); +}; + +ModernAudio.defaultProps = { + label: "", + labelPosition: ModernAudioLabelPosition.TopLeft +}; diff --git a/src/controls/modernAudio/ModernAudioLabelPosition.ts b/src/controls/modernAudio/ModernAudioLabelPosition.ts new file mode 100644 index 000000000..ae7198dde --- /dev/null +++ b/src/controls/modernAudio/ModernAudioLabelPosition.ts @@ -0,0 +1,6 @@ +export enum ModernAudioLabelPosition { + TopLeft = 1, + TopCenter, + BottomLeft, + BottomCenter +} \ No newline at end of file diff --git a/src/controls/modernAudio/index.ts b/src/controls/modernAudio/index.ts new file mode 100644 index 000000000..e613dd841 --- /dev/null +++ b/src/controls/modernAudio/index.ts @@ -0,0 +1,3 @@ +export * from './IModernAudioProps'; +export * from './ModernAudio'; +export * from './ModernAudioLabelPosition'; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 6c280d078..c5999c33f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,3 +40,4 @@ export * from './DynamicForm'; export * from './Carousel'; export * from './ModernTaxonomyPicker'; export * from './LivePersona'; +export * from './ModernAudio'; \ No newline at end of file diff --git a/src/loc/de-de.ts b/src/loc/de-de.ts index 8134464de..f7c60a51f 100644 --- a/src/loc/de-de.ts +++ b/src/loc/de-de.ts @@ -378,6 +378,11 @@ define([], () => { "ModernTaxonomyPickerRemoveButtonText": "Löschen", "ModernTaxonomyPickerPanelCloseButtonText": "Schließen", "ModernTaxonomyPickerNoResultsFound": "Keine Ergebnisse gefunden", - "ModernTaxonomyPickerSuggestionInLabel": "In" + "ModernTaxonomyPickerSuggestionInLabel": "In", + "ModernAudioPlay": "Abspielen", + "ModernAudioPause": "Pause", + "ModernAudioIncVol": "Lauter", + "ModernAudioDecVol": "Leiser", + "ModernAudioMute": "Ton aus" }; }); diff --git a/src/loc/en-us.ts b/src/loc/en-us.ts index bb5fbbe5a..98cb66466 100644 --- a/src/loc/en-us.ts +++ b/src/loc/en-us.ts @@ -398,6 +398,12 @@ define([], () => { ModernTaxonomyPickerRemoveButtonText: "Remove", ModernTaxonomyPickerPanelCloseButtonText: "Close", ModernTaxonomyPickerNoResultsFound: "No results found", - ModernTaxonomyPickerSuggestionInLabel: "in" + ModernTaxonomyPickerSuggestionInLabel: "in", + + ModernAudioPlay: "Play", + ModernAudioPause: "Pause", + ModernAudioIncVol: "Increase Volume", + ModernAudioDecVol: "Decrease Volume", + ModernAudioMute: "Mute" }; }); diff --git a/src/loc/mystrings.d.ts b/src/loc/mystrings.d.ts index 4383b1a72..9f7ecf2bf 100644 --- a/src/loc/mystrings.d.ts +++ b/src/loc/mystrings.d.ts @@ -374,6 +374,13 @@ declare interface IControlStrings { ModernTaxonomyPickerPanelCloseButtonText: string; ModernTaxonomyPickerNoResultsFound: string; ModernTaxonomyPickerSuggestionInLabel: string; + + // Modern Audio control + ModernAudioPlay: string; + ModernAudioPause: string; + ModernAudioIncVol: string; + ModernAudioDecVol: string; + ModernAudioMute: string; } declare interface IDateTimeStrings { diff --git a/src/webparts/controlsTest/components/ControlsTest.tsx b/src/webparts/controlsTest/components/ControlsTest.tsx index 090a99426..0190c26d1 100644 --- a/src/webparts/controlsTest/components/ControlsTest.tsx +++ b/src/webparts/controlsTest/components/ControlsTest.tsx @@ -185,7 +185,7 @@ import { VariantThemeProvider, VariantType } from "../../../controls/variantThem import { Label } from "office-ui-fabric-react/lib/Label"; import { EnhancedThemeProvider } from "../../../EnhancedThemeProvider"; import { ControlsTestEnhancedThemeProvider, ControlsTestEnhancedThemeProviderFunctionComponent } from "./ControlsTestEnhancedThemeProvider"; - +import { ModernAudio, ModernAudioLabelPosition } from "../../../ModernAudio"; // Used to render document card @@ -1402,6 +1402,8 @@ export default class ControlsTest extends React.Component + +