├── .editorconfig ├── .github └── workflows │ └── php.yml ├── .gitignore ├── .phpunit-watcher.yml ├── LICENSE ├── README.md ├── composer.json ├── frontend ├── .eslintrc.js ├── .gitignore ├── assets │ ├── .gitkeep │ ├── index.js │ └── style.css ├── gulpfile.esm.js ├── index.html ├── package.json ├── postcss.config.js ├── public │ ├── favicon.ico │ ├── images │ │ ├── spinner-2x.gif │ │ ├── spinner.gif │ │ ├── stars-2x.png │ │ └── stars.png │ ├── wp-includes │ │ └── fonts │ │ │ ├── dashicons.eot │ │ │ ├── dashicons.svg │ │ │ ├── dashicons.ttf │ │ │ ├── dashicons.woff │ │ │ └── dashicons.woff2 │ └── wp.css ├── src │ ├── App.vue │ ├── assets │ │ └── logo.png │ ├── components │ │ ├── Spinner.vue │ │ ├── Windsor.vue │ │ ├── WindsorError.vue │ │ ├── WindsorFieldGroupItem.vue │ │ ├── WindsorFieldGroups.vue │ │ ├── WindsorHeader.vue │ │ ├── WindsorSidebar.vue │ │ ├── WindsorYamlPreview.vue │ │ └── useLoadYaml.js │ ├── index.css │ ├── main.js │ ├── store.js │ └── utils │ │ ├── ajax.js │ │ ├── clipboard.js │ │ ├── config.js │ │ └── repository.js ├── tailwind.config.js ├── vite.config.js └── yarn.lock ├── phpcs.xml ├── screenshot.png ├── src ├── AcfBlock.php ├── AcfServiceProvider.php ├── Admin │ ├── Exporter │ │ ├── CompactRules │ │ │ ├── CompactAccordion.php │ │ │ ├── CompactButtonGroup.php │ │ │ ├── CompactCheckbox.php │ │ │ ├── CompactClone.php │ │ │ ├── CompactField.php │ │ │ ├── CompactFieldGroup.php │ │ │ ├── CompactFile.php │ │ │ ├── CompactFlexibleContent.php │ │ │ ├── CompactGallery.php │ │ │ ├── CompactGoogleMap.php │ │ │ ├── CompactHelper.php │ │ │ ├── CompactImage.php │ │ │ ├── CompactLayout.php │ │ │ ├── CompactMessage.php │ │ │ ├── CompactNumber.php │ │ │ ├── CompactOEmbed.php │ │ │ ├── CompactPageLink.php │ │ │ ├── CompactPostObject.php │ │ │ ├── CompactRadio.php │ │ │ ├── CompactRange.php │ │ │ ├── CompactRelationship.php │ │ │ ├── CompactRepeater.php │ │ │ ├── CompactRichEditor.php │ │ │ ├── CompactSelect.php │ │ │ ├── CompactTab.php │ │ │ ├── CompactTaxonomy.php │ │ │ ├── CompactTextArea.php │ │ │ ├── CompactTrueFalse.php │ │ │ └── CompactUser.php │ │ ├── FieldGroupsStore.php │ │ ├── FieldsPacker.php │ │ ├── MutableField.php │ │ ├── MutableFieldCollection.php │ │ └── YamlComposer.php │ └── WordPress │ │ ├── AjaxHandler.php │ │ └── UiLoader.php ├── Capsule │ ├── AbstractCapsule.php │ ├── Block.php │ ├── BlueprintBuilder.php │ ├── BlueprintsFactory.php │ ├── FieldGroup.php │ └── Manager.php ├── Contracts │ └── ParserContract.php ├── Parser │ ├── Finder.php │ └── YamlParser.php ├── Rules │ ├── FieldConditionRule.php │ ├── FieldDefaultsRule.php │ ├── GroupLocationRule.php │ ├── HelperRule.php │ ├── Utilities │ │ └── TransformConditionalLogic.php │ └── WrapperShortcuts.php ├── Support │ ├── Config.php │ ├── Fluent.php │ ├── RulesCollector.php │ └── Singleton.php ├── config.php └── helpers.php └── tests ├── BaseTestCase.php ├── BlockSimpleTest.php ├── BlueprintsTest.php ├── ChangeInstructionsToFoo.php ├── CompactRulesTest.php ├── ConditionalTest.php ├── DummyHandler.php ├── FieldsPackerTest.php ├── KeysTest.php ├── MutableFieldTest.php ├── RulesTest.php ├── SetupTest.php ├── bootstrap.php ├── raw ├── default.php ├── simple-conditional.php ├── simple-flex-content.php ├── simple-group.php └── simple-repeater.php └── yaml ├── blocks ├── block-with-handler.acf.yaml └── sample-block.acf.yaml ├── blueprints ├── my-blueprint.acf.yaml ├── test-default.acf.yaml ├── test-exclude.acf.yaml ├── test-layout.acf.yaml ├── test-merge.acf.yaml ├── test-only.acf.yaml └── test-prefix.acf.yaml ├── clones.acf.yaml ├── index-empty.yaml ├── index.yaml ├── keys-static.acf.yaml ├── keys.acf.yaml ├── rules ├── conditional-rules.acf.yaml ├── default-rules.acf.yaml ├── helper-rules.acf.yaml └── wrapper-rules.acf.yaml ├── sample-field.acf.yaml ├── sample-page.acf.yaml ├── sample-post.acf.yaml ├── simple.acf.yaml └── sub ├── fields └── empty-field.acf.yaml └── index.yaml /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.php] 13 | indent_size = 4 14 | -------------------------------------------------------------------------------- /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: PHP Composer 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Validate composer.json and composer.lock 18 | run: composer validate 19 | 20 | - name: Cache Composer packages 21 | id: composer-cache 22 | uses: actions/cache@v2 23 | with: 24 | path: vendor 25 | key: ${{ runner.os }}-node-${{ hashFiles('**/composer.lock') }} 26 | restore-keys: | 27 | ${{ runner.os }}-node- 28 | 29 | - name: Install dependencies 30 | run: composer install --prefer-dist --no-progress --no-suggest 31 | 32 | # Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit" 33 | # Docs: https://getcomposer.org/doc/articles/scripts.md 34 | 35 | - name: Run test suite 36 | run: composer run-script test 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Composer 2 | composer.phar 3 | composer.lock 4 | /vendor/ 5 | 6 | # OS files 7 | .DS_Store 8 | 9 | # Environment file 10 | .env 11 | .env.*.php 12 | .env.php 13 | 14 | # Logs 15 | php_errors.log 16 | nginx-error.log 17 | nginx-access.log 18 | nginx-ssl.access.log 19 | nginx-ssl.error.log 20 | php-errors.log 21 | yarn-error.log 22 | 23 | # IDE specific 24 | nbproject 25 | .phpintel 26 | .idea 27 | _ide_helper.php 28 | .phpstorm.meta.php 29 | .vscode -------------------------------------------------------------------------------- /.phpunit-watcher.yml: -------------------------------------------------------------------------------- 1 | phpunit: 2 | binaryPath: vendor/bin/phpunit 3 | arguments: 'tests --bootstrap tests/bootstrap.php --colors=always' 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ACF Windsor 2 | 3 | [![Latest Stable Version](https://poser.pugx.org/jofrysutanto/windsor/v)](//packagist.org/packages/jofrysutanto/windsor) [![Total Downloads](https://poser.pugx.org/jofrysutanto/windsor/downloads)](//packagist.org/packages/jofrysutanto/windsor) [![Latest Unstable Version](https://poser.pugx.org/jofrysutanto/windsor/v/unstable)](//packagist.org/packages/jofrysutanto/windsor) [![License](https://poser.pugx.org/jofrysutanto/windsor/license)](//packagist.org/packages/jofrysutanto/windsor) 4 | 5 | This package extends [Advanced Custom Fields](https://advancedcustomfields.com) plugin for WordPress and enable developers to write their ACF fields blazingly fast in configuration file. 6 | 7 | ![ACF Windsor](https://raw.githubusercontent.com/jofrysutanto/windsor/master/screenshot.png) 8 | 9 | ### Features 10 | - Permanently lock your custom fields in your version-controlled code, preventing accidental edits that quickly leads to out-of-sync configurations. 11 | - Create your fields much faster, especially when complimented with the IDE integration. 12 | - Composition is at the heart of it. Write your own rules to further supercharge your development productivity. 13 | 14 | ### Getting Started 15 | 16 | - The easiest way to install Windsor is to use composer: 17 | ```sh 18 | composer require jofrysutanto/windsor 19 | ``` 20 | - If you are using VSCode, be sure to add the Schema file to your configuration. 21 | - Ensure you have included composer auto-loader file. If you're not sure, add the following line into your `functions.php` file: 22 | ```php 23 | require_once __DIR__ . '/vendor/autoload.php'; 24 | ``` 25 | - Register Windsor on ACF initialization. You may also do this in `functions.php` file: 26 | ```php 27 | function register_acf_windsor() 28 | { 29 | \Windsor\Capsule\Manager::make()->register(); 30 | } 31 | add_action('acf/init', 'register_acf_windsor'); 32 | ``` 33 | - Create YAML entry file at `[your-active-theme]/acf-fields/index.yaml`, where `[your-active-theme]` refers to your currently active WordPress theme directory. At minimum, your entry file should contain: 34 | ```yaml 35 | fields: [] 36 | pages: [] 37 | blocks: [] 38 | ``` 39 | - Test your installation: 40 | - Create your first custom field YAML, for example create a file `your-theme/acf-fields/page-default.acf.yaml`: 41 | ```yaml 42 | title: 'Page Default' 43 | key: 'page_default' 44 | position: 'acf_after_title' 45 | hide_on_screen: [] 46 | location: 47 | - 48 | - 49 | param: 'page_template' 50 | operator: '==' 51 | value: 'default' 52 | fields: 53 | heading: 54 | type: text 55 | label: Heading 56 | ``` 57 | - Register this new ACF file in your index: 58 | ```yaml 59 | fields: [] 60 | pages: 61 | - page-default.acf.yaml 62 | blocks: [] 63 | ``` 64 | - You have successfully registered a new field group which will be made available when creating a new default page. 65 | - Check out our full documentation below. Now go and create beautiful ACF fields! 66 | 67 | ## Migrating Existing Fields 68 | If you have existing field groups created through ACF interface, you can easily export them out to YAML by enabling the exporter through `ui` configuration when registering Windsor: 69 | ```php 70 | function register_acf_windsor() 71 | { 72 | \Windsor\Capsule\Manager::make([ 73 | 'ui' => true 74 | ]) 75 | ->register(); 76 | } 77 | add_action('acf/init', 'register_acf_windsor'); 78 | ``` 79 | 80 | Once enabled, you access the exporter within WordPress backend by clicking on Custom Fields > Export to YAML link in sidebar. 81 | 82 | More information about this tool can be found in [configurations section](https://windsor-docs.netlify.app/configurations.html#ui). 83 | 84 | ## Learn More 85 | Check out full documentations at [https://windsor-docs.netlify.app/](https://windsor-docs.netlify.app/) 86 | 87 | ## IDE Integration 88 | 89 | Only VSCode integration is available at the moment. To enable autocompletion and useful snippets, follow the installation steps below: 90 | - If not already installed, download and enable [YAML language server](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) extension. 91 | - Update your VSCode [settings](https://code.visualstudio.com/docs/getstarted/settings#_settings-file-locations) (i.e. `settings.json`): 92 | ```json 93 | "yaml.schemas": { 94 | "https://windsor-docs.netlify.app/schema.json": "*.acf.yaml" 95 | } 96 | ``` 97 | 98 | ## Credits 99 | 100 | This package is written to be used with [Advanced Custom Fields](https://www.advancedcustomfields.com/) plugin by [Elliot Condon](https://www.elliotcondon.com/), who deserves most of the credits for delivering and maintaining such an incredible plugin for WordPress developers. 101 | 102 | If you have not already started using Advanced Custom Fields, be sure to check it out; it will definitely be worth your while. 103 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jofrysutanto/windsor", 3 | "description": "YAML-ised Configuration for ACF", 4 | "homepage": "https://github.com/jofrysutanto/windsor", 5 | "type": "package", 6 | "keywords": [ 7 | "wordpress", "advanced-custom-fields" 8 | ], 9 | "license": "Apache-2.0", 10 | "authors": [ 11 | { 12 | "name": "Jofry Sutanto", 13 | "email": "jofrysutanto@gmail.com" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=7.0.0", 18 | "symfony/yaml": "~3.4|~4.0|~5.0", 19 | "tightenco/collect": "^8.0" 20 | }, 21 | "require-dev": { 22 | "squizlabs/php_codesniffer": "^3.2", 23 | "phpunit/phpunit": "^9", 24 | "mockery/mockery": "^1.4", 25 | "spatie/phpunit-watcher": "^1.22" 26 | }, 27 | "autoload": { 28 | "psr-4": { 29 | "Windsor\\": "src/" 30 | }, 31 | "files": [ 32 | "src/helpers.php" 33 | ] 34 | }, 35 | "scripts": { 36 | "test": [ 37 | "phpcs --extensions=php --standard=PSR2 src/", 38 | "vendor/bin/phpunit tests --bootstrap tests/bootstrap.php" 39 | ], 40 | "fix": [ 41 | "phpcbf src/" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /frontend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'root': true, 3 | 'extends': [ 4 | 'eslint:recommended', 5 | 'plugin:vue/essential', 6 | ], 7 | 'globals': { 8 | 'wp': true, 9 | }, 10 | 'env': { 11 | 'node': true, 12 | 'es6': true, 13 | 'amd': true, 14 | 'browser': true, 15 | 'jquery': true, 16 | }, 17 | 'parserOptions': { 18 | 'parser': 'babel-eslint', 19 | 'ecmaFeatures': { 20 | 'globalReturn': true, 21 | 'generators': false, 22 | 'objectLiteralDuplicateProperties': false 23 | }, 24 | 'ecmaVersion': 2017, 25 | 'sourceType': 'module', 26 | }, 27 | 'plugins': [ 28 | 'import', 29 | ], 30 | 'settings': { 31 | 'import/core-modules': [], 32 | 'import/ignore': [ 33 | 'node_modules', 34 | '\\.(coffee|scss|css|less|hbs|svg|json)$', 35 | ], 36 | }, 37 | 'rules': { 38 | 'no-console': 0, 39 | 'quotes': ['error', 'single'], 40 | 'comma-dangle': [ 41 | 'error', 42 | { 43 | 'arrays': 'always-multiline', 44 | 'objects': 'always-multiline', 45 | 'imports': 'always-multiline', 46 | 'exports': 'always-multiline', 47 | 'functions': 'ignore', 48 | }, 49 | ], 50 | }, 51 | }; 52 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | *.local -------------------------------------------------------------------------------- /frontend/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jofrysutanto/windsor/0a130f2920283ea42033777600aec9d5a45b88e1/frontend/assets/.gitkeep -------------------------------------------------------------------------------- /frontend/gulpfile.esm.js: -------------------------------------------------------------------------------- 1 | const { join, extname, basename } = require('path') 2 | const { readdir, stat, copyFileSync } = require('fs') 3 | const { promisify } = require('util') 4 | const readdirP = promisify(readdir) 5 | const statP = promisify(stat) 6 | 7 | const config = { 8 | src: './dist/_assets', 9 | output: './assets/', 10 | copy: [ 11 | 'index.js', 12 | 'style.css', 13 | ] 14 | } 15 | 16 | /** 17 | * Scan and retrieve all files from given directory 18 | * @param {String} dir 19 | * @param {Array} allFiles 20 | */ 21 | async function rreaddir(dir, allFiles = []) { 22 | const files = (await readdirP(dir)).map(f => join(dir, f)) 23 | allFiles.push(...files) 24 | await Promise.all( 25 | files.map( 26 | async f => (await statP(f)).isDirectory() && rreaddir(f, allFiles) 27 | ) 28 | ) 29 | return allFiles 30 | } 31 | 32 | /** 33 | * Clean up given file path by removing hash. 34 | * e.g. 'dist/_assets/index.abcdefg.js' -> 'index.js' 35 | * @param {String} filepath 36 | */ 37 | function removeFileHash (filepath) { 38 | let fragments = basename(filepath).split('.') 39 | fragments.splice(fragments.length - 2, 1) 40 | return fragments.join('.') 41 | } 42 | 43 | /** 44 | * Copy and normalise compiled Vite files 45 | * @param {Closure} cb 46 | */ 47 | async function copy (cb) { 48 | let assets = await rreaddir(config.src) 49 | assets.forEach(filepath => { 50 | if (!extname(filepath)) { 51 | return 52 | } 53 | if (!(filepath.endsWith('.js') || (filepath.endsWith('.css')))) { 54 | return 55 | } 56 | let filename = removeFileHash(filepath) 57 | if (config.copy.indexOf(filename) === -1) { 58 | return 59 | } 60 | let destination = `${config.output}/${filename}` 61 | copyFileSync(filepath, destination) 62 | }) 63 | cb(); 64 | } 65 | 66 | exports.build = copy 67 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Vite App 11 | 12 | 13 |
14 | 59 |
60 |
61 | 76 |
77 |
78 |
79 |
80 |
81 |
82 | 83 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build && gulp build" 7 | }, 8 | "dependencies": { 9 | "@vueuse/core": "^4.0.0-beta.2", 10 | "axios": "^0.21.1", 11 | "file-saver": "^2.0.2", 12 | "vue": "^3.0.0-rc.1" 13 | }, 14 | "devDependencies": { 15 | "@tailwindcss/ui": "^0.3.1", 16 | "@vue/compiler-sfc": "^3.0.0-rc.1", 17 | "autoprefixer": "^9.8.5", 18 | "eslint": "^7.5.0", 19 | "esm": "^3.2.25", 20 | "gulp": "^4.0.2", 21 | "tailwindcss": "^1.5.1", 22 | "vite": "^1.0.0-rc.1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('tailwindcss'), 4 | require('autoprefixer'), 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jofrysutanto/windsor/0a130f2920283ea42033777600aec9d5a45b88e1/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/images/spinner-2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jofrysutanto/windsor/0a130f2920283ea42033777600aec9d5a45b88e1/frontend/public/images/spinner-2x.gif -------------------------------------------------------------------------------- /frontend/public/images/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jofrysutanto/windsor/0a130f2920283ea42033777600aec9d5a45b88e1/frontend/public/images/spinner.gif -------------------------------------------------------------------------------- /frontend/public/images/stars-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jofrysutanto/windsor/0a130f2920283ea42033777600aec9d5a45b88e1/frontend/public/images/stars-2x.png -------------------------------------------------------------------------------- /frontend/public/images/stars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jofrysutanto/windsor/0a130f2920283ea42033777600aec9d5a45b88e1/frontend/public/images/stars.png -------------------------------------------------------------------------------- /frontend/public/wp-includes/fonts/dashicons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jofrysutanto/windsor/0a130f2920283ea42033777600aec9d5a45b88e1/frontend/public/wp-includes/fonts/dashicons.eot -------------------------------------------------------------------------------- /frontend/public/wp-includes/fonts/dashicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jofrysutanto/windsor/0a130f2920283ea42033777600aec9d5a45b88e1/frontend/public/wp-includes/fonts/dashicons.ttf -------------------------------------------------------------------------------- /frontend/public/wp-includes/fonts/dashicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jofrysutanto/windsor/0a130f2920283ea42033777600aec9d5a45b88e1/frontend/public/wp-includes/fonts/dashicons.woff -------------------------------------------------------------------------------- /frontend/public/wp-includes/fonts/dashicons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jofrysutanto/windsor/0a130f2920283ea42033777600aec9d5a45b88e1/frontend/public/wp-includes/fonts/dashicons.woff2 -------------------------------------------------------------------------------- /frontend/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | -------------------------------------------------------------------------------- /frontend/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jofrysutanto/windsor/0a130f2920283ea42033777600aec9d5a45b88e1/frontend/src/assets/logo.png -------------------------------------------------------------------------------- /frontend/src/components/Spinner.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 17 | 18 | 35 | -------------------------------------------------------------------------------- /frontend/src/components/Windsor.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 101 | -------------------------------------------------------------------------------- /frontend/src/components/WindsorError.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 23 | 26 | -------------------------------------------------------------------------------- /frontend/src/components/WindsorFieldGroupItem.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 76 | -------------------------------------------------------------------------------- /frontend/src/components/WindsorFieldGroups.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 35 | -------------------------------------------------------------------------------- /frontend/src/components/WindsorHeader.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 52 | -------------------------------------------------------------------------------- /frontend/src/components/WindsorSidebar.vue: -------------------------------------------------------------------------------- 1 | 141 | 142 | 190 | -------------------------------------------------------------------------------- /frontend/src/components/WindsorYamlPreview.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 84 | 85 | 87 | -------------------------------------------------------------------------------- /frontend/src/components/useLoadYaml.js: -------------------------------------------------------------------------------- 1 | import { onMounted, computed, ref, toRefs } from 'vue' 2 | import { useGlobalState } from "./../store"; 3 | 4 | export const useLoadYaml = (fieldKey) => { 5 | const { yamlFields, isLoadingField } = toRefs(useGlobalState()) 6 | const { loadField } = useGlobalState() 7 | let hasError = ref(false) 8 | const fieldYaml = computed(() => { 9 | return yamlFields.value[fieldKey.value] 10 | }) 11 | onMounted(async () => { 12 | try { 13 | await loadField(fieldKey.value) 14 | } catch (error) { 15 | console.error(error) 16 | hasError.value = true 17 | } 18 | }) 19 | let isLoadingYaml = computed(() => { 20 | return isLoadingField.value === fieldKey.value 21 | }) 22 | return { 23 | isLoadingYaml, 24 | hasError, 25 | fieldYaml 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /frontend/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | 3 | @tailwind components; 4 | 5 | @tailwind utilities; 6 | 7 | body.custom-fields_page_windsor #wpcontent { 8 | padding-left: 0; 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import Windsor from './components/Windsor.vue' 3 | import './index.css' 4 | 5 | createApp(Windsor).mount('#windsor') 6 | -------------------------------------------------------------------------------- /frontend/src/store.js: -------------------------------------------------------------------------------- 1 | import { createGlobalState } from '@vueuse/core' 2 | import { ref, watch } from 'vue' 3 | import repository from './utils/repository' 4 | 5 | export const useGlobalState = createGlobalState( 6 | () => { 7 | let isModeCompact = ref(true) 8 | let indentation = ref(2) 9 | let yamlFields = ref({}) 10 | let activePreview = ref(null) 11 | let isLoadingField = ref(null) 12 | 13 | const changeMode = (mode) => { 14 | isModeCompact.value = mode 15 | } 16 | 17 | const changeIndentation = (newVal) => { 18 | indentation.value = newVal 19 | } 20 | 21 | const loadField = async (key) => { 22 | if (typeof yamlFields.value[key] !== 'undefined') { 23 | return 24 | } 25 | isLoadingField.value = key 26 | let { yaml } = await repository.fetchSingle({ 27 | key, 28 | indent: indentation.value, 29 | mode: (isModeCompact.value === true) ? 'compact' : 'full' 30 | }) 31 | yamlFields.value[key] = yaml 32 | isLoadingField.value = null 33 | } 34 | 35 | watch([isModeCompact, indentation], () => { 36 | yamlFields.value = {} 37 | if (activePreview.value) { 38 | loadField(activePreview.value) 39 | } 40 | }) 41 | 42 | return { 43 | isModeCompact, 44 | indentation, 45 | isLoadingField, 46 | yamlFields, 47 | activePreview, 48 | changeMode, 49 | changeIndentation, 50 | loadField, 51 | } 52 | }, 53 | ) 54 | -------------------------------------------------------------------------------- /frontend/src/utils/ajax.js: -------------------------------------------------------------------------------- 1 | import config from './config' 2 | import axios from 'axios' 3 | export default { 4 | post (params) { 5 | // Use FormData instead of normal POST parameters, 6 | // @see https://wordpress.stackexchange.com/questions/282163/wordpress-ajax-with-axios 7 | let form_data = new FormData; 8 | for (const key in params) { 9 | if (params.hasOwnProperty(key)) { 10 | form_data.append(key, params[key]) 11 | } 12 | } 13 | return axios.post(config.ajaxurl, form_data, { 14 | ...this.buildHeaders() 15 | }) 16 | }, 17 | buildHeaders () { 18 | return { 19 | headers: { 20 | 'Content-Type': 'application/json' 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /frontend/src/utils/clipboard.js: -------------------------------------------------------------------------------- 1 | export const copyToClipboard = (str) => { 2 | /* ——— Derived from: https://hackernoon.com/copying-text-to-clipboard-with-javascript-df4d4988697f 3 | improved to add iOS device compatibility——— */ 4 | const el = document.createElement('textarea'); // Create a