PluginProbe ʕ •ᴥ•ʔ
Presto Player / 4.3.3
Presto Player v4.3.3
4.4.0 4.3.3 4.3.2 4.3.1 4.3.0 4.2.4 4.2.3 4.2.2 4.2.0 4.2.1 trunk 1.10.0 1.10.1 1.10.2 1.11.0 1.12.0 1.13.0 1.14.0 1.14.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.14 1.5.15 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.10 1.6.11 1.6.12 1.6.13 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.13 2.0.14 2.0.15 2.0.16 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.2.0 2.2.1 2.2.2 2.2.3 2.2.3-beta1 2.3.0 2.3.1 2.3.2 2.3.3 3.0.0 3.0.0-beta1 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.1.0 3.1.1 3.1.2 3.1.3 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4
presto-player / src / admin / dashboard / pages / settings / general / CustomCssPage.js
presto-player / src / admin / dashboard / pages / settings / general Last commit date
BrandingPage.js 3 months ago ContributePage.js 3 months ago CustomCssPage.js 3 months ago LicensePage.js 3 months ago MediaHubPage.js 3 months ago PresetsPage.js 3 months ago UninstallPage.js 3 months ago ViewingAnalyticsPage.js 3 months ago
CustomCssPage.js
130 lines
1 import { __ } from '@wordpress/i18n';
2 import { useCallback, useEffect, useState } from 'react';
3 import { Container, Skeleton, Text, toast } from '@bsf/force-ui';
4 import SettingsPageShell from '../shared/SettingsPageShell';
5 import SectionCard from '../shared/SectionCard';
6 import ProGate from '../shared/ProGate';
7 import useSettingOption from '../../../hooks/useSettingOption';
8 import useRegisterActivePage from '../../../hooks/useRegisterActivePage';
9 import { OPTION_KEYS } from '../config';
10
11 const DEFAULT_BRANDING = {
12 logo: '',
13 logo_width: 150,
14 color: '#00b3ff',
15 player_css: '',
16 };
17
18 const isPremium = window.prestoPlayer?.isPremium ?? false;
19
20 const CustomCssPage = ( { registerActivePage } ) => {
21 const { data, setData, save, reset, isDirty, isSaving, isLoading } =
22 useSettingOption( OPTION_KEYS.branding, DEFAULT_BRANDING );
23
24 const [ CodeMirrorEditor, setCodeMirrorEditor ] = useState( null );
25 const [ cssExtension, setCssExtension ] = useState( null );
26
27 useEffect( () => {
28 Promise.all( [
29 import( '@uiw/react-codemirror' ),
30 import( '@codemirror/lang-css' ),
31 ] ).then( ( [ CodeMirrorModule, CSSModule ] ) => {
32 setCodeMirrorEditor( () => CodeMirrorModule.default );
33 setCssExtension( () => CSSModule.css );
34 } );
35 }, [] );
36
37 const update = ( patch ) =>
38 setData( ( prev ) => ( {
39 ...( prev || DEFAULT_BRANDING ),
40 ...patch,
41 } ) );
42
43 const handleSave = useCallback( async () => {
44 try {
45 await save();
46 toast.success( __( 'Settings saved.', 'presto-player' ), {
47 autoDismiss: 3000,
48 } );
49 } catch ( err ) {
50 toast.error( __( 'Could not save settings.', 'presto-player' ), {
51 autoDismiss: 5000,
52 } );
53 }
54 }, [ save ] );
55
56 useRegisterActivePage( registerActivePage, {
57 isDirty,
58 save: handleSave,
59 discard: reset,
60 } );
61
62 return (
63 <SettingsPageShell
64 title={ __( 'Custom CSS', 'presto-player' ) }
65 isDirty={ isDirty }
66 isSaving={ isSaving }
67 isLoading={ isLoading }
68 onSave={ handleSave }
69 >
70 <ProGate
71 enabled={ isPremium }
72 title={ __( 'Custom CSS', 'presto-player' ) }
73 description={ __(
74 'Style the player with custom CSS across your site.',
75 'presto-player'
76 ) }
77 >
78 <SectionCard>
79 <Container direction="column" className="gap-6">
80 <Text size="sm" className="text-text-secondary">
81 { __(
82 'Add custom CSS rules to fine-tune how your video player looks. Styles defined here apply globally to every Presto Player on your site.',
83 'presto-player'
84 ) }
85 </Text>
86 <div className="presto-custom-css-panel presto-css-editor">
87 { ! CodeMirrorEditor || ! cssExtension ? (
88 <Skeleton className="h-48 w-full" />
89 ) : (
90 <CodeMirrorEditor
91 value={ data?.player_css || '' }
92 height="300px"
93 extensions={ [ cssExtension() ] }
94 onChange={ ( val ) => update( { player_css: val } ) }
95 editable={ isPremium }
96 />
97 ) }
98 </div>
99 <Container direction="column" className="gap-2">
100 <Text size="sm" weight={ 600 }>
101 { __( 'Quick Info:', 'presto-player' ) }
102 </Text>
103 <ul className="list-disc pl-5 m-0 flex flex-col gap-1.5">
104 <li>
105 <Text size="sm" className="text-text-secondary">
106 { __(
107 'Use your browser’s developer tools (right-click → Inspect) to find the exact selectors before writing rules.',
108 'presto-player'
109 ) }
110 </Text>
111 </li>
112 <li>
113 <Text size="sm" className="text-text-secondary">
114 { __(
115 'Custom CSS loads after all default styles, so your rules override the player’s built-in styling without needing !important.',
116 'presto-player'
117 ) }
118 </Text>
119 </li>
120 </ul>
121 </Container>
122 </Container>
123 </SectionCard>
124 </ProGate>
125 </SettingsPageShell>
126 );
127 };
128
129 export default CustomCssPage;
130