added Welcome page

changed settings API and page to React + rest
changed structure of scripts - different folders for editor and admin scripts
This commit is contained in:
Nikita 2023-08-05 16:01:44 +03:00
parent 5f489c933e
commit f1c3c698dc
66 changed files with 978 additions and 1286 deletions

View file

@ -18,6 +18,8 @@ class Mind_Admin {
*/ */
public function __construct() { public function __construct() {
add_action( 'admin_menu', [ $this, 'register_admin_menu' ], 20 ); add_action( 'admin_menu', [ $this, 'register_admin_menu' ], 20 );
add_filter( 'admin_body_class', [ $this, 'admin_body_class' ] );
} }
/** /**
@ -35,11 +37,60 @@ class Mind_Admin {
esc_html__( 'Mind', 'mind' ), esc_html__( 'Mind', 'mind' ),
'manage_options', 'manage_options',
'mind', 'mind',
[ 'Mind_Settings', 'print_settings_page' ], [ $this, 'print_admin_page' ],
// phpcs:ignore // phpcs:ignore
'data:image/svg+xml;base64,' . base64_encode( file_get_contents( mind()->plugin_path . 'assets/images/admin-icon.svg' ) ), 'data:image/svg+xml;base64,' . base64_encode( file_get_contents( mind()->plugin_path . 'assets/images/admin-icon.svg' ) ),
'58.7' '58.7'
); );
add_submenu_page(
'mind',
'',
esc_html__( 'Welcome', 'mind' ),
'manage_options',
'mind'
);
add_submenu_page(
'mind',
'',
esc_html__( 'Settings', 'mind' ),
'manage_options',
'admin.php?page=mind&sub_page=settings'
);
}
/**
* Print admin page.
*/
public function print_admin_page() {
?>
<div class="mind-admin-root"></div>
<?php
}
/**
* Add page class to body.
*
* @param string $classes - body classes.
*/
public function admin_body_class( $classes ) {
$screen = get_current_screen();
if ( 'toplevel_page_mind' !== $screen->id ) {
return $classes;
}
$page_name = 'welcome';
// phpcs:ignore
if ( isset( $_GET['sub_page'] ) && $_GET['sub_page'] ) {
// phpcs:ignore
$page_name = $_GET['sub_page'];
}
$classes .= ' mind-admin-page mind-admin-page-' . esc_attr( $page_name );
return $classes;
} }
} }

View file

@ -18,6 +18,7 @@ class Mind_Assets {
*/ */
public function __construct() { public function __construct() {
add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_block_editor_assets' ] ); add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_block_editor_assets' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] );
} }
/** /**
@ -45,12 +46,14 @@ class Mind_Assets {
* Enqueue editor assets * Enqueue editor assets
*/ */
public function enqueue_block_editor_assets() { public function enqueue_block_editor_assets() {
$openai_key = Mind_Settings::get_option( 'openai_key', 'mind_general' ); $settings = get_option( 'mind_settings', array() );
$asset_data = $this->get_asset_file( 'build/index' );
$openai_key = $settings['openai_api_key'] ?? '';
$asset_data = $this->get_asset_file( 'build/editor' );
wp_enqueue_script( wp_enqueue_script(
'mind-editor', 'mind-editor',
mind()->plugin_url . 'build/index.js', mind()->plugin_url . 'build/editor.js',
$asset_data['dependencies'], $asset_data['dependencies'],
$asset_data['version'], $asset_data['version'],
true true
@ -59,15 +62,52 @@ class Mind_Assets {
wp_localize_script( wp_localize_script(
'mind-editor', 'mind-editor',
'mindData', 'mindData',
array( [
'connected' => ! ! $openai_key, 'connected' => ! ! $openai_key,
'settingsPageURL' => admin_url( 'admin.php?page=mind' ), 'settingsPageURL' => admin_url( 'admin.php?page=mind&sub_page=settings' ),
) ]
); );
wp_enqueue_style( wp_enqueue_style(
'mind-editor', 'mind-editor',
mind()->plugin_url . 'build/style-index.css', mind()->plugin_url . 'build/style-editor.css',
[],
$asset_data['version']
);
}
/**
* Enqueue admin pages assets.
*/
public function admin_enqueue_scripts() {
$screen = get_current_screen();
if ( 'toplevel_page_mind' !== $screen->id ) {
return;
}
$asset_data = $this->get_asset_file( 'build/admin' );
wp_enqueue_script(
'mind-admin',
mind()->plugin_url . 'build/admin.js',
$asset_data['dependencies'],
$asset_data['version'],
true
);
wp_localize_script(
'mind-admin',
'mindAdminData',
[
'settings' => get_option( 'mind_settings', array() ),
]
);
wp_enqueue_style(
'mind-admin',
mind()->plugin_url . 'build/style-admin.css',
[], [],
$asset_data['version'] $asset_data['version']
); );

View file

@ -40,7 +40,18 @@ class Mind_Rest extends WP_REST_Controller {
public function register_routes() { public function register_routes() {
$namespace = $this->namespace . $this->version; $namespace = $this->namespace . $this->version;
// Get layouts list. // Update Settings.
register_rest_route(
$namespace,
'/update_settings/',
[
'methods' => [ 'POST' ],
'callback' => [ $this, 'update_settings' ],
'permission_callback' => [ $this, 'update_settings_permission' ],
]
);
// Request OpenAI API.
register_rest_route( register_rest_route(
$namespace, $namespace,
'/request_ai/', '/request_ai/',
@ -52,6 +63,19 @@ class Mind_Rest extends WP_REST_Controller {
); );
} }
/**
* Get edit options permissions.
*
* @return bool
*/
public function update_settings_permission() {
if ( ! current_user_can( 'manage_options' ) ) {
return $this->error( 'user_dont_have_permission', __( 'User don\'t have permissions to change options.', 'mind' ), true );
}
return true;
}
/** /**
* Get permissions for OpenAI api request. * Get permissions for OpenAI api request.
* *
@ -65,6 +89,24 @@ class Mind_Rest extends WP_REST_Controller {
return true; return true;
} }
/**
* Update Settings.
*
* @param WP_REST_Request $req request object.
*
* @return mixed
*/
public function update_settings( WP_REST_Request $req ) {
$new_settings = $req->get_param( 'settings' );
if ( is_array( $new_settings ) ) {
$current_settings = get_option( 'mind_settings', [] );
update_option( 'mind_settings', array_merge( $current_settings, $new_settings ) );
}
return $this->success( true );
}
/** /**
* Send request to OpenAI. * Send request to OpenAI.
* *
@ -73,9 +115,11 @@ class Mind_Rest extends WP_REST_Controller {
* @return mixed * @return mixed
*/ */
public function request_ai( WP_REST_Request $req ) { public function request_ai( WP_REST_Request $req ) {
$openai_key = Mind_Settings::get_option( 'openai_key', 'mind_general' ); $settings = get_option( 'mind_settings', array() );
$request = $req->get_param( 'request' ) ?? ''; $openai_key = $settings['openai_api_key'] ?? '';
$context = $req->get_param( 'context' ) ?? '';
$request = $req->get_param( 'request' ) ?? '';
$context = $req->get_param( 'context' ) ?? '';
if ( ! $openai_key ) { if ( ! $openai_key ) {
return $this->error( 'no_openai_key_found', __( 'Provide OpenAI key in the plugin settings.', 'mind' ) ); return $this->error( 'no_openai_key_found', __( 'Provide OpenAI key in the plugin settings.', 'mind' ) );

View file

@ -1,209 +0,0 @@
<?php
/**
* Plugin Settings
*
* @package mind
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
require_once mind()->plugin_path . 'vendors/class-settings-api.php';
/**
* Mind Settings Class
*/
class Mind_Settings {
/**
* Settings API instance
*
* @var object
*/
public static $settings_api;
/**
* Cached settings fields. We call settings fields method a lot of times to get default values.
* So, for performance reasons we need to cache the output.
*
* @var object
*/
public static $cached_settings_fields;
/**
* Mind_Settings constructor.
*/
public function __construct() {
self::init_actions();
}
/**
* Get Option Value
*
* @param string $option - option name.
* @param string $section - section name.
*
* @return bool|string
*/
public static function get_option( $option, $section ) {
$options = get_option( $section );
$result = '';
if ( isset( $options[ $option ] ) ) {
$result = $options[ $option ];
} else {
// find default.
$fields = self::get_settings_fields();
if ( isset( $fields[ $section ] ) && is_array( $fields[ $section ] ) ) {
foreach ( $fields[ $section ] as $field_data ) {
if ( $option === $field_data['name'] && isset( $field_data['default'] ) ) {
$result = $field_data['default'];
}
}
}
}
return 'off' === $result ? false : ( 'on' === $result ? true : $result );
}
/**
* Update Option Value
*
* @param string $option - option name.
* @param string $section - section name.
* @param string $value - new option value.
*/
public static function update_option( $option, $section, $value ) {
$options = get_option( $section );
if ( ! is_array( $options ) ) {
$options = [];
}
$options[ $option ] = $value;
update_option( $section, $options );
}
/**
* Init actions
*/
public static function init_actions() {
self::$settings_api = new Mind_Settings_API();
add_action( 'admin_init', [ __CLASS__, 'admin_init' ] );
}
/**
* Initialize the settings
*
* @return void
*/
public static function admin_init() {
// set the settings.
self::$settings_api->set_sections( self::get_settings_sections() );
self::$settings_api->set_fields( self::get_settings_fields() );
// initialize settings.
self::$settings_api->admin_init();
}
/**
* Plugin settings sections
*
* @return array
*/
public static function get_settings_sections() {
$sections = [
[
'id' => 'mind_general',
'title' => esc_html__( 'General', 'mind' ),
'icon' => '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" /></svg>',
],
];
return apply_filters( 'mind_settings_sections', $sections );
}
/**
* Returns all the settings fields
*
* @return array settings fields
*/
public static function get_settings_fields() {
if ( ! empty( self::$cached_settings_fields ) ) {
return self::$cached_settings_fields;
}
// retrieve openai key from the DB.
// we can't use Mind Settings API as it will result a stack trace error.
$openai_key = false;
$general_settings = get_option( 'mind_general' );
if ( isset( $general_settings['openai_key'] ) ) {
$openai_key = $general_settings['openai_key'];
}
$settings_fields = [
'mind_general' => [
[
'name' => 'openai_key',
'label' => esc_html__( 'OpenAI API Key', 'mind' ),
'desc' => esc_html__( 'This setting is required, since our plugin works with OpenAI.', 'mind' ) . ' <a href="https://platform.openai.com/account/api-keys" target="_blank">Create API key</a>',
'type' => $openai_key ? 'password' : 'text',
'default' => '',
],
],
];
self::$cached_settings_fields = apply_filters( 'mind_settings_fields', $settings_fields );
return self::$cached_settings_fields;
}
/**
* The plugin page handler
*
* @return void
*/
public static function print_settings_page() {
self::$settings_api->admin_enqueue_scripts();
echo '<div class="wrap">';
echo '<h2>' . esc_html__( 'Settings', 'mind' ) . '</h2>';
self::$settings_api->show_navigation();
self::$settings_api->show_forms();
echo '</div>';
?>
<script>
(function( $ ) {
// Don't allow adding input number values that > then max attribute and < min attribute.
$('form').on('input', '[type="number"]', function(e) {
var current = parseFloat( this.value );
var min = parseFloat(this.min);
var max = parseFloat(this.max);
if ('' !== this.value) {
if (!Number.isNaN(min) && current < min) {
this.value = min;
}
if (!Number.isNaN(max) && current > max) {
this.value = max;
}
}
});
<?php if ( ! class_exists( 'Mind_Pro' ) ) : ?>
// disable pro inputs.
$('.mind-settings-control-pro').find('input, textarea').attr('disabled', 'disabled');
<?php endif; ?>
})(jQuery);
</script>
<?php
}
}
new Mind_Settings();

View file

@ -114,7 +114,6 @@ class Mind {
* Include dependencies * Include dependencies
*/ */
private function include_dependencies() { private function include_dependencies() {
require_once $this->plugin_path . 'classes/class-settings.php';
require_once $this->plugin_path . 'classes/class-admin.php'; require_once $this->plugin_path . 'classes/class-admin.php';
require_once $this->plugin_path . 'classes/class-assets.php'; require_once $this->plugin_path . 'classes/class-assets.php';
require_once $this->plugin_path . 'classes/class-rest.php'; require_once $this->plugin_path . 'classes/class-rest.php';

5
src/_variables.scss Normal file
View file

@ -0,0 +1,5 @@
:root {
--mind-brand-color: #e455df;
--mind-brand-darken-color: #bb56df;
--mind-brand-color-2: #4376ec;
}

125
src/admin/index.js Normal file
View file

@ -0,0 +1,125 @@
/**
* Styles
*/
import './style.scss';
/**
* External dependencies
*/
import clsx from 'clsx';
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { render, useEffect } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
/**
* Internal dependencies
*/
import './store/admin';
import './store/settings';
import pages from './pages';
import { ReactComponent as MindLogoIcon } from '../icons/mind-logo.svg';
function PageWrapper() {
const { setActivePage } = useDispatch('mind/admin');
const { activePage } = useSelect((select) => {
const { getActivePage } = select('mind/admin');
return {
activePage: getActivePage(),
};
});
useEffect(() => {
// disable active links.
document
.querySelectorAll('.toplevel_page_mind .current')
.forEach(($el) => {
$el.classList.remove('current');
});
// find new active link.
let $links = document.querySelectorAll(
`.toplevel_page_mind [href="admin.php?page=mind&sub_page=${activePage}"]`
);
if (!$links || !$links.length) {
$links = document.querySelectorAll(
'.toplevel_page_mind [href="admin.php?page=mind"]'
);
}
$links.forEach(($link) => {
$link.parentNode.classList.add('current');
});
// Change body class.
document.body.classList.forEach((className) => {
if (/mind-admin-page-/.test(className)) {
document.body.classList.remove(className);
}
});
document.body.classList.add(`mind-admin-page-${activePage}`);
// change address bar link
if ($links && $links.length) {
window.history.pushState(
document.title,
document.title,
$links[0].href
);
}
}, [activePage]);
const resultTabs = [];
let resultContent = '';
Object.keys(pages).forEach((k) => {
resultTabs.push(
<li key={k}>
{/* eslint-disable-next-line react/button-has-type */}
<button
className={clsx(
'mind-admin-tabs-button',
activePage === k && 'mind-admin-tabs-button-active'
)}
onClick={() => {
setActivePage(k);
}}
>
{pages[k].label}
</button>
</li>
);
});
if (activePage && pages[activePage]) {
const NewBlock = pages[activePage].block;
resultContent = <NewBlock />;
}
return (
<>
<div className="mind-admin-head">
<div className="mind-admin-head-container">
<div className="mind-admin-head-logo">
<MindLogoIcon />
<h1>{__('Mind', 'mind')}</h1>
</div>
<ul className="mind-admin-tabs">{resultTabs}</ul>
</div>
</div>
<div className="mind-admin-content">{resultContent}</div>
</>
);
}
window.addEventListener('load', () => {
render(<PageWrapper />, document.querySelector('.mind-admin-root'));
});

View file

@ -0,0 +1,99 @@
/**
* Styles
*/
import './style.scss';
/**
* WordPress dependencies
*/
// eslint-disable-next-line import/no-extraneous-dependencies
import { isEqual } from 'lodash';
import { useState, useEffect } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import { ReactComponent as LoadingIcon } from '../../icons/loading.svg';
export default function PageSettings() {
const [pendingSettings, setPendingSettings] = useState({});
const [settingsChanged, setSettingsChanged] = useState(false);
const { updateSettings } = useDispatch('mind/settings');
const { settings, updating, error } = useSelect((select) => {
const settingsSelect = select('mind/settings');
return {
settings: settingsSelect.getSettings(),
updating: settingsSelect.getUpdating(),
error: settingsSelect.getError(),
};
});
// Update pending settings from actual settings object.
useEffect(() => {
setPendingSettings(settings);
}, [settings]);
// Check if settings changed.
useEffect(() => {
setSettingsChanged(!isEqual(settings, pendingSettings));
}, [settings, pendingSettings]);
return (
<>
<div className="mind-admin-settings-card">
<div className="mind-admin-settings-card-name">
<label htmlFor="mind-settings-openai-api-key">
{__('OpenAI API Key', 'mind')}
</label>
<div className="mind-admin-settings-card-description">
{__(
'This setting is required, since our plugin works with OpenAI.',
'mind'
)}{' '}
<a
href="https://platform.openai.com/account/api-keys"
target="_blank"
rel="noreferrer"
>
{__('Create API key', 'mind')}
</a>
</div>
</div>
<div className="mind-admin-settings-card-input">
<input
id="mind-settings-openai-api-key"
type="text"
placeholder={__('Enter API key', 'mind')}
value={pendingSettings.openai_api_key || ''}
onChange={(e) => {
e.preventDefault();
setPendingSettings({
...pendingSettings,
openai_api_key: e.target.value,
});
}}
/>
</div>
</div>
{error && <div className="mind-admin-settings-error">{error}</div>}
<div className="mind-admin-settings-actions">
<button
disabled={!settingsChanged}
onClick={(e) => {
e.preventDefault();
updateSettings(pendingSettings);
}}
>
{__('Save Changes', 'mind')}
{updating && <LoadingIcon viewBox="0 0 24 24" />}
</button>
</div>
</>
);
}

View file

@ -0,0 +1,102 @@
.mind-admin-page-settings .mind-admin-content {
max-width: 700px;
}
.mind-admin-settings-card {
display: flex;
gap: 20px;
.mind-admin-settings-card-name {
flex: 1;
max-width: 250px;
label {
display: block;
margin-top: 8px;
font-size: 18px;
font-weight: 300;
}
}
.mind-admin-settings-card-description {
font-size: 12px;
margin-top: 16px;
color: #646464;
}
.mind-admin-settings-card-input {
flex: 1;
input {
width: 100%;
padding: 6px 15px;
font-size: 1em;
border: 1px solid #000;
border-radius: 7px;
&:focus {
box-shadow: 0 0 0 2px rgba(#000, 30%);
outline: 2px solid transparent;
}
}
}
}
.mind-admin-settings-error {
padding: 20px;
margin-top: 20px;
margin-bottom: -20px;
background-color: #faf4f4;
color: #e74f4f;
border-left: 3px solid #e74f4f;
}
.mind-admin-settings-actions {
margin-top: 50px;
button {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 10px 18px;
border-radius: 7px;
background-color: #000;
border: 1px solid #000;
color: #fff;
cursor: pointer;
&:hover,
&:focus {
background-color: #303030;
}
&:focus {
box-shadow: 0 0 0 2px rgba(#000, 30%);
outline: 2px solid transparent;
}
&:disabled {
border: 1px solid #bfbfbf;
background-color: #dcdcdc;
color: #000;
opacity: 0.3;
pointer-events: none;
}
svg {
width: 20px;
height: auto;
margin: -5px;
margin-left: 0;
animation: 1s mind-settings-loading-icon infinite linear;
}
}
}
@keyframes mind-settings-loading-icon {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}

View file

@ -0,0 +1,45 @@
/**
* Styles
*/
import './style.scss';
/**
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { useDispatch } from '@wordpress/data';
export default function PageWelcome() {
const { setActivePage } = useDispatch('mind/admin');
return (
<>
<p
dangerouslySetInnerHTML={{
__html: sprintf(
// translators: %s - Mind logo.
__('Hello, my name is %s', 'mind'),
`<span class="mind-inline-logo">Mind</span>`
),
}}
/>
<p>
{__(
'I am an AI assistant designed to help you in writing content for your blog',
'mind'
)}
</p>
<div>
{__('To get started, enter your', 'mind')}
<button
onClick={(e) => {
e.preventDefault();
setActivePage('settings');
}}
>
{__('OpenAI API key →', 'mind')}
</button>
</div>
</>
);
}

View file

@ -0,0 +1,49 @@
.mind-admin-page-welcome {
.mind-inline-logo {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 12px;
vertical-align: bottom;
letter-spacing: 0;
font-size: 0.95em;
// Gradient.
@supports (-webkit-background-clip: text) {
background: linear-gradient(to right, var(--mind-brand-color), var(--mind-brand-color-2));
background-clip: text;
-webkit-text-fill-color: transparent;
}
}
.mind-admin-content {
display: flex;
flex-direction: column;
gap: 25px;
> * {
margin: 0;
font-size: 40px;
font-weight: 300;
line-height: 1.3;
}
p {
letter-spacing: 0.03em;
}
button {
border: none;
background: none;
cursor: pointer;
margin-left: 8px;
// Gradient.
@supports (-webkit-background-clip: text) {
background: linear-gradient(to right, var(--mind-brand-color), var(--mind-brand-color-2));
background-clip: text;
-webkit-text-fill-color: transparent;
}
}
}
}

21
src/admin/pages/index.js Normal file
View file

@ -0,0 +1,21 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import PageWelcome from '../page-welcome';
import PageSettings from '../page-settings';
export default {
welcome: {
label: __('Welcome', 'mind'),
block: PageWelcome,
},
settings: {
label: __('Settings', 'mind'),
block: PageSettings,
},
};

View file

@ -0,0 +1,6 @@
export function setActivePage(activePage) {
return {
type: 'SET_ACTIVE_PAGE',
activePage,
};
}

View file

@ -0,0 +1,19 @@
/**
* Internal dependencies
*/
import reducer from './reducer';
import * as actions from './actions';
import * as selectors from './selectors';
/**
* WordPress dependencies
*/
import { createReduxStore, register } from '@wordpress/data';
const store = createReduxStore('mind/admin', {
reducer,
actions,
selectors,
});
register(store);

View file

@ -0,0 +1,32 @@
/**
* Internal dependencies
*/
import pages from '../../pages';
// get variable.
const $_GET = [];
window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, (a, name, value) => {
$_GET[name] = value;
});
function reducer(
state = {
activePage: $_GET.sub_page || Object.keys(pages)[0],
},
action = {}
) {
switch (action.type) {
case 'SET_ACTIVE_PAGE':
if (state.activePage !== action.activePage) {
return {
...state,
activePage: action.activePage,
};
}
break;
}
return state;
}
export default reducer;

View file

@ -0,0 +1,3 @@
export function getActivePage(state) {
return state?.activePage || '';
}

View file

@ -0,0 +1,39 @@
/**
* WordPress dependencies.
*/
import { __ } from '@wordpress/i18n';
import apiFetch from '@wordpress/api-fetch';
export function updateSettings(settings) {
return ({ dispatch }) => {
if (!settings || !Object.keys(settings).length) {
return;
}
dispatch({ type: 'UPDATE_SETTINGS_PENDING' });
const data = { settings };
apiFetch({
path: '/mind/v1/update_settings',
method: 'POST',
data,
})
.then((res) => {
dispatch({
type: 'UPDATE_SETTINGS_SUCCESS',
settings,
});
return res.response;
})
.catch((err) => {
dispatch({
type: 'UPDATE_SETTINGS_ERROR',
error:
err?.response ||
err?.error_code ||
__('Something went wrong, please, try again…', 'mind'),
});
});
};
}

View file

@ -0,0 +1,19 @@
/**
* Internal dependencies
*/
import reducer from './reducer';
import * as actions from './actions';
import * as selectors from './selectors';
/**
* WordPress dependencies
*/
import { createReduxStore, register } from '@wordpress/data';
const store = createReduxStore('mind/settings', {
reducer,
actions,
selectors,
});
register(store);

View file

@ -0,0 +1,37 @@
const { settings } = window.mindAdminData;
function reducer(
state = {
settings,
updating: false,
error: '',
},
action = {}
) {
switch (action.type) {
case 'UPDATE_SETTINGS_PENDING':
return {
...state,
updating: true,
};
case 'UPDATE_SETTINGS_SUCCESS':
return {
...state,
updating: false,
settings: {
...state.settings,
...action.settings,
},
};
case 'UPDATE_SETTINGS_ERROR':
return {
...state,
updating: false,
error: action.error || '',
};
}
return state;
}
export default reducer;

View file

@ -0,0 +1,15 @@
export function getSettings(state) {
return state?.settings || {};
}
export function getSetting(state, name) {
return state?.settings[name] || '';
}
export function getUpdating(state) {
return state?.updating || false;
}
export function getError(state) {
return state?.error || false;
}

151
src/admin/style.scss Normal file
View file

@ -0,0 +1,151 @@
@import "../variables";
$offset: 15px;
.mind-admin-page {
background-color: #1d2327;
#wpcontent {
min-height: calc(100vh - var(--wp-admin--admin-bar--height, 0) - $offset);
border-radius: 10px;
margin-right: $offset;
margin-bottom: $offset;
background-color: #fff;
}
#wpfooter {
bottom: $offset;
right: $offset;
}
ul#adminmenu a.wp-has-current-submenu::after,
ul#adminmenu > li.current > a.current::after {
border-right-color: #fff;
}
}
.mind-admin-root {
color: #000;
}
.mind-admin-head {
position: sticky;
top: var(--wp-admin--admin-bar--height, 0);
padding: 5px 20px;
margin-left: -20px;
margin-bottom: 100px;
}
.mind-admin-head-container {
display: flex;
flex-wrap: wrap;
align-items: center;
// max-width: 1200px;
margin: 0 auto;
}
.mind-admin-head-logo {
display: flex;
flex-wrap: wrap;
align-items: center;
margin-right: auto;
h1 {
margin: 6px 10px;
font-size: 14px;
font-weight: 500;
}
svg {
display: block;
width: 20px;
height: auto;
color: var(--mind-brand-color);
}
// Gradient.
@supports (-webkit-background-clip: text) {
background: linear-gradient(to right, var(--mind-brand-color), var(--mind-brand-color-2));
background-clip: text;
-webkit-text-fill-color: transparent;
}
}
.mind-admin-content {
max-width: 1000px;
margin: 0 auto;
> h2 {
margin-bottom: 35px;
font-size: 1.8em;
font-weight: 400;
}
}
.mind-admin-tabs {
display: flex;
flex-wrap: wrap;
margin: 0;
margin-left: 15px;
list-style: none;
> li {
margin: 0;
}
button {
position: relative;
padding: 10px;
margin: 0 2px;
font-size: 1.1em;
cursor: pointer;
background: none;
border: none;
outline: none;
&:hover,
&:focus {
color: var(--mind-brand-darken-color);
}
&.mind-admin-tabs-button-active::after {
content: "";
position: absolute;
right: 4px;
bottom: 2px;
left: 4px;
display: block;
height: 1.5px;
background: currentColor;
}
}
}
// .visible .sc-6c118e8c-6 {
// animation-range-start: normal;
// animation-range-end: normal;
// animation: 4.1s ease-out 1 normal forwards running fbvaXF auto normal normal;
// ///
// ///
// animation-duration: 4.1s;
// animation-timing-function: ease-out;
// animation-iteration-count: 1;
// animation-direction: normal;
// animation-fill-mode: forwards;
// animation-play-state: running;
// animation-name: fbvaXF;
// animation-timeline: auto;
// animation-range: normal;
// animation-delay: calc(var(--base-delay) + 600ms);
// }
// .NXHGL > * {
// grid-row: 1 / 1;
// grid-column: 1 / 1;
// }
// .kGaiuY {
// opacity: 0;
// filter: blur(160px);
// transform: translateZ(0);
// background: conic-gradient(from 230.29deg at 51.63% 52.16%, rgb(36, 0, 255) 0deg, rgb(0, 135, 255) 67.5deg, rgb(108, 39, 157) 198.75deg, rgb(24, 38, 163) 251.25deg, rgb(54, 103, 196) 301.88deg, rgb(105, 30, 255) 360deg);
// }

View file

@ -22,17 +22,17 @@ import {
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { ReactComponent as ArrowRightIcon } from '../../icons/arrow-right.svg'; import { ReactComponent as ArrowRightIcon } from '../../../icons/arrow-right.svg';
import { ReactComponent as AIImproveIcon } from '../../icons/ai-improve.svg'; import { ReactComponent as AIImproveIcon } from '../../../icons/ai-improve.svg';
import { ReactComponent as AIFixSpellingIcon } from '../../icons/ai-fix-spelling.svg'; import { ReactComponent as AIFixSpellingIcon } from '../../../icons/ai-fix-spelling.svg';
import { ReactComponent as AIShorterIcon } from '../../icons/ai-shorter.svg'; import { ReactComponent as AIShorterIcon } from '../../../icons/ai-shorter.svg';
import { ReactComponent as AILongerIcon } from '../../icons/ai-longer.svg'; import { ReactComponent as AILongerIcon } from '../../../icons/ai-longer.svg';
import { ReactComponent as AISummarizeIcon } from '../../icons/ai-summarize.svg'; import { ReactComponent as AISummarizeIcon } from '../../../icons/ai-summarize.svg';
import { ReactComponent as AIToneIcon } from '../../icons/ai-tone.svg'; import { ReactComponent as AIToneIcon } from '../../../icons/ai-tone.svg';
import { ReactComponent as AIParaphraseIcon } from '../../icons/ai-paraphrase.svg'; import { ReactComponent as AIParaphraseIcon } from '../../../icons/ai-paraphrase.svg';
import { ReactComponent as AITranslateIcon } from '../../icons/ai-translate.svg'; import { ReactComponent as AITranslateIcon } from '../../../icons/ai-translate.svg';
import wrapEmoji from '../../utils/wrap-emoji'; import { ReactComponent as MindLogoIcon } from '../../../icons/mind-logo.svg';
import TOOLBAR_ICON from '../../utils/icon'; import wrapEmoji from '../../../utils/wrap-emoji';
const ALLOWED_BLOCKS = ['core/paragraph', 'core/heading']; const ALLOWED_BLOCKS = ['core/paragraph', 'core/heading'];
@ -88,8 +88,9 @@ function Toolbar() {
return ( return (
<ToolbarGroup> <ToolbarGroup>
<DropdownMenu <DropdownMenu
icon={TOOLBAR_ICON} icon={<MindLogoIcon />}
label={__('Mind', '@@text_domain')} label={__('Mind', '@@text_domain')}
className="mind-toolbar-toggle"
popoverProps={{ className: 'mind-toolbar-dropdown' }} popoverProps={{ className: 'mind-toolbar-dropdown' }}
> >
{() => { {() => {

View file

@ -1,3 +1,7 @@
.mind-toolbar-toggle svg {
color: var(--mind-brand-color);
}
.mind-toolbar-dropdown { .mind-toolbar-dropdown {
--wp-admin-theme-color: var(--mind-brand-darken-color); --wp-admin-theme-color: var(--mind-brand-darken-color);

View file

@ -13,7 +13,7 @@ import { throttle } from 'lodash';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import TOOLBAR_ICON from '../../utils/icon'; import { ReactComponent as MindLogoIcon } from '../../../icons/mind-logo.svg';
const TOOLBAR_TOGGLE_CONTAINER_CLASS = 'mind-post-toolbar-toggle'; const TOOLBAR_TOGGLE_CONTAINER_CLASS = 'mind-post-toolbar-toggle';
@ -30,7 +30,7 @@ function Toggle() {
toggle(); toggle();
}} }}
> >
{TOOLBAR_ICON} <MindLogoIcon />
{__('Open Mind', '@@text_domain')} {__('Open Mind', '@@text_domain')}
</button> </button>
); );

View file

@ -13,12 +13,12 @@ import { Button } from '@wordpress/components';
*/ */
import LoadingText from '../loading-text'; import LoadingText from '../loading-text';
import Notice from '../notice'; import Notice from '../notice';
import { ReactComponent as PopupPostTitleAboutIcon } from '../../../icons/popup-post-title-about.svg'; import { ReactComponent as PopupPostTitleAboutIcon } from '../../../../icons/popup-post-title-about.svg';
import { ReactComponent as PopupPostAboutIcon } from '../../../icons/popup-post-about.svg'; import { ReactComponent as PopupPostAboutIcon } from '../../../../icons/popup-post-about.svg';
import { ReactComponent as PopupOutlineAboutIcon } from '../../../icons/popup-outline-about.svg'; import { ReactComponent as PopupOutlineAboutIcon } from '../../../../icons/popup-outline-about.svg';
import { ReactComponent as PopupParagraphAboutIcon } from '../../../icons/popup-paragraph-about.svg'; import { ReactComponent as PopupParagraphAboutIcon } from '../../../../icons/popup-paragraph-about.svg';
import { ReactComponent as PopupListAboutIcon } from '../../../icons/popup-list-about.svg'; import { ReactComponent as PopupListAboutIcon } from '../../../../icons/popup-list-about.svg';
import { ReactComponent as PopupTableAboutIcon } from '../../../icons/popup-table-about.svg'; import { ReactComponent as PopupTableAboutIcon } from '../../../../icons/popup-table-about.svg';
const commands = [ const commands = [
{ {

View file

@ -11,7 +11,7 @@ import { useSelect, useDispatch } from '@wordpress/data';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import TOOLBAR_ICON from '../../../utils/icon'; import { ReactComponent as MindLogoIcon } from '../../../../icons/mind-logo.svg';
export default function Input(props) { export default function Input(props) {
const { onInsert } = props; const { onInsert } = props;
@ -99,7 +99,7 @@ export default function Input(props) {
return ( return (
<div className="mind-popup-input" ref={ref}> <div className="mind-popup-input" ref={ref}>
{TOOLBAR_ICON} <MindLogoIcon />
<TextControl <TextControl
placeholder={__('Ask AI to write anything…', 'mind')} placeholder={__('Ask AI to write anything…', 'mind')}
value={input} value={input}

View file

@ -10,6 +10,7 @@ $padding: 10px;
position: absolute; position: absolute;
left: $padding * 2; left: $padding * 2;
pointer-events: none; pointer-events: none;
color: var(--mind-brand-color);
} }
> .components-base-control { > .components-base-control {

View file

@ -12,7 +12,7 @@ import { useSelect } from '@wordpress/data';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { ReactComponent as KeyIcon } from '../../../icons/key.svg'; import { ReactComponent as KeyIcon } from '../../../../icons/key.svg';
export default function NotConnectedScreen() { export default function NotConnectedScreen() {
const { settingsPageURL } = useSelect((select) => { const { settingsPageURL } = useSelect((select) => {

View file

@ -7,7 +7,7 @@ import apiFetch from '@wordpress/api-fetch';
/** /**
* Internal dependencies. * Internal dependencies.
*/ */
import getSelectedBlocksContent from '../../utils/get-selected-blocks-content'; import getSelectedBlocksContent from '../../../utils/get-selected-blocks-content';
import { isConnected } from '../core/selectors'; import { isConnected } from '../core/selectors';
export function open() { export function open() {

View file

@ -1,4 +1,4 @@
import mdToHtml from '../../utils/md-to-html'; import mdToHtml from '../../../utils/md-to-html';
function reducer( function reducer(
state = { state = {

View file

@ -1,14 +1,11 @@
:root { @import "../variables";
--mind-brand-color: #e455df;
--mind-brand-darken-color: #bb56df;
}
// Gradients for logos. // Gradients for logos.
@supports (-webkit-background-clip: text) { @supports (-webkit-background-clip: text) {
.mind-post-toolbar-toggle button, .mind-post-toolbar-toggle button,
.mind-popup-footer-logo, .mind-popup-footer-logo,
.mind-popup-connected-screen-button { .mind-popup-connected-screen-button {
background: linear-gradient(to right, #e455df, #4376ec); background: linear-gradient(to right, var(--mind-brand-color), var(--mind-brand-color-2));
background-clip: text; background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
} }

7
src/icons/loading.svg Normal file
View file

@ -0,0 +1,7 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M19.75 12C19.75 7.71979 16.2802 4.25 12 4.25C9.8599 4.25 7.9224 5.11745 6.51992 6.51992L5.39922 7.64062M4.25 12C4.25 16.2802 7.71979 19.75 12 19.75C14.1401 19.75 16.0776 18.8826 17.4801 17.4801L18.6008 16.3594"
stroke="currentColor" stroke-width="1.5" fill="transparent" />
<path d="M4.75 4.25V8.25H8.75" stroke="currentColor" stroke-width="1.5" fill="transparent" />
<path d="M19.25 19.75V15.75H15.25" stroke="currentColor" stroke-width="1.5" fill="transparent" />
</svg>

After

Width:  |  Height:  |  Size: 584 B

14
src/icons/mind-logo.svg Normal file
View file

@ -0,0 +1,14 @@
<svg width="20" height="20" viewBox="0 0 66 66" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M24.6667 24.3162C28.8307 20.1695 31.6752 11 31.6752 11C31.6752 11 34.2643 20.1633 38.3333 24.3162C42.5311 28.6006 52 31.3248 52 31.3248C52 31.3248 42.5235 34.0416 38.3333 38.3333C34.1859 42.5812 31.6752 52 31.6752 52C31.6752 52 28.9079 42.5746 24.6667 38.3333C20.4254 34.0921 11 31.3248 11 31.3248C11 31.3248 20.4166 28.5487 24.6667 24.3162Z"
fill="currentColor" />
<path
d="M48.6667 51.5214C50.3932 49.802 51.5726 46 51.5726 46C51.5726 46 52.6462 49.7994 54.3333 51.5214C56.0739 53.2978 60 54.4274 60 54.4274C60 54.4274 56.0707 55.5538 54.3333 57.3333C52.6137 59.0947 51.5726 63 51.5726 63C51.5726 63 50.4252 59.0919 48.6667 57.3333C46.9081 55.5748 43 54.4274 43 54.4274C43 54.4274 46.9044 53.2763 48.6667 51.5214Z"
fill="currentColor" />
<path
d="M6.66667 45.5726C7.78384 44.4601 8.54701 42 8.54701 42C8.54701 42 9.24164 44.4584 10.3333 45.5726C11.4596 46.7221 14 47.453 14 47.453C14 47.453 11.4575 48.1819 10.3333 49.3333C9.2206 50.473 8.54701 53 8.54701 53C8.54701 53 7.80457 50.4712 6.66667 49.3333C5.52877 48.1954 3 47.453 3 47.453C3 47.453 5.5264 46.7082 6.66667 45.5726Z"
fill="currentColor" />
<path
d="M50.3333 6.22222C51.6536 4.9074 52.5556 2 52.5556 2C52.5556 2 53.3765 4.90543 54.6667 6.22222C55.9977 7.58066 59 8.44444 59 8.44444C59 8.44444 55.9953 9.30588 54.6667 10.6667C53.3516 12.0136 52.5556 15 52.5556 15C52.5556 15 51.6781 12.0115 50.3333 10.6667C48.9885 9.32188 46 8.44444 46 8.44444C46 8.44444 48.9858 7.56421 50.3333 6.22222Z"
fill="currentColor" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -1,29 +0,0 @@
import './style.scss';
export default (
<svg
width="20"
height="20"
viewBox="0 0 66 66"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="mind-icon"
>
<path
d="M24.6667 24.3162C28.8307 20.1695 31.6752 11 31.6752 11C31.6752 11 34.2643 20.1633 38.3333 24.3162C42.5311 28.6006 52 31.3248 52 31.3248C52 31.3248 42.5235 34.0416 38.3333 38.3333C34.1859 42.5812 31.6752 52 31.6752 52C31.6752 52 28.9079 42.5746 24.6667 38.3333C20.4254 34.0921 11 31.3248 11 31.3248C11 31.3248 20.4166 28.5487 24.6667 24.3162Z"
fill="currentColor"
/>
<path
d="M48.6667 51.5214C50.3932 49.802 51.5726 46 51.5726 46C51.5726 46 52.6462 49.7994 54.3333 51.5214C56.0739 53.2978 60 54.4274 60 54.4274C60 54.4274 56.0707 55.5538 54.3333 57.3333C52.6137 59.0947 51.5726 63 51.5726 63C51.5726 63 50.4252 59.0919 48.6667 57.3333C46.9081 55.5748 43 54.4274 43 54.4274C43 54.4274 46.9044 53.2763 48.6667 51.5214Z"
fill="currentColor"
/>
<path
d="M6.66667 45.5726C7.78384 44.4601 8.54701 42 8.54701 42C8.54701 42 9.24164 44.4584 10.3333 45.5726C11.4596 46.7221 14 47.453 14 47.453C14 47.453 11.4575 48.1819 10.3333 49.3333C9.2206 50.473 8.54701 53 8.54701 53C8.54701 53 7.80457 50.4712 6.66667 49.3333C5.52877 48.1954 3 47.453 3 47.453C3 47.453 5.5264 46.7082 6.66667 45.5726Z"
fill="currentColor"
/>
<path
d="M50.3333 6.22222C51.6536 4.9074 52.5556 2 52.5556 2C52.5556 2 53.3765 4.90543 54.6667 6.22222C55.9977 7.58066 59 8.44444 59 8.44444C59 8.44444 55.9953 9.30588 54.6667 10.6667C53.3516 12.0136 52.5556 15 52.5556 15C52.5556 15 51.6781 12.0115 50.3333 10.6667C48.9885 9.32188 46 8.44444 46 8.44444C46 8.44444 48.9858 7.56421 50.3333 6.22222Z"
fill="currentColor"
/>
</svg>
);

View file

@ -1,5 +0,0 @@
.mind-icon {
color: var(--mind-brand-color);
width: 20px;
height: 20px;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,989 +0,0 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Mind Settings API wrapper class
* based on weDevs Settings API
*
* ADDED PR: https://github.com/tareq1988/wordpress-settings-api-class/pull/47
* ADDED: enqueue scripts manually, without `admin_enqueue_scripts` action.
* ADDED: new controls such as toggle and number slider.
*
* @author Tareq Hasan <tareq@weDevs.com>
* @link https://tareq.co Tareq Hasan
*/
class Mind_Settings_API {
/**
* Settings sections array
*
* @var array
*/
protected $settings_sections = [];
/**
* Settings fields array
*
* @var array
*/
protected $settings_fields = [];
/**
* Mind_Settings_API constructor.
*/
public function __construct() {
// add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] );
}
/**
* Enqueue scripts and styles
*/
public function admin_enqueue_scripts() {
wp_enqueue_style( 'wp-color-picker' );
wp_enqueue_media();
wp_enqueue_script( 'wp-color-picker' );
wp_enqueue_script( 'jquery' );
wp_enqueue_script( 'conditionize', mind()->plugin_url . 'vendors/assets/conditionize/conditionize.min.js', [ 'jquery' ], '1.0.5' );
wp_enqueue_style( 'select2', mind()->plugin_url . 'vendors/assets/select2/select2.min.css', [], '4.0.13' );
wp_enqueue_script( 'select2', mind()->plugin_url . 'vendors/assets/select2/select2.min.js', [ 'jquery' ], '4.0.13' );
}
/**
* Set settings sections
*
* @param array $sections setting sections array
*/
public function set_sections( $sections ) {
$this->settings_sections = $sections;
return $this;
}
/**
* Add a single section
*
* @param array $section
*/
public function add_section( $section ) {
$this->settings_sections[] = $section;
return $this;
}
/**
* Set settings fields
*
* @param array $fields settings fields array
*/
public function set_fields( $fields ) {
$this->settings_fields = $fields;
return $this;
}
public function add_field( $section, $field ) {
$defaults = [
'name' => '',
'label' => '',
'desc' => '',
'type' => 'text',
'is_pro' => false,
];
$arg = wp_parse_args( $field, $defaults );
$this->settings_fields[ $section ][] = $arg;
return $this;
}
/**
* Initialize and registers the settings sections and fileds to WordPress
*
* Usually this should be called at `admin_init` hook.
*
* This function gets the initiated settings sections and fields. Then
* registers them to WordPress and ready for use.
*/
public function admin_init() {
// register settings sections
foreach ( $this->settings_sections as $section ) {
if ( false == get_option( $section['id'] ) ) {
add_option( $section['id'] );
}
if ( isset( $section['desc'] ) && ! empty( $section['desc'] ) ) {
$section['desc'] = '<div class="inside">' . $section['desc'] . '</div>';
$callback = create_function( '', 'echo "' . str_replace( '"', '\"', $section['desc'] ) . '";' );
} elseif ( isset( $section['callback'] ) ) {
$callback = $section['callback'];
} else {
$callback = null;
}
add_settings_section( $section['id'], $section['title'], $callback, $section['id'] );
}
// register settings fields
foreach ( $this->settings_fields as $section => $field ) {
foreach ( $field as $option ) {
$name = $option['name'];
$type = isset( $option['type'] ) ? $option['type'] : 'text';
$label = isset( $option['label'] ) ? $option['label'] : '';
$callback = isset( $option['callback'] ) ? $option['callback'] : [ $this, 'callback_' . $type ];
$class_name = isset( $option['class'] ) ? $option['class'] : $name;
$is_pro = isset( $option['is_pro'] ) ? $option['is_pro'] : false;
if ( $is_pro ) {
$class_name .= ' mind-settings-control-pro';
$go_pro_url = Visual_Portfolio_Admin::get_plugin_site_url(
[
'utm_medium' => 'settings_page',
'utm_campaign' => esc_attr( $name ),
]
);
$label .= '<a class="mind-settings-control-pro-label" target="_blank" rel="noopener noreferrer" href="' . esc_url( $go_pro_url ) . '">?<span>' . esc_html__( 'This feature is available in the Pro plugin only.', '@@text_domain' ) . '</span></a>';
}
$args = [
'id' => $name,
'class' => $class_name,
'label_for' => "{$section}[{$name}]",
'desc' => isset( $option['desc'] ) ? $option['desc'] : '',
'name' => $label,
'section' => $section,
'size' => isset( $option['size'] ) ? $option['size'] : null,
'options' => isset( $option['options'] ) ? $option['options'] : '',
'std' => isset( $option['default'] ) ? $option['default'] : '',
'sanitize_callback' => isset( $option['sanitize_callback'] ) ? $option['sanitize_callback'] : '',
'type' => $type,
'placeholder' => isset( $option['placeholder'] ) ? $option['placeholder'] : '',
'min' => isset( $option['min'] ) ? $option['min'] : '',
'max' => isset( $option['max'] ) ? $option['max'] : '',
'step' => isset( $option['step'] ) ? $option['step'] : '',
'is_pro' => isset( $option['is_pro'] ) ? $option['is_pro'] : false,
'condition' => isset( $option['condition'] ) ? $option['condition'] : null,
'conditionize' => isset( $option['condition'] ) ? $this->convert_arguments_to_conditionize_string( $option['condition'] ) : '',
];
add_settings_field( "{$section}[{$name}]", $label, $callback, $section, $section, $args );
}
}
// creates our settings in the options table
foreach ( $this->settings_sections as $section ) {
register_setting( $section['id'], $section['id'], [ $this, 'sanitize_options' ] );
}
}
/**
* Get field description for display
*
* @param array $args settings field args
*/
public function get_field_description( $args ) {
if ( ! empty( $args['desc'] ) ) {
$desc = sprintf( '<p class="description">%s</p>', $args['desc'] );
} else {
$desc = '';
}
return $desc;
}
/**
* Displays a text field for a settings field
*
* @param array $args settings field args
*/
public function callback_text( $args ) {
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
$type = isset( $args['type'] ) ? $args['type'] : 'text';
$placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . $args['placeholder'] . '"';
$html = sprintf( '<input type="%1$s" class="%2$s-text" id="%3$s[%4$s]" name="%3$s[%4$s]" value="%5$s"%6$s/>', $type, $size, $args['section'], $args['id'], $value, $placeholder );
$html .= $this->get_field_description( $args );
echo $html;
}
/**
* Displays a url field for a settings field
*
* @param array $args settings field args
*/
public function callback_url( $args ) {
$this->callback_text( $args );
}
/**
* Displays a number field for a settings field
*
* @param array $args settings field args
*/
public function callback_number( $args ) {
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
$type = isset( $args['type'] ) ? $args['type'] : 'number';
$placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . $args['placeholder'] . '"';
$min = empty( $args['min'] ) ? '' : ' min="' . $args['min'] . '"';
$max = empty( $args['max'] ) ? '' : ' max="' . $args['max'] . '"';
$step = empty( $args['max'] ) ? '' : ' step="' . $args['step'] . '"';
$html = sprintf( '<input type="%1$s" class="%2$s-number" id="%3$s[%4$s]" name="%3$s[%4$s]" value="%5$s"%6$s%7$s%8$s%9$s/>', $type, $size, $args['section'], $args['id'], $value, $placeholder, $min, $max, $step );
$html .= $this->get_field_description( $args );
echo $html;
}
/**
* Displays a checkbox for a settings field
*
* @param array $args settings field args
*/
public function callback_checkbox( $args ) {
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$html = '<fieldset>';
$html .= sprintf( '<label for="wpuf-%1$s[%2$s]">', $args['section'], $args['id'] );
$html .= sprintf( '<input type="hidden" name="%1$s[%2$s]" value="off" />', $args['section'], $args['id'] );
$html .= sprintf( '<input type="checkbox" class="checkbox" id="wpuf-%1$s[%2$s]" name="%1$s[%2$s]" value="on" %3$s />', $args['section'], $args['id'], checked( $value, 'on', false ) );
$html .= sprintf( '<span class="description">%1$s</span></label>', $args['desc'] );
$html .= '</fieldset>';
echo $html;
}
/**
* Displays a multicheckbox for a settings field
*
* @param array $args settings field args
*/
public function callback_multicheck( $args ) {
$value = $this->get_option( $args['id'], $args['section'], $args['std'] );
$html = '<fieldset>';
$html .= sprintf( '<input type="hidden" name="%1$s[%2$s]" value="" />', $args['section'], $args['id'] );
foreach ( $args['options'] as $key => $label ) {
$checked = isset( $value[ $key ] ) ? $value[ $key ] : '0';
$html .= sprintf( '<label for="wpuf-%1$s[%2$s][%3$s]">', $args['section'], $args['id'], $key );
$html .= sprintf( '<input type="checkbox" class="checkbox" id="wpuf-%1$s[%2$s][%3$s]" name="%1$s[%2$s][%3$s]" value="%3$s" %4$s />', $args['section'], $args['id'], $key, checked( $checked, $key, false ) );
$html .= sprintf( '<span class="description">%1$s</span></label><br>', $label );
}
$html .= $this->get_field_description( $args );
$html .= '</fieldset>';
echo $html;
}
/**
* Displays a radio button for a settings field
*
* @param array $args settings field args
*/
public function callback_radio( $args ) {
$value = $this->get_option( $args['id'], $args['section'], $args['std'] );
$html = '<fieldset>';
foreach ( $args['options'] as $key => $label ) {
$html .= sprintf( '<label for="wpuf-%1$s[%2$s][%3$s]">', $args['section'], $args['id'], $key );
$html .= sprintf( '<input type="radio" class="radio" id="wpuf-%1$s[%2$s][%3$s]" name="%1$s[%2$s]" value="%3$s" %4$s />', $args['section'], $args['id'], $key, checked( $value, $key, false ) );
$html .= sprintf( '<span class="description">%1$s</span></label><br>', $label );
}
$html .= $this->get_field_description( $args );
$html .= '</fieldset>';
echo $html;
}
/**
* Displays a selectbox for a settings field
*
* @param array $args settings field args
*/
public function callback_select( $args ) {
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$classes = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
$html = sprintf( '<select class="%1$s" name="%2$s[%3$s]" id="%2$s[%3$s]">', $classes, $args['section'], $args['id'] );
foreach ( $args['options'] as $key => $label ) {
$html .= sprintf( '<option value="%s"%s>%s</option>', $key, selected( $value, $key, false ), $label );
}
$html .= sprintf( '</select>' );
$html .= $this->get_field_description( $args );
echo $html;
}
/**
* Displays a textarea for a settings field
*
* @param array $args settings field args
*/
public function callback_textarea( $args ) {
$value = esc_textarea( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
$placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . $args['placeholder'] . '"';
$html = sprintf( '<textarea rows="5" cols="55" class="%1$s-text" id="%2$s[%3$s]" name="%2$s[%3$s]"%4$s>%5$s</textarea>', $size, $args['section'], $args['id'], $placeholder, $value );
$html .= $this->get_field_description( $args );
echo $html;
}
/**
* Displays the html for a settings field
*
* @param array $args settings field args
* @return string
*/
public function callback_html( $args ) {
echo $this->get_field_description( $args );
}
/**
* Displays the section title for a settings field
*
* @param array $args settings field args
* @return string
*/
public function callback_section_title( $args ) {
echo $this->get_field_description( $args );
}
/**
* Displays a rich text textarea for a settings field
*
* @param array $args settings field args
*/
public function callback_wysiwyg( $args ) {
$value = $this->get_option( $args['id'], $args['section'], $args['std'] );
$size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : '500px';
echo '<div style="max-width: ' . $size . ';">';
$editor_settings = [
'teeny' => true,
'textarea_name' => $args['section'] . '[' . $args['id'] . ']',
'textarea_rows' => 10,
];
if ( isset( $args['options'] ) && is_array( $args['options'] ) ) {
$editor_settings = array_merge( $editor_settings, $args['options'] );
}
wp_editor( $value, $args['section'] . '-' . $args['id'], $editor_settings );
echo '</div>';
echo $this->get_field_description( $args );
}
/**
* Displays a file upload field for a settings field
*
* @param array $args settings field args
*/
public function callback_file( $args ) {
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
$id = $args['section'] . '[' . $args['id'] . ']';
$label = isset( $args['options']['button_label'] ) ? $args['options']['button_label'] : __( 'Choose File' );
$html = sprintf( '<input type="text" class="%1$s-text wpsa-url" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s"/>', $size, $args['section'], $args['id'], $value );
$html .= '<input type="button" class="button wpsa-browse" value="' . $label . '" />';
$html .= $this->get_field_description( $args );
echo $html;
}
/**
* Displays a image upload field for a settings field
*
* @param array $args settings field args
*/
public function callback_image( $args ) {
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
$id = $args['section'] . '[' . $args['id'] . ']';
$label = isset( $args['options']['button_label'] ) ? $args['options']['button_label'] : __( 'Choose Image' );
$label_remove = isset( $args['options']['button_remove_label'] ) ? $args['options']['button_remove_label'] : __( 'Remove Image' );
$img = wp_get_attachment_image_src( $value, $args['size'] ? $args['size'] : 'thumbnail' );
$img_url = $img ? $img[0] : '';
$html = sprintf( '<input type="hidden" class="%1$s-text wpsa-image-id" id="%2$s" name="%2$s" value="%3$s"/>', $size, $id, $value );
$html .= '<p class="wpsa-image-preview"><img src="' . $img_url . '" /></p>';
$html .= '<input type="button" class="button button-primary wpsa-image-browse" value="' . $label . '" />';
$html .= '<input type="button" class="button button-link wpsa-image-remove" value="' . $label_remove . '" />';
$html .= $this->get_field_description( $args );
echo $html;
}
/**
* Displays a password field for a settings field
*
* @param array $args settings field args
*/
public function callback_password( $args ) {
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
$html = sprintf( '<input type="password" class="%1$s-text" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s"/>', $size, $args['section'], $args['id'], $value );
$html .= $this->get_field_description( $args );
echo $html;
}
/**
* Displays a color picker field for a settings field
*
* @param array $args settings field args
*/
public function callback_color( $args ) {
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
$html = sprintf( '<input type="text" class="%1$s-text wp-color-picker-field" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s" data-default-color="%5$s" />', $size, $args['section'], $args['id'], $value, $args['std'] );
$html .= $this->get_field_description( $args );
echo $html;
}
/**
* Displays a select box for creating the pages select box
*
* @param array $args settings field args
*/
public function callback_pages( $args ) {
$dropdown_args = [
'selected' => esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) ),
'name' => $args['section'] . '[' . $args['id'] . ']',
'id' => $args['section'] . '[' . $args['id'] . ']',
'echo' => 0,
];
$html = wp_dropdown_pages( $dropdown_args );
echo $html;
}
/**
* Displays a toggle field for a settings field
*
* @param array $args settings field args.
*/
public function callback_toggle( $args ) {
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$html = sprintf( '<label for="wpuf-%1$s[%2$s]" class="mind-toggle-field">', $args['section'], $args['id'] );
$html .= sprintf( '<input type="hidden" name="%1$s[%2$s]" value="off" />', esc_attr( $args['section'] ), esc_attr( $args['id'] ) );
$html .= sprintf( '<input type="checkbox" id="wpuf-%1$s[%2$s]" name="%1$s[%2$s]" value="on" %3$s/>', esc_attr( $args['section'] ), esc_attr( $args['id'] ), checked( $value, 'on', false ) );
$html .= sprintf( '<span class="mind-toggle-field-slider-round"></span><span class="description">%1$s</span></label>', $args['desc'] );
echo $html;
}
/**
* Displays a range field for a settings field
*
* @param array $args settings field args.
*/
public function callback_range( $args ) {
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
$type = isset( $args['type'] ) ? $args['type'] : 'range';
$placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . $args['placeholder'] . '"';
$min = empty( $args['min'] ) ? '' : ' min="' . $args['min'] . '"';
$max = empty( $args['max'] ) ? '' : ' max="' . $args['max'] . '"';
$step = empty( $args['max'] ) ? '' : ' step="' . $args['step'] . '"';
?>
<?php
echo sprintf(
'<input type="%1$s" class="%2$s-range mind-range-field" id="%3$s[%4$s]" name="%3$s[%4$s]" value="%5$s"%6$s%7$s%8$s%9$s/>',
esc_attr( $type ),
esc_attr( $size ),
esc_attr( $args['section'] ),
esc_attr( $args['id'] ),
esc_attr( $value ),
esc_attr( $placeholder ),
esc_attr( $min ),
esc_attr( $max ),
esc_attr( $step )
);
echo sprintf(
'<input type="number" class="number-range mind-range-number-field" name="%1$s[%2$s]" value="%3$s"%4$s%5$s%6$s%7$s/>',
esc_attr( $args['section'] ),
esc_attr( $args['id'] ),
esc_attr( $value ),
esc_attr( $placeholder ),
esc_attr( $min ),
esc_attr( $max ),
esc_attr( $step )
);
echo wp_kses_post( $this->get_field_description( $args ) );
?>
<?php
}
/**
* Displays a hidden field for a settings field
*
* @param array $args settings field args.
*/
public function callback_hidden( $args ) {
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
$type = isset( $args['type'] ) ? $args['type'] : 'hidden';
$placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . $args['placeholder'] . '"';
echo sprintf(
'<input type="%1$s" class="%2$s-text" id="%3$s[%4$s]" name="%3$s[%4$s]" value="%5$s"%6$s/>',
esc_attr( $type ),
esc_attr( $size ),
esc_attr( $args['section'] ),
esc_attr( $args['id'] ),
esc_attr( $value ),
esc_attr( $placeholder )
);
echo wp_kses_post( $this->get_field_description( $args ) );
}
/**
* Sanitize callback for Settings API
*
* @return mixed
*/
public function sanitize_options( $options ) {
if ( ! $options ) {
return $options;
}
foreach ( $options as $option_slug => $option_value ) {
$sanitize_callback = $this->get_sanitize_callback( $option_slug );
// If callback is set, call it
if ( $sanitize_callback ) {
$options[ $option_slug ] = call_user_func( $sanitize_callback, $option_value );
continue;
}
}
return $options;
}
/**
* Get sanitization callback for given option slug
*
* @param string $slug option slug
*
* @return mixed string or bool false
*/
public function get_sanitize_callback( $slug = '' ) {
if ( empty( $slug ) ) {
return false;
}
// Iterate over registered fields and see if we can find proper callback
foreach ( $this->settings_fields as $section => $options ) {
foreach ( $options as $option ) {
if ( $option['name'] != $slug ) {
continue;
}
// Return the callback name
return isset( $option['sanitize_callback'] ) && is_callable( $option['sanitize_callback'] ) ? $option['sanitize_callback'] : false;
}
}
return false;
}
/**
* Get the value of a settings field
*
* @param string $option settings field name
* @param string $section the section name this field belongs to
* @param string $default default text if it's not found
* @return string
*/
public function get_option( $option, $section, $default = '' ) {
$options = get_option( $section );
if ( isset( $options[ $option ] ) ) {
return $options[ $option ];
}
return $default;
}
/**
* Show navigations as tab
*
* Shows all the settings section labels as tab
*/
public function show_navigation() {
$html = '<h2 class="nav-tab-wrapper">';
$count = count( $this->settings_sections );
// don't show the navigation if only one section exists
if ( $count === 1 ) {
return;
}
foreach ( $this->settings_sections as $tab ) {
$html .= sprintf( '<a href="#%1$s" class="nav-tab" id="%1$s-tab">%2$s%3$s</a>', $tab['id'], isset( $tab['icon'] ) ? $tab['icon'] : '', $tab['title'] );
}
$html .= '</h2>';
echo $html;
}
/**
* Tabbable JavaScript codes & Initiate Color Picker
*
* This code uses localstorage for displaying active tabs
*/
public function script() {
?>
<script>
jQuery(function($) {
// Initiate Conditionize
$( '.metabox-holder' ).conditionize();
// Initiate Color Picker
$('.wp-color-picker-field').wpColorPicker();
// Switches option sections
$('.group').hide();
var activetab = '';
if (typeof(localStorage) != 'undefined' ) {
activetab = localStorage.getItem("activetab");
}
//if url has section id as hash then set it as active or override the current local storage value
if(window.location.hash){
activetab = window.location.hash;
if (typeof(localStorage) != 'undefined' ) {
localStorage.setItem("activetab", activetab);
}
}
if (activetab != '' && $(activetab).length ) {
$(activetab).fadeIn();
} else {
$('.group:first').fadeIn();
}
$('.group .collapsed').each(function(){
$(this).find('input:checked').parent().parent().parent().nextAll().each(
function(){
if ($(this).hasClass('last')) {
$(this).removeClass('hidden');
return false;
}
$(this).filter('.hidden').removeClass('hidden');
});
});
if (activetab != '' && $(activetab + '-tab').length ) {
$(activetab + '-tab').addClass('nav-tab-active');
}
else {
$('.nav-tab-wrapper a:first').addClass('nav-tab-active');
}
$('.nav-tab-wrapper a').click(function(evt) {
$('.nav-tab-wrapper a').removeClass('nav-tab-active');
$(this).addClass('nav-tab-active').blur();
var clicked_group = $(this).attr('href');
if (typeof(localStorage) != 'undefined' ) {
localStorage.setItem("activetab", $(this).attr('href'));
}
$('.group').hide();
$(clicked_group).fadeIn();
evt.preventDefault();
});
$('.wpsa-browse').on('click', function (event) {
event.preventDefault();
var $this = $(this);
// Create the media frame.
var file_frame = wp.media.frames.file_frame = wp.media({
title: $this.data('uploader_title'),
button: {
text: $this.data('uploader_button_text'),
},
multiple: false
});
file_frame.on('select', function () {
attachment = file_frame.state().get('selection').first().toJSON();
$this.prev('.wpsa-url').val(attachment.url).change();
});
// Finally, open the modal
file_frame.open();
});
$('.wpsa-image-browse').on('click', function(event) {
event.preventDefault();
var $this = $(this);
// Create the media frame.
var file_frame = wp.media.frames.file_frame = wp.media({
title: $this.data('uploader_title'),
button: {
text: $this.data('uploader_button_text'),
},
multiple: false,
library: { type: 'image' }
})
.on('select', function () {
attachment = file_frame.state().get('selection').first().toJSON();
var url;
if (attachment.sizes && attachment.sizes.thumbnail) {
url = attachment.sizes.thumbnail.url;
} else {
url = attachment.url;
}
$this.siblings('.wpsa-image-id').val(attachment.id).change();
$this.siblings('.wpsa-image-preview').children('img').attr('src', url);
$this.siblings('.wpsa-image-remove').css('display', 'inline-block');
$this.trigger( 'wpsa-image-browse-selected', [ attachment, url ] );
})
// Finally, open the modal
.open();
});
$('.wpsa-image-remove').each(function() {
var $this = $(this);
if ( $this.siblings('.wpsa-image-id').val() ) {
$this.css('display', 'inline-block');
}
});
$('.wpsa-image-remove').on('click', function(event) {
event.preventDefault();
var $this = $(this);
$this.siblings('.wpsa-image-id').val('').change();
$this.siblings('.wpsa-image-preview').children('img').attr('src', '');
$this.css('display', '');
$this.trigger( 'wpsa-image-removed' );
});
$( 'input.mind-range-field, input.mind-range-number-field' ).on( 'input change', function() {
const name = $( this ).attr( 'name' );
const min = parseInt( $( this ).attr( 'min' ).replace( /"/g, '' ), 10 );
const max = parseInt( $( this ).attr( 'max' ).replace( /"/g, '' ), 10 );
const val = parseInt( $( this ).val(), 10 );
const inputs = $( `input[name="${ name }"]` );
if ( max < val ) {
$( this ).val( max );
}
if ( min > val || isNaN( val ) ) {
$( this ).val( min );
}
if ( $( inputs[ 0 ] ).val() !== $( inputs[ 1 ] ).val() ) {
inputs.val( $( this ).val() );
}
});
});
</script>
<?php
}
/**
* Show the section settings forms
*
* This function displays every sections in a different form
*/
public function show_forms() {
?>
<div class="metabox-holder">
<?php foreach ( $this->settings_sections as $form ) {
echo apply_filters( 'mind_settings_show_section_form', $this->get_form( $form ), $form );
} ?>
</div>
<?php
$this->script();
}
/**
* Prints out all settings sections added to a particular settings page
*
* Part of the Settings API. Use this in a settings page callback function
* to output all the sections and fields that were added to that $page with
* add_settings_section() and add_settings_field()
*
* @global array $wp_settings_sections Storage array of all settings sections added to admin pages.
* @global array $wp_settings_fields Storage array of settings fields and info about their pages/sections.
*
* @param string $page The slug name of the page whose settings sections you want to output.
*/
public function do_settings_sections( $page ) {
global $wp_settings_sections, $wp_settings_fields;
if ( ! isset( $wp_settings_sections[ $page ] ) ) {
return;
}
foreach ( (array) $wp_settings_sections[ $page ] as $section ) {
if ( $section['callback'] ) {
call_user_func( $section['callback'], $section );
}
if ( ! isset( $wp_settings_fields ) || ! isset( $wp_settings_fields[ $page ] ) || ! isset( $wp_settings_fields[ $page ][ $section['id'] ] ) ) {
continue;
}
echo '<table class="form-table" role="presentation">';
$this->do_settings_fields( $page, $section['id'] );
echo '</table>';
}
}
/**
* Get the section settings form.
* This function return displays detailed section in a each of forms.
*
* @param array $form - Form item.
* @return string
*/
public function get_form( $form ) {
ob_start();
?>
<div id="<?php echo $form['id']; ?>" class="group" style="display: none;">
<form method="post" action="options.php">
<?php
do_action( 'wsa_form_top_' . $form['id'], $form );
settings_fields( $form['id'] );
$this->do_settings_sections( $form['id'] );
do_action( 'wsa_form_bottom_' . $form['id'], $form );
if ( isset( $this->settings_fields[ $form['id'] ] ) ) :
?>
<div class="metabox-holder-footer">
<?php submit_button(); ?>
</div>
<?php endif; ?>
</form>
</div>
<?php
return ob_get_clean();
}
/**
* Print out the settings fields for a particular settings section.
*
* Part of the Settings API. Use this in a settings page to output
* a specific section. Should normally be called by do_settings_sections()
* rather than directly.
*
* @global array $wp_settings_fields Storage array of settings fields and their pages/sections.
*
* @param string $page Slug title of the admin page whose settings fields you want to show.
* @param string $section Slug title of the settings section whose fields you want to show.
*/
public function do_settings_fields( $page, $section ) {
global $wp_settings_fields;
if ( ! isset( $wp_settings_fields[ $page ][ $section ] ) ) {
return;
}
foreach ( (array) $wp_settings_fields[ $page ][ $section ] as $field ) {
$class = '';
$condition = '';
$style = '';
if ( ! empty( $field['args']['class'] ) ) {
$class = $field['args']['class'];
}
if ( isset( $field['args']['type'] ) ) {
$class .= ' mind-setting-type-' . $field['args']['type'];
}
if ( isset( $field['args']['conditionize'] ) && ! empty( $field['args']['conditionize'] ) ) {
$condition = ' data-cond="' . esc_attr( $field['args']['conditionize'] ) . '"';
}
if ( ! empty( $class ) ) {
$class = ' class="' . esc_attr( $class ) . '"';
}
echo "<tr{$class}{$condition}{$style}>";
if ( ! empty( $field['args']['label_for'] ) ) {
echo '<th scope="row"><label for="' . esc_attr( $field['args']['label_for'] ) . '">' . $field['title'] . '</label></th>';
} else {
echo '<th scope="row">' . $field['title'] . '</th>';
}
echo '<td>';
call_user_func( $field['callback'], $field['args'] );
echo '</td>';
echo '</tr>';
}
}
/**
* Convert condition arguments to Conditionize string.
*
* @param array $conditions - Array with Condition arguments.
* @return string
*/
public function convert_arguments_to_conditionize_string( $conditions ) {
$data_condition = '';
if ( isset( $conditions ) && is_array( $conditions ) && ! empty( $conditions ) ) {
foreach ( $conditions as $key => $condition ) {
$condition['value'] = empty( $condition['value'] ) ? "''" : ( '' . $condition['value'] );
$data_condition .= $condition['control'];
if ( isset( $condition['operator'] ) && isset( $condition['value'] ) ) {
$data_condition .= ' ' . $condition['operator'] . ' ' . $condition['value'];
}
if ( 1 < count( $conditions ) && ( count( $conditions ) - 1 ) !== $key ) {
$data_condition .= ' && ';
}
}
}
return $data_condition;
}
}

View file

@ -1,9 +1,19 @@
/**
* External Dependencies
*/
const { resolve } = require('path');
const defaultConfig = require('@wordpress/scripts/config/webpack.config'); const defaultConfig = require('@wordpress/scripts/config/webpack.config');
const isProduction = process.env.NODE_ENV === 'production'; const isProduction = process.env.NODE_ENV === 'production';
const newConfig = { const newConfig = {
...defaultConfig, ...defaultConfig,
...{
entry: {
admin: resolve(process.cwd(), 'src/admin', 'index.js'),
editor: resolve(process.cwd(), 'src/editor', 'index.js'),
},
},
// Display minimum info in terminal. // Display minimum info in terminal.
stats: 'minimal', stats: 'minimal',