Initial commit: WordPress wp-content (themes, plugins, languages)

- Theme: momentry (custom theme with REST API routes)
- Plugins: code-snippets (contains all API proxies)
- Languages: zh_TW translations
- Excludes: cache, backups, uploads, logs
This commit is contained in:
OpenCode
2026-05-29 19:07:56 +08:00
commit 09ef1f000f
6521 changed files with 867163 additions and 0 deletions

View File

@@ -0,0 +1,192 @@
import React, { useState } from 'react'
import classnames from 'classnames'
import { __ } from '@wordpress/i18n'
import { addQueryArgs } from '@wordpress/url'
import { WithRestAPIContext } from '../../hooks/useRestAPI'
import { WithSnippetsListContext, useSnippetsList } from '../../hooks/useSnippetsList'
import { SubmitSnippetAction, useSubmitSnippet } from '../../hooks/useSubmitSnippet'
import { handleUnknownError } from '../../utils/errors'
import { createSnippetObject, getSnippetType, isCondition, validateSnippet } from '../../utils/snippets/snippets'
import { WithSnippetFormContext, useSnippetForm } from '../../hooks/useSnippetForm'
import { ConfirmDialog } from '../common/ConfirmDialog'
import { UpsellDialog } from '../common/UpsellDialog'
import { EditorSidebar } from '../EditorSidebar'
import { UpsellBanner } from '../common/UpsellBanner'
import { SnippetTypeInput } from './fields/SnippetTypeInput'
import { TagsEditor } from './fields/TagsEditor'
import { CodeEditor } from './fields/CodeEditor'
import { DescriptionEditor } from './fields/DescriptionEditor'
import { NameInput } from './fields/NameInput'
import { PageHeading } from './page/PageHeading'
import type { PropsWithChildren } from 'react'
import type { Snippet } from '../../types/Snippet'
const editFormClassName = ({ snippet, isReadOnly, isExpanded }: {
snippet: Snippet,
isReadOnly: boolean,
isExpanded: boolean
}) =>
classnames(
'snippet-form',
isExpanded ? 'snippet-form-expanded' : 'snippet-form-collapsed',
`${snippet.scope}-snippet`,
`${getSnippetType(snippet)}-snippet`,
`${snippet.id ? 'saved' : 'new'}-snippet`,
`${snippet.active ? 'active' : 'inactive'}-snippet`,
{
'erroneous-snippet': !!snippet.code_error,
'read-only-snippet': isReadOnly
}
)
interface ConfirmSubmitDialogProps {
doSubmit: (action: SubmitSnippetAction | undefined) => void
submitAction: SubmitSnippetAction | undefined
setSubmitAction: (action: SubmitSnippetAction | undefined) => void
validationWarning: string | undefined
setValidationWarning: (warning: string | undefined) => void
}
const ConfirmSubmitDialog: React.FC<ConfirmSubmitDialogProps> = ({
doSubmit,
submitAction,
setSubmitAction,
validationWarning,
setValidationWarning
}) =>
<ConfirmDialog
open={validationWarning !== undefined}
title={__('Snippet incomplete', 'code-snippets')}
confirmLabel={__('Continue', 'code-snippets')}
onCancel={() => {
setSubmitAction(undefined)
setValidationWarning(undefined)
}}
onConfirm={() => {
doSubmit(submitAction)
setSubmitAction(undefined)
setValidationWarning(undefined)
}}
>
<p>{`${validationWarning} ${__('Continue?', 'code-snippets')}`}</p>
</ConfirmDialog>
interface EditFormProps extends PropsWithChildren {
className?: string
}
const EditForm: React.FC<EditFormProps> = ({ children, className }) => {
const { submitSnippet } = useSubmitSnippet()
const { snippet } = useSnippetForm()
const { refreshSnippetsList } = useSnippetsList()
const [validationWarning, setValidationWarning] = useState<string | undefined>()
const [submitAction, setSubmitAction] = useState<SubmitSnippetAction | undefined>()
const doSubmit = (action?: SubmitSnippetAction) => {
submitSnippet(action)
.then(response => {
if (response && 0 !== response.id && window.CODE_SNIPPETS) {
if (window.location.href.toString().includes(window.CODE_SNIPPETS.urls.addNew)) {
document.title = document.title
.replace(__('Add New Snippet', 'code-snippets'), __('Edit Snippet', 'code-snippets'))
.replace(__('Add New Condition', 'code-snippets'), __('Edit Condition', 'code-snippets'))
const newUrl = addQueryArgs(window.CODE_SNIPPETS.urls.edit, { id: response.id })
window.history.pushState({}, document.title, newUrl)
}
}
})
.then(refreshSnippetsList)
.catch(handleUnknownError)
}
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()
const action = Object.values(SubmitSnippetAction).find(actionName =>
actionName === document.activeElement?.getAttribute('name'))
const validationWarning = validateSnippet(snippet)
if (validationWarning) {
setValidationWarning(validationWarning)
setSubmitAction(action)
} else {
doSubmit(action)
}
}
return (
<>
<form id="snippet-form" method="post" onSubmit={handleSubmit} className={className}>
{children}
</form>
<ConfirmSubmitDialog {...{ doSubmit, submitAction, setSubmitAction, validationWarning, setValidationWarning }} />
</>
)
}
const ConditionsEditor: React.FC = () => {
const { snippet } = useSnippetForm()
return isCondition(snippet)
? <div id="snippet_conditions" className="snippet-condition-editor-container">
<p>{__('This snippet type is not supported in this version of Code Snippets.')}</p>
</div>
: null
}
const EditFormWrap: React.FC = () => {
const { snippet, isReadOnly } = useSnippetForm()
const [isExpanded, setIsExpanded] = useState(false)
const [isUpgradeDialogOpen, setIsUpgradeDialogOpen] = useState(false)
return (
<div className="wrap">
<p><small className="cs-back">
{isCondition(snippet)
? <a href={addQueryArgs(window.CODE_SNIPPETS?.urls.manage, { type: 'cond' })}>
{__('Back to all conditions', 'code-snippets')}
</a>
: <a href={window.CODE_SNIPPETS?.urls.manage}>
{__('Back to all snippets', 'code-snippets')}
</a>}
</small></p>
<PageHeading />
<EditForm className={editFormClassName({ snippet, isReadOnly, isExpanded })}>
<main className="snippet-form-upper">
<div className="snippet-name-wrapper">
<NameInput />
<SnippetTypeInput setIsUpgradeDialogOpen={setIsUpgradeDialogOpen} />
</div>
<CodeEditor {...{ isExpanded, setIsExpanded }} />
<ConditionsEditor />
</main>
<div className="snippet-form-lower">
<UpsellBanner />
<DescriptionEditor />
<TagsEditor />
</div>
<EditorSidebar setIsUpgradeDialogOpen={setIsUpgradeDialogOpen} />
</EditForm>
<UpsellDialog isOpen={isUpgradeDialogOpen} setIsOpen={setIsUpgradeDialogOpen} />
</div>
)
}
export const SnippetForm: React.FC = () =>
<WithRestAPIContext>
<WithSnippetsListContext>
<WithSnippetFormContext initialSnippet={() => createSnippetObject(window.CODE_SNIPPETS_EDIT?.snippet)}>
<EditFormWrap />
</WithSnippetFormContext>
</WithSnippetsListContext>
</WithRestAPIContext>

View File

@@ -0,0 +1,94 @@
import React, { useEffect, useRef } from 'react'
import { __ } from '@wordpress/i18n'
import { useSubmitSnippet } from '../../../hooks/useSubmitSnippet'
import { handleUnknownError } from '../../../utils/errors'
import { isMacOS } from '../../../utils/screen'
import { useSnippetForm } from '../../../hooks/useSnippetForm'
import { Button } from '../../common/Button'
import { ExpandIcon } from '../../common/icons/ExpandIcon'
import { MinimiseIcon } from '../../common/icons/MinimiseIcon'
import { CodeEditorShortcuts } from './CodeEditorShortcuts'
import type { Dispatch, RefObject, SetStateAction } from 'react'
interface EditorTextareaProps {
textareaRef: RefObject<HTMLTextAreaElement>
}
const EditorTextarea: React.FC<EditorTextareaProps> = ({ textareaRef }) => {
const { snippet, setSnippet } = useSnippetForm()
return (
<div className="snippet-editor">
<textarea
ref={textareaRef}
id="snippet-code"
name="snippet_code"
value={snippet.code}
rows={200}
spellCheck={false}
onChange={event => {
setSnippet(previous => ({ ...previous, code: event.target.value }))
}}
/>
<CodeEditorShortcuts editorTheme={window.CODE_SNIPPETS_EDIT?.editorTheme ?? 'default'} />
</div>
)
}
export interface CodeEditorProps {
isExpanded: boolean
setIsExpanded: Dispatch<SetStateAction<boolean>>
}
export const CodeEditor: React.FC<CodeEditorProps> = ({ isExpanded, setIsExpanded }) => {
const { snippet, setSnippet, codeEditorInstance, setCodeEditorInstance } = useSnippetForm()
const { submitSnippet } = useSubmitSnippet()
const textareaRef = useRef<HTMLTextAreaElement>(null)
useEffect(() => {
setCodeEditorInstance(editorInstance => {
if (textareaRef.current && !editorInstance) {
editorInstance = window.wp.codeEditor.initialize(textareaRef.current)
editorInstance.codemirror.on('changes', instance => {
setSnippet(previous => ({ ...previous, code: instance.getValue() }))
})
}
return editorInstance
})
}, [setCodeEditorInstance, textareaRef, setSnippet])
useEffect(() => {
if (codeEditorInstance) {
const extraKeys = codeEditorInstance.codemirror.getOption('extraKeys') ?? {}
const controlKey = isMacOS() ? 'Cmd' : 'Ctrl'
const onSave = () => {
submitSnippet()
.then(() => undefined)
.catch(handleUnknownError)
}
codeEditorInstance.codemirror.setOption('extraKeys', {
...'object' === typeof extraKeys ? extraKeys : undefined,
[`${controlKey}-S`]: onSave,
[`${controlKey}-Enter`]: onSave
})
}
}, [submitSnippet, codeEditorInstance, snippet])
return (
<div className="snippet-code-container">
<div className="above-snippet-code">
<h2><label htmlFor="snippet-code">{__('Snippet Content', 'code-snippets')}</label></h2>
<Button small className="expand-editor-button" onClick={() => setIsExpanded(current => !current)}>
{isExpanded ? <MinimiseIcon /> : <ExpandIcon />}
{isExpanded ? __('Minimize', 'code-snippets') : __('Expand', 'code-snippets')}
</Button>
</div>
<EditorTextarea textareaRef={textareaRef} />
</div>
)
}

View File

@@ -0,0 +1,123 @@
import { __, _x } from '@wordpress/i18n'
import classnames from 'classnames'
import React from 'react'
import { KEYBOARD_KEYS } from '../../../types/KeyboardShortcut'
import { isMacOS } from '../../../utils/screen'
import type { KeyboardKey, KeyboardShortcut } from '../../../types/KeyboardShortcut'
const shortcuts: Record<string, KeyboardShortcut> = {
saveChanges: {
label: __('Save changes', 'code-snippets'),
mod: 'Cmd',
key: 'S'
},
selectAll: {
label: __('Select all', 'code-snippets'),
mod: 'Cmd',
key: 'A'
},
beginSearch: {
label: __('Begin searching', 'code-snippets'),
mod: 'Cmd',
key: 'F'
},
findNext: {
label: __('Find next', 'code-snippets'),
mod: 'Cmd',
key: 'G'
},
findPrevious: {
label: __('Find previous', 'code-snippets'),
mod: ['Shift', 'Cmd'],
key: 'G'
},
replace: {
label: __('Replace', 'code-snippets'),
mod: ['Shift', 'Cmd'],
key: 'F'
},
replaceAll: {
label: __('Replace all', 'code-snippets'),
mod: ['Shift', 'Cmd', 'Option'],
key: 'R'
},
search: {
label: __('Persistent search', 'code-snippets'),
mod: 'Alt',
key: 'F'
},
toggleComment: {
label: __('Toggle comment', 'code-snippets'),
mod: 'Cmd',
key: '/'
},
swapLineUp: {
label: __('Swap line up', 'code-snippets'),
mod: 'Option',
key: 'Up'
},
swapLineDown: {
label: __('Swap line down', 'code-snippets'),
mod: 'Option',
key: 'Down'
},
autoIndent: {
label: __('Auto-indent current line or selection', 'code-snippets'),
mod: 'Shift',
key: 'Tab'
}
}
const SEP = _x('-', 'keyboard shortcut separator', 'code-snippets')
const ModifierKey: React.FC<{ modifier: KeyboardKey }> = ({ modifier }) => {
switch (modifier) {
case 'Ctrl':
case 'Cmd':
return (
<>
<kbd className="pc-key">{KEYBOARD_KEYS.Ctrl}</kbd>
<kbd className="mac-key">{KEYBOARD_KEYS.Cmd}</kbd>
{SEP}
</>
)
case 'Option':
return (
<span className="mac-key">
<kbd className="mac-key">{KEYBOARD_KEYS.Option}</kbd>{SEP}
</span>
)
default:
return <><kbd>{KEYBOARD_KEYS[modifier]}</kbd>{SEP}</>
}
}
export interface CodeEditorShortcutsProps {
editorTheme: string
}
export const CodeEditorShortcuts: React.FC<CodeEditorShortcutsProps> = ({ editorTheme }) =>
<div className="snippet-editor-help tooltip tooltip-inline tooltip-start">
<span className={`dashicons dashicons-editor-help cm-s-${editorTheme}`}></span>
<div className={classnames('tooltip-content', { 'platform-mac': isMacOS() })}>
<table>
<tbody>
{Object.entries(shortcuts).map(([name, { label, mod, key }]) =>
<tr key={name}>
<td>{label}</td>
<td>
{(Array.isArray(mod) ? mod : [mod]).map(modifier =>
<span key={modifier}>
<ModifierKey modifier={modifier} />
</span>
)}
<kbd>{KEYBOARD_KEYS[key]}</kbd>
</td>
</tr>)}
</tbody>
</table>
</div>
</div>

View File

@@ -0,0 +1,88 @@
import React, { useCallback, useEffect } from 'react'
import { __ } from '@wordpress/i18n'
import domReady from '@wordpress/dom-ready'
import { useSnippetForm } from '../../../hooks/useSnippetForm'
export const EDITOR_ID = 'snippet_description'
const TOOLBAR_BUTTONS = [
[
'bold',
'italic',
'underline',
'strikethrough',
'blockquote',
'bullist',
'numlist',
'alignleft',
'aligncenter',
'alignright',
'link',
'wp_adv',
'code_snippets'
],
[
'formatselect',
'forecolor',
'pastetext',
'removeformat',
'charmap',
'outdent',
'indent',
'undo',
'redo',
'spellchecker'
]
]
const initializeEditor = (onChange: (content: string) => void) => {
window.wp.editor?.initialize(EDITOR_ID, {
mediaButtons: window.CODE_SNIPPETS_EDIT?.descEditorOptions.mediaButtons,
quicktags: true,
tinymce: {
toolbar: TOOLBAR_BUTTONS.map(line => line.join(' ')),
setup: editor => {
editor.on('change', () => onChange(editor.getContent()))
}
}
})
}
const DescriptionEditorTextarea: React.FC = () => {
const { snippet, setSnippet, isReadOnly } = useSnippetForm()
const handleChange = useCallback(
(desc: string) => setSnippet(previous => ({ ...previous, desc })),
[setSnippet]
)
useEffect(() => {
domReady(() => initializeEditor(handleChange))
}, [handleChange])
return (
<textarea
id={EDITOR_ID}
className="wp-editor-area"
onChange={event => handleChange(event.target.value)}
autoComplete="off"
disabled={isReadOnly}
rows={window.CODE_SNIPPETS_EDIT?.descEditorOptions.rows}
cols={40}
value={snippet.desc}
/>
)
}
export const DescriptionEditor: React.FC = () =>
window.CODE_SNIPPETS_EDIT?.enableDescription
? <div className="snippet-description-container">
<h2>
<label htmlFor={EDITOR_ID}>
{__('Description', 'code-snippets')}
</label>
</h2>
<DescriptionEditorTextarea />
</div>
: null

View File

@@ -0,0 +1,28 @@
import React from 'react'
import { __ } from '@wordpress/i18n'
import { useSnippetForm } from '../../../hooks/useSnippetForm'
export const NameInput: React.FC = () => {
const { snippet, setSnippet, isReadOnly } = useSnippetForm()
return (
<div id="titlediv">
<div id="titlewrap">
<label htmlFor="title" className="screen-reader-text">
{__('Name', 'code-snippets')}
</label>
<input
id="title"
type="text"
name="snippet_name"
autoComplete="off"
value={snippet.name}
disabled={isReadOnly}
placeholder={__('Enter snippet title', 'code-snippets')}
onChange={event =>
setSnippet(previous => ({ ...previous, name: event.target.value }))}
/>
</div>
</div>
)
}

View File

@@ -0,0 +1,72 @@
import { __ } from '@wordpress/i18n'
import React from 'react'
import Select from 'react-select'
import { useSnippetForm } from '../../../hooks/useSnippetForm'
import { SNIPPET_TYPE_SCOPES } from '../../../types/Snippet'
import { getSnippetType, isCondition } from '../../../utils/snippets/snippets'
import type { SnippetCodeScope } from '../../../types/Snippet'
import type { SelectOption } from '../../../types/SelectOption'
const SCOPE_ICONS: Record<SnippetCodeScope, string> = {
'global': 'admin-site',
'admin': 'admin-tools',
'front-end': 'admin-appearance',
'single-use': 'clock',
'content': 'shortcode',
'head-content': 'editor-code',
'footer-content': 'editor-code',
'admin-css': 'dashboard',
'site-css': 'admin-customizer',
'site-head-js': 'media-code',
'site-footer-js': 'media-code'
}
const SCOPE_DESCRIPTIONS: Record<SnippetCodeScope, string> = {
'global': __('Run everywhere', 'code-snippets'),
'admin': __('Only run in administration area', 'code-snippets'),
'front-end': __('Only run on site front-end', 'code-snippets'),
'single-use': __('Only run once', 'code-snippets'),
'content': __('Where inserted in editor', 'code-snippets'),
'head-content': __('In site <head> section', 'code-snippets'),
'footer-content': __('In site footer (end of <body>)', 'code-snippets'),
'site-css': __('Site front-end', 'code-snippets'),
'admin-css': __('Administration area', 'code-snippets'),
'site-footer-js': __('In site footer (end of <body>)', 'code-snippets'),
'site-head-js': __('In site <head> section', 'code-snippets')
}
export const SnippetLocationInput: React.FC = () => {
const { snippet, setSnippet, isReadOnly } = useSnippetForm()
const options: SelectOption<SnippetCodeScope>[] = SNIPPET_TYPE_SCOPES[getSnippetType(snippet)]
.filter(scope => 'condition' !== scope)
.map(scope => ({
key: scope,
value: scope,
label: SCOPE_DESCRIPTIONS[scope]
}))
return isCondition(snippet)
? null
: <div className="block-form-field">
<h4><label htmlFor="snippet-location">{__('Location', 'code-snippets')}</label></h4>
<Select
inputId="snippet-location"
className="code-snippets-select code-snippets-select-location"
options={options}
isDisabled={isReadOnly}
styles={{
menu: provided => ({ ...provided, zIndex: 9999 }),
input: provided => ({ ...provided, ':focus': { boxShadow: 'none' } })
}}
value={options.find(option => option.value === snippet.scope)}
formatOptionLabel={({ label, value }) =>
<>
<span className={`dashicons dashicons-${SCOPE_ICONS[value]}`}></span>{` ${label}`}
</>
}
onChange={option =>
option?.value && setSnippet(previous => ({ ...previous, scope: option.value }))}
/>
</div>
}

View File

@@ -0,0 +1,106 @@
import React, { useEffect } from 'react'
import classnames from 'classnames'
import { __, _x } from '@wordpress/i18n'
import Select from 'react-select'
import { useSnippetForm } from '../../../hooks/useSnippetForm'
import { SNIPPET_TYPE_SCOPES } from '../../../types/Snippet'
import { isLicensed } from '../../../utils/screen'
import { getSnippetType, isProType } from '../../../utils/snippets/snippets'
import { Badge } from '../../common/Badge'
import type { FormatOptionLabelContext } from 'react-select'
import type { Dispatch, SetStateAction } from 'react'
import type { SnippetCodeType, SnippetType } from '../../../types/Snippet'
import type { SelectOption } from '../../../types/SelectOption'
import type { EditorConfiguration } from 'codemirror'
export interface SnippetTypeInputProps {
setIsUpgradeDialogOpen: Dispatch<SetStateAction<boolean>>
}
const EDITOR_MODES: Record<SnippetCodeType, string> = {
css: 'text/css',
js: 'javascript',
php: 'text/x-php',
html: 'application/x-httpd-php'
}
const OPTIONS: SelectOption<SnippetType>[] = [
{ value: 'php', label: __('Functions', 'code-snippets') },
{ value: 'html', label: __('Content', 'code-snippets') },
{ value: 'css', label: __('Styles', 'code-snippets') },
{ value: 'js', label: __('Scripts', 'code-snippets') },
{ value: 'cond', label: __('Conditions', 'code-snippets') }
]
interface SnippetTypeOptionProps {
option: SelectOption<SnippetType>
context: FormatOptionLabelContext
}
const SnippetTypeOption: React.FC<SnippetTypeOptionProps> = ({ option: { value, label }, context }) =>
<div className={classnames('snippet-type-option', { 'inverted-badges': isProType(value) && !isLicensed() })}>
{'menu' === context
? <div>
{label}
{isProType(value) && !isLicensed()
? <Badge name="pro" small>{_x('Pro', 'Upgrade to Pro', 'code-snippets')}</Badge>
: null}
</div>
: null}
<Badge name={value} />
</div>
export const SnippetTypeInput: React.FC<SnippetTypeInputProps> = ({ setIsUpgradeDialogOpen }) => {
const { snippet, setSnippet, codeEditorInstance, isReadOnly } = useSnippetForm()
const snippetType = getSnippetType(snippet)
useEffect(() => {
if (codeEditorInstance) {
const codeEditor = codeEditorInstance.codemirror
codeEditor.setOption('lint' as keyof EditorConfiguration, 'php' === snippetType || 'css' === snippetType)
if ('cond' !== snippetType && EDITOR_MODES[snippetType]) {
codeEditor.setOption('mode', EDITOR_MODES[snippetType])
codeEditor.refresh()
}
}
}, [codeEditorInstance, snippetType])
return (
<div className="snippet-type-container">
<label htmlFor="snippet-type-select-input" className="screen-reader-text">
{__('Snippet Type', 'code-snippets')}
</label>
<Select
inputId="snippet-type-select-input"
className="code-snippets-select"
isDisabled={isReadOnly}
options={0 === snippet.id ? OPTIONS : OPTIONS.filter(option => 'cond' !== option.value)}
menuPlacement="bottom"
styles={{
menu: provided => ({
...provided,
zIndex: 9999,
width: 'max-content',
minWidth: '100%'
}),
input: provided => ({ ...provided, boxShadow: 'none' })
}}
value={OPTIONS.find(option => option.value === snippetType)}
formatOptionLabel={(data, meta) =>
<SnippetTypeOption option={data} context={meta.context} />}
onChange={option => {
if (option && isProType(option.value) && !isLicensed()) {
setIsUpgradeDialogOpen(true)
} else if (option) {
setSnippet(previous => ({
...previous,
scope: SNIPPET_TYPE_SCOPES[option.value][0]
}))
}
}}
/>
</div>
)
}

View File

@@ -0,0 +1,31 @@
import React from 'react'
import { __ } from '@wordpress/i18n'
import { FormTokenField } from '@wordpress/components'
import { useSnippetForm } from '../../../hooks/useSnippetForm'
const options = window.CODE_SNIPPETS_EDIT?.tagOptions
export const TagsEditor: React.FC = () => {
const { snippet, setSnippet, isReadOnly } = useSnippetForm()
return options?.enabled
? <div className="snippet-tags-container">
<h3><label htmlFor="components-form-token-input-0">{__('Snippet Tags', 'code-snippets')}</label></h3>
<FormTokenField
label=""
value={snippet.tags}
disabled={isReadOnly}
suggestions={options.availableTags}
tokenizeOnBlur
tokenizeOnSpace={!options.allowSpaces}
onChange={tokens => {
setSnippet(previous => ({
...previous,
tags: tokens.map(token => 'string' === typeof token ? token : token.value)
}))
}}
/>
</div>
: null
}

View File

@@ -0,0 +1 @@
export * from './SnippetForm'

View File

@@ -0,0 +1,34 @@
import { createInterpolateElement } from '@wordpress/element'
import React from 'react'
import { __, sprintf } from '@wordpress/i18n'
import { useSnippetForm } from '../../../hooks/useSnippetForm'
import { DismissibleNotice } from '../../common/DismissableNotice'
export const Notices: React.FC = () => {
const { currentNotice, setCurrentNotice, snippet, setSnippet } = useSnippetForm()
return <>
{currentNotice
? <DismissibleNotice className={currentNotice[0]} onDismiss={() => setCurrentNotice(undefined)}>
<p>{createInterpolateElement(currentNotice[1], { strong: <strong /> })}</p>
</DismissibleNotice>
: null}
{snippet.code_error
? <DismissibleNotice
className="notice-error"
onDismiss={() => setSnippet(previous => ({ ...previous, code_error: null }))}
>
<p>
<strong>{sprintf(
// translators: %d: line number.
__('Snippet automatically deactivated due to an error on line %d:', 'code-snippets'),
snippet.code_error[1]
)}</strong>
<blockquote>{snippet.code_error[0]}</blockquote>
</p>
</DismissibleNotice>
: null}
</>
}

View File

@@ -0,0 +1,52 @@
import { __, _x } from '@wordpress/i18n'
import React from 'react'
import { useSnippetForm } from '../../../hooks/useSnippetForm'
import { createSnippetObject } from '../../../utils/snippets/snippets'
import type { Snippet } from '../../../types/Snippet'
const OPTIONS = window.CODE_SNIPPETS_EDIT
const getAddNewHeading = (snippet: Snippet): string =>
'condition' === snippet.scope
? __('Add New Condition', 'code-snippets')
: __('Add New Snippet', 'code-snippets')
export const PageHeading: React.FC = () => {
const { snippet, updateSnippet, setCurrentNotice } = useSnippetForm()
return (
<h1>
{snippet.id
? <>
{`${'condition' === snippet.scope
? __('Edit Condition', 'code-snippets')
: __('Edit Snippet', 'code-snippets')} `}
<a
href={window.CODE_SNIPPETS?.urls.addNew}
className="page-title-action"
onClick={event => {
event.preventDefault()
updateSnippet(({ scope }) => createSnippetObject({ scope }))
setCurrentNotice(undefined)
window.document.title = window.document.title
.replace(__('Edit Snippet', 'code-snippets'), getAddNewHeading(snippet))
.replace(__('Edit Condition', 'code-snippets'), getAddNewHeading(snippet))
window.history.pushState({}, '', window.CODE_SNIPPETS?.urls.addNew)
}}
>
{_x('Add New', 'snippet', 'code-snippets')}
</a>
</>
: getAddNewHeading(snippet)}
{OPTIONS?.pageTitleActions && Object.entries(OPTIONS.pageTitleActions).map(([label, url]) =>
<>
<a key={label} href={url} className="page-title-action">{label}</a>{' '}
</>
)}
</h1>
)
}