Skip to content

Commit

Permalink
Merge pull request #55 from BaronVonPerko/v2.0
Browse files Browse the repository at this point in the history
V2.0
  • Loading branch information
BaronVonPerko authored Oct 7, 2021
2 parents 74b0f7a + 9537b8d commit b0edb45
Show file tree
Hide file tree
Showing 83 changed files with 11,444 additions and 1,874 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
.idea/
/vendor/
dist
node_modules
.cache
composer.phar
*.zip
4 changes: 4 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"tabWidth": 2,
"useTabs": false
}
6 changes: 6 additions & 0 deletions .wp-env.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"plugins": ["."],
"config": {
"WP_DEBUG_DISPLAY": true
}
}
Empty file modified assets/icon-128x128.png
100755 → 100644
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file modified assets/icon-256x256.png
100755 → 100644
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file modified assets/icon-500x500.png
100755 → 100644
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
69 changes: 69 additions & 0 deletions assets/lib/CustomizerUI.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Component } from "react";
import React = require("react");
import WarningBar from "./elements/WarningBar";
import { upgrade } from "./services/database";
import { fetchData } from "./services/api";
import CustomizerEditor from "./components/CustomizerEditor";
import TabPane from "./components/TabPane";
import { connect } from "react-redux";
import store, { actions } from "./redux/wpcuiReducer";
import Notification from "./components/Notification";
import Modal from "./components/Modal";
import { DatabaseObject, NavigationTab } from "./models/models";
import { getNavigationTabs } from "./services/navigation";

interface IProps {
data: DatabaseObject;
}
class CustomizerUI extends Component<IProps, null> {
constructor(props) {
super(props);

this.upgradeDatabase = this.upgradeDatabase.bind(this);
}

componentDidMount() {
fetchData().then((data) => {
store.dispatch({
type: actions.DATA_FETCH,
data,
});
});
}

upgradeDatabase() {
upgrade(this.props.data);
}

databaseUpgradeWarning() {
return (
<WarningBar
title="Database Upgrade Required"
buttonText="Upgrade Now"
buttonClick={this.upgradeDatabase}
innerText="Your database needs to be updated in order to use this version of the Customizer UI plugin. It is recommended that you create a backup of your database before continuing."
/>
);
}

render() {
if (this.props.data.db_version < 2 && this.props.data.db_version > 0) {
return this.databaseUpgradeWarning();
} else if (this.props.data.sections) {
return (
<section>
<Notification />
<Modal />
<TabPane tabs={getNavigationTabs(this.props.data)} />
</section>
);
} else {
return <p>Loading ...</p>;
}
}
}

const mapStateToProps = (state) => ({
data: state,
});
export default connect(mapStateToProps)(CustomizerUI);
59 changes: 59 additions & 0 deletions assets/lib/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Control, DatabaseObject, Settings } from "./models/models";

export function stringToSnakeCase(input: string): string {
const strArr = input.split(" ");
const snakeArr = strArr.reduce((acc, val) => {
return acc.concat(val.toLowerCase());
}, []);
return snakeArr.join("_");
}

/**
* Check across all controls in every section to see
* if the given control ID already exists.
* @param controlId
* @param data
*/
export function controlIdExists(
controlId: string,
data: DatabaseObject
): boolean {
let exists = false;

data.sections.forEach((section) => {
section.controls.forEach((control) => {
if (control.id === controlId) exists = true;
});
});

return exists;
}

/**
* Check across all sections to see if the given section ID
* is already in use.
* @param sectionId
* @param data
*/
export function sectionIdExists(
sectionId: string,
data: DatabaseObject
): boolean {
let exists = false;

data.sections.forEach((section) => {
if (section.id === sectionId) exists = true;
});

return exists;
}


/**
* Get the full control ID, including the prefix (if any).
* @param control
* @param data
*/
export function getFullControlId(control: Control, settings: Settings) {
return settings.controlPrefix ? `${settings.controlPrefix}_${control.id}` : control.id;
}
73 changes: 73 additions & 0 deletions assets/lib/components/CardHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Component } from "react";
import React = require("react");
import { CardHeaderBar } from "../styled";

interface IconButton {
function: Function;
title: string;
}

interface IProps {
onDuplicate?: IconButton;
onEdit?: IconButton;
onDelete?: IconButton;
onCode?: IconButton;
title?: string;
}

export default class CardHeader extends Component<IProps, {}> {
renderHeaderButtons() {
const buttons = [];
if (this.props.onCode) {
buttons.push(
<i
key="codeIcon"
title={this.props.onCode.title}
onClick={() => this.props.onCode.function()}
className="dashicons dashicons-editor-code"
/>
);
}
if (this.props.onDuplicate) {
buttons.push(
<i
key="duplicateIcon"
title={this.props.onDuplicate.title}
onClick={() => this.props.onDuplicate.function}
className="dashicons dashicons-admin-page"
/>
);
}
if (this.props.onEdit) {
buttons.push(
<i
key="editIcon"
title={this.props.onEdit.title}
onClick={() => this.props.onEdit.function()}
className="dashicons dashicons-edit"
/>
);
}
if (this.props.onDelete) {
buttons.push(
<i
key="deleteIcon"
title={this.props.onDelete.title}
onClick={() => this.props.onDelete.function()}
className="dashicons dashicons-trash"
/>
);
}

return buttons;
}

render() {
return (
<CardHeaderBar>
<div>{this.props.title}</div>
<div>{this.renderHeaderButtons()}</div>
</CardHeaderBar>
);
}
}
89 changes: 89 additions & 0 deletions assets/lib/components/Control.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { Component } from "react";
import { Control as CustomizerControl, Settings } from "../models/models";
import React = require("react");
import store, { actions } from "../redux/wpcuiReducer";
import CardHeader from "./CardHeader";
import { Card, CardContents } from "../styled";
import { GetControlTypeById } from "../models/selectOptions";
import { hideModal, modal } from "./Modal";
import ControlForm from "../forms/ControlForm";
import { ModalWrapper, ModalContent, CodeSample, ButtonBar } from "../styled";
import Button from "../elements/Button";
import { getFullControlId } from "../common";

interface IProps {
control: CustomizerControl;
settings: Settings;
prefix: string;
}

export default class Control extends Component<IProps, null> {
constructor(props: IProps) {
super(props);

this.delete = this.delete.bind(this);
this.edit = this.edit.bind(this);
this.showCode = this.showCode.bind(this);
}

delete() {
let res = confirm(
`Are you sure that you want to delete the control with ID of ${this.props.control.id}`
);

if (res) {
store.dispatch({
type: actions.DELETE_CONTROL,
controlId: this.props.control.id
});
}
}

copyCode() {
// @ts-ignore
document.getElementById('wpcui_sample_code').select();
document.execCommand('copy');
alert('Code copied to your clipboard!');
}

showCode() {
const id = this.props.prefix ? `${this.props.prefix}_${this.props.control.id}` : this.props.control.id;
const sample = `get_theme_mod( '${id}', '${this.props.control.default ? this.props.control.default : "Default Value"}' )`;
modal(
<ModalWrapper>
<ModalContent>
<CodeSample>
<textarea id="wpcui_sample_code" readOnly value={sample}></textarea>
<span onClick={() => this.copyCode()} title="Copy code" className="wpcui-copy-icon dashicons dashicons-admin-page"></span>
</CodeSample>

<ButtonBar>
<Button innerText="Close" click={hideModal} />
</ButtonBar>
</ModalContent>
</ModalWrapper>);
}

edit() {
modal(<ControlForm control={this.props.control} />);
}

render() {
return (
<Card>
<CardHeader
title="Control"
onDelete={{ title: "Delete Control", function: this.delete }}
onCode={{ title: "Show Code", function: this.showCode }}
onEdit={{ title: "Edit", function: this.edit }}
/>
<CardContents>
<p>Label: <strong>{this.props.control.label}</strong></p>
<p>Id: <strong>{getFullControlId(this.props.control, this.props.settings)}</strong></p>
<p>Type: <strong>{GetControlTypeById(this.props.control.type).text}</strong></p>
<p>Default Value: <strong>{this.props.control.default}</strong></p>
</CardContents>
</Card>
);
}
}
58 changes: 58 additions & 0 deletions assets/lib/components/ControlList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Component } from "react";
import Control from "./Control";
import Button from "../elements/Button";
import ControlForm from "../forms/ControlForm";
import { connect } from "react-redux";
import { modal } from "./Modal";
import { CardListWrapper } from "../styled";
import { Section, Settings } from "../models/models";
import React = require("react");

interface IProps {
selectedSection: Section;
settings: Settings;
}

class ControlList extends Component<IProps, null> {
constructor(props) {
super(props);
}

displayCreateControlForm() {
modal(<ControlForm />);
}

renderControls() {
const controls = this.props.selectedSection.controls;
if (controls && controls.length) {
return controls.map((control) => (
<Control key={control.id} control={control} settings={this.props.settings}
prefix={this.props.settings.controlPrefix} />
));
} else {
return <p>There are no controls for this section yet.</p>;
}
}

render() {
if (!this.props.selectedSection) {
return null;
}

return (
<CardListWrapper>
{this.renderControls()}
<Button
innerText="Create New Control"
click={() => this.displayCreateControlForm()}
/>
</CardListWrapper>
);
}
}

const mapStateToProps = (state): IProps => ({
selectedSection: state.selectedSection,
settings: state.settings
});
export default connect(mapStateToProps)(ControlList);
Loading

0 comments on commit b0edb45

Please sign in to comment.