返回 oh-my-ppt
MediaInspector.tsx
1 import { Image } from 'lucide-react'
2 import { Input } from '../../ui/Input'
3 import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../ui/Select'
4 import { InspectorSection } from './InspectorSection'
5 import type { ElementEditorProps } from './types'
6 import { useT } from '@renderer/i18n'
7
8 function ToggleRow({
9 label,
10 checked,
11 onChange
12 }: {
13 label: string
14 checked: boolean
15 onChange: (checked: boolean) => void
16 }): React.JSX.Element {
17 return (
18 <label className="flex h-8 items-center justify-between rounded-full border border-[#d7cbb7]/40 bg-[#f5efe4]/40 px-3 text-xs text-[#59664b]">
19 <span>{label}</span>
20 <input
21 type="checkbox"
22 checked={checked}
23 onChange={(event) => onChange(event.target.checked)}
24 className="h-4 w-4 accent-[#7a875f]"
25 />
26 </label>
27 )
28 }
29
30 export function MediaInspector({
31 selection,
32 draft,
33 onDraftChange
34 }: ElementEditorProps): React.JSX.Element {
35 const t = useT()
36 const mediaAttrs = selection.snapshot?.attrs || {}
37 const isVideo = selection.elementTag === 'video'
38 const hasVideoControls =
39 'controls' in mediaAttrs ||
40 'muted' in mediaAttrs ||
41 'loop' in mediaAttrs ||
42 'autoplay' in mediaAttrs ||
43 'playsInline' in mediaAttrs ||
44 'preload' in mediaAttrs
45 return (
46 <InspectorSection
47 title={t('sessionDetail.media')}
48 icon={<Image className="h-3.5 w-3.5 text-[#7a875f]" />}
49 >
50 <div className="space-y-2.5">
51 {!isVideo && (
52 <label className="block space-y-1.5">
53 <span className="text-[11px] font-medium text-[#7a875f]">
54 {t('sessionDetail.objectFit')}
55 </span>
56 <Select
57 value={draft.objectFit || 'contain'}
58 onValueChange={(value) =>
59 onDraftChange(
60 { ...draft, objectFit: value },
61 { commit: true, fields: ['objectFit'] }
62 )
63 }
64 >
65 <SelectTrigger className="h-8 rounded-full border-[#ded2bd]/72 bg-[#fffdf8]/88 px-2.5 text-xs text-[#3f4b35] shadow-[inset_0_1px_2px_rgba(74,59,42,0.05)] focus-visible:border-[#9bb98a]">
66 <SelectValue />
67 </SelectTrigger>
68 <SelectContent>
69 <SelectItem value="contain">contain</SelectItem>
70 <SelectItem value="cover">cover</SelectItem>
71 <SelectItem value="fill">fill</SelectItem>
72 <SelectItem value="none">none</SelectItem>
73 <SelectItem value="scale-down">scale-down</SelectItem>
74 </SelectContent>
75 </Select>
76 </label>
77 )}
78 {!isVideo && (
79 <label className="block space-y-1.5">
80 <span className="text-[11px] font-medium text-[#7a875f]">{t('sessionDetail.alt')}</span>
81 <Input
82 value={draft.alt}
83 onChange={(event) => onDraftChange({ ...draft, alt: event.target.value })}
84 onBlur={(event) =>
85 onDraftChange(
86 { ...draft, alt: event.target.value },
87 { commit: true, fields: ['alt'] }
88 )
89 }
90 className="h-8 rounded-full border border-[#ded2bd]/72 bg-[#fffdf8]/88 px-2.5 text-xs text-[#3f4b35] shadow-[inset_0_1px_2px_rgba(74,59,42,0.05)] focus-visible:border-[#9bb98a] focus-visible:ring-0 focus-visible:ring-offset-0"
91 />
92 </label>
93 )}
94 {hasVideoControls && (
95 <>
96 <div className="grid grid-cols-2 gap-2">
97 <ToggleRow
98 label={t('sessionDetail.controls')}
99 checked={draft.controls}
100 onChange={(controls) =>
101 onDraftChange({ ...draft, controls }, { commit: true, fields: ['controls'] })
102 }
103 />
104 <ToggleRow
105 label={t('sessionDetail.muted')}
106 checked={draft.muted}
107 onChange={(muted) =>
108 onDraftChange({ ...draft, muted }, { commit: true, fields: ['muted'] })
109 }
110 />
111 <ToggleRow
112 label={t('sessionDetail.loop')}
113 checked={draft.loop}
114 onChange={(loop) =>
115 onDraftChange({ ...draft, loop }, { commit: true, fields: ['loop'] })
116 }
117 />
118 <ToggleRow
119 label={t('sessionDetail.autoplay')}
120 checked={draft.autoplay}
121 onChange={(autoplay) =>
122 onDraftChange({ ...draft, autoplay }, { commit: true, fields: ['autoplay'] })
123 }
124 />
125 <ToggleRow
126 label={t('sessionDetail.playsInline')}
127 checked={draft.playsInline}
128 onChange={(playsInline) =>
129 onDraftChange(
130 { ...draft, playsInline },
131 { commit: true, fields: ['playsInline'] }
132 )
133 }
134 />
135 </div>
136 </>
137 )}
138 </div>
139 </InspectorSection>
140 )
141 }
142
142 lines Plain Text