add multiple-field_type

This commit is contained in:
Diatrex 2021-04-27 14:35:54 +03:00
parent bcc7625d1b
commit ba324de564
70 changed files with 3170 additions and 0 deletions

View File

@ -0,0 +1,10 @@
.coverage
composer.lock
npm-debug.log
mix-manifest.json
package-lock.json
/bin
/build
/vendor
/coverage
/node_modules

View File

@ -0,0 +1,21 @@
#The MIT License (MIT)
###Copyright (c) 2014 AnomalyLabs, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,7 @@
# Multiple Field Type
*visiosoft.field_type.multiple*
### A multiple relationship field type.
The multiple field type provides a multi-select input for a related model.

View File

@ -0,0 +1,36 @@
{
"name": "visiosoft/multiple-field_type",
"type": "streams-addon",
"description": "A multiple relationship field type.",
"keywords": [
"streams field type",
"streams addon",
"field type"
],
"homepage": "http://pyrocms.com/anomaly/multiple-field_type",
"license": "MIT",
"authors": [
{
"name": "PyroCMS, Inc.",
"homepage": "http://pyrocms.com/",
"role": "Owner"
},
{
"name": "Ryan Thompson",
"homepage": "http://ryanthepyro.com/",
"role": "Developer"
}
],
"support": {
"docs": "https://pyrocms.com/documentation/multiple-field-type",
"forum": "https://pyrocms.com/forum/channels/multiple-field-type",
"slack": "https://pyrocms.com/slack",
"issues": "https://github.com/pyrocms/pyrocms/issues?q=is:issue is:open [multiple-field-type]",
"source": "https://github.com/anomalylabs/multiple-field-type"
},
"autoload": {
"psr-4": {
"Visiosoft\\MultipleFieldType\\": "src/"
}
}
}

View File

@ -0,0 +1,20 @@
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"devDependencies": {
"choices": "^0.1.0",
"cross-env": "^5.1",
"laravel-mix": "^4.0.12",
"resolve-url-loader": "^2.3.1",
"sass": "^1.16.0",
"sass-loader": "^7.1.0"
}
}

View File

@ -0,0 +1,39 @@
<?php
use Visiosoft\MultipleFieldType\Support\Config\RelatedHandler;
return [
'related' => [
'required' => true,
'type' => 'anomaly.field_type.select',
'config' => [
'handler' => RelatedHandler::class,
],
],
'mode' => [
'required' => true,
'type' => 'anomaly.field_type.select',
'config' => [
'options' => [
'tags' => 'visiosoft.field_type.multiple::config.mode.option.tags',
'lookup' => 'visiosoft.field_type.multiple::config.mode.option.lookup',
'checkboxes' => 'visiosoft.field_type.multiple::config.mode.option.checkboxes',
],
],
],
'title_name' => [
'type' => 'anomaly.field_type.text',
],
'min' => [
'type' => 'anomaly.field_type.integer',
'config' => [
'min' => 1,
],
],
'max' => [
'type' => 'anomaly.field_type.integer',
'config' => [
'min' => 1,
],
],
];

View File

@ -0,0 +1,322 @@
/*===============================
= Choices =
===============================*/
.choices {
position: relative;
margin-bottom: 0.5rem;
font-size: 16px;
}
.choices:focus {
outline: none;
}
.choices:last-child {
margin-bottom: 0;
}
.choices.is-disabled .choices__inner, .choices.is-disabled .choices__input {
background-color: #eceeef;
cursor: not-allowed;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.choices.is-disabled .choices__item {
cursor: not-allowed;
}
.choices[data-type*=select-one] {
cursor: pointer;
}
.choices[data-type*=select-one] .choices__inner {
padding-bottom: 7.5px;
}
.choices[data-type*=select-one] .choices__input {
display: block;
width: 100%;
padding: 10px;
border-bottom: 1px solid #DDDDDD;
background-color: #FFFFFF;
margin: 0;
}
.choices[data-type*=select-one] .choices__button {
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);
padding: 0;
background-size: 8px;
height: 100%;
position: absolute;
top: 50%;
right: 0;
margin-top: -10px;
margin-right: 25px;
height: 20px;
width: 20px;
border-radius: 10em;
opacity: 0.5;
}
.choices[data-type*=select-one] .choices__button:hover, .choices[data-type*=select-one] .choices__button:focus {
opacity: 1;
}
.choices[data-type*=select-one] .choices__button:focus {
box-shadow: 0px 0px 0px 2px #11bef6;
}
.choices[data-type*=select-one]:after {
content: "";
height: 0;
width: 0;
border-style: solid;
border-color: #55595c transparent transparent transparent;
border-width: 5px;
position: absolute;
right: 11.5px;
top: 50%;
margin-top: -2.5px;
pointer-events: none;
}
.choices[data-type*=select-one].is-open:after {
border-color: transparent transparent #55595c transparent;
margin-top: -7.5px;
}
.choices[data-type*=select-one][dir=rtl]:after {
left: 11.5px;
right: auto;
}
.choices[data-type*=select-one][dir=rtl] .choices__button {
right: auto;
left: 0;
margin-left: 25px;
margin-right: 0;
}
.choices[data-type*=select-multiple] .choices__inner, .choices[data-type*=text] .choices__inner {
cursor: text;
}
.choices[data-type*=select-multiple] .choices__button, .choices[data-type*=text] .choices__button {
position: relative;
display: inline-block;
margin-top: 0;
margin-right: -4px;
margin-bottom: 0;
margin-left: 8px;
padding-left: 16px;
border-left: 1px solid #089ccc;
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);
background-size: 8px;
width: 8px;
line-height: 1;
opacity: 0.75;
}
.choices[data-type*=select-multiple] .choices__button:hover, .choices[data-type*=select-multiple] .choices__button:focus, .choices[data-type*=text] .choices__button:hover, .choices[data-type*=text] .choices__button:focus {
opacity: 1;
}
.choices__inner {
display: inline-block;
vertical-align: top;
width: 100%;
background-color: #ffffff;
padding: 7.5px 7.5px 3.75px;
border: 1px solid #DDDDDD;
border-radius: 0.1rem;
font-size: 14px;
min-height: 44px;
overflow: hidden;
}
.is-focused .choices__inner, .is-open .choices__inner {
border-color: #b7b7b7;
}
.is-open .choices__inner {
border-radius: 0.1rem 0.1rem 0 0;
}
.is-flipped.is-open .choices__inner {
border-radius: 0 0 0.1rem 0.1rem;
}
.choices__list {
margin: 0;
padding-left: 0;
list-style: none;
}
.choices__list--single {
display: inline-block;
padding: 4px 16px 4px 4px;
width: 100%;
}
[dir=rtl] .choices__list--single {
padding-right: 4px;
padding-left: 16px;
}
.choices__list--single .choices__item {
width: 100%;
}
.choices__list--multiple {
display: inline;
}
.choices__list--multiple .choices__item {
display: inline-block;
vertical-align: middle;
border-radius: 0.1rem;
padding: 4px 10px;
font-size: 12px;
font-weight: 500;
margin-right: 3.75px;
margin-bottom: 3.75px;
background-color: #11bef6;
border: 1px solid #09afe5;
color: #FFFFFF;
word-break: break-all;
}
.choices__list--multiple .choices__item[data-deletable] {
padding-right: 5px;
}
[dir=rtl] .choices__list--multiple .choices__item {
margin-right: 0;
margin-left: 3.75px;
}
.choices__list--multiple .choices__item.is-highlighted {
background-color: #09afe5;
border: 1px solid #089ccc;
}
.is-disabled .choices__list--multiple .choices__item {
background-color: #aaaaaa;
border: 1px solid #919191;
}
.choices__list--dropdown {
display: none;
z-index: 1;
position: absolute;
width: 100%;
background-color: #FFFFFF;
border: 1px solid #DDDDDD;
top: 100%;
margin-top: -1px;
border-bottom-left-radius: 0.1rem;
border-bottom-right-radius: 0.1rem;
overflow: hidden;
word-break: break-all;
}
.choices__list--dropdown.is-active {
display: block;
}
.is-open .choices__list--dropdown {
border-color: #b7b7b7;
}
.is-flipped .choices__list--dropdown {
top: auto;
bottom: 100%;
margin-top: 0;
margin-bottom: -1px;
border-radius: 0.25rem 0.25rem 0 0;
}
.choices__list--dropdown .choices__list {
position: relative;
max-height: 300px;
overflow: auto;
-webkit-overflow-scrolling: touch;
will-change: scroll-position;
}
.choices__list--dropdown .choices__item {
position: relative;
padding: 10px;
font-size: 14px;
}
[dir=rtl] .choices__list--dropdown .choices__item {
text-align: right;
}
@media (min-width: 640px) {
.choices__list--dropdown .choices__item--selectable {
padding-right: 100px;
}
.choices__list--dropdown .choices__item--selectable:after {
content: attr(data-select-text);
font-size: 12px;
opacity: 0;
position: absolute;
right: 10px;
top: 50%;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
}
[dir=rtl] .choices__list--dropdown .choices__item--selectable {
text-align: right;
padding-left: 100px;
padding-right: 10px;
}
[dir=rtl] .choices__list--dropdown .choices__item--selectable:after {
right: auto;
left: 10px;
}
}
.choices__list--dropdown .choices__item--selectable.is-highlighted {
background-color: #f2f2f2;
}
.choices__list--dropdown .choices__item--selectable.is-highlighted:after {
opacity: 0.5;
}
.choices__item {
cursor: default;
}
.choices__item--selectable {
cursor: pointer;
}
.choices__item--disabled {
cursor: not-allowed;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
opacity: 0.5;
}
.choices__heading {
font-weight: 600;
font-size: 12px;
padding: 10px;
border-bottom: 1px solid #f7f7f7;
color: gray;
}
.choices__button {
text-indent: -9999px;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
border: 0;
background-color: transparent;
background-repeat: no-repeat;
background-position: center;
cursor: pointer;
}
.choices__button:focus {
outline: none;
}
.choices__input {
display: inline-block;
vertical-align: baseline;
background-color: #ffffff;
font-size: 14px;
margin-bottom: 5px;
border: 0;
border-radius: 0;
max-width: 100%;
padding: 4px 0 4px 2px;
}
.choices__input:focus {
outline: 0;
}
[dir=rtl] .choices__input {
padding-right: 2px;
padding-left: 0;
}
.choices__placeholder {
opacity: 0.5;
}
/*===== End of Choices ======*/

View File

@ -0,0 +1,15 @@
.multiple-field_type .selected {
margin-top: 15px;
}
.multiple-field_type .selected table tr td {
padding: 0.5rem;
border-top: none;
border-bottom: none;
}
.multiple-field_type .selected table tr td:first-of-type {
width: 0;
padding-right: 15px;
}
.multiple-field_type .selected table tr td:last-of-type {
padding-left: 15px;
}

View File

@ -0,0 +1,3 @@
.multiple-field_type .choices {
z-index: 100;
}

View File

@ -0,0 +1,75 @@
/**
* Prompts the user for multiple choice
*
* `choices` are displayed in an ordered list, user is prompted with message,
* callback is returned with the index of the choice.
*
* @param prompt String Message to show users
* @param choices Array[String] Names of the choices
* @param callback Function Callback the function when a valid choice is made
*
* @install
* npm install choices
*
* @example
*
* var choices = require('choices');
* var options = ['First Option', 'Second option', 'Third option'];
* choices('Pick an option', options, function(idx) {
* console.log('You picked ' + options[idx]);
* });
*
* [1]: First option
* [2]: Second option
* [3]: Third option
* Pick an option>> 2
* You picked Second option
*
* @todo Add support for more than 9 options (key number + enter)
*
*/
var Choices = module.exports = function Choices(prompt, options, callback) {
var stdin = process.stdin;
var colors = require('colors');
if (options.length > 9) {
throw new Error('choices only supports < 9 choices');
}
options.forEach(function(name, idx) {
console.log(' ['.bold + ('' + (idx + 1)).green.bold + ']'.bold + ': ' + name);
});
process.stdout.write(prompt + '>> '.red);
stdin.setRawMode(true);
stdin.resume();
stdin.once(
'data',
function(key) {
var idx,
charCode = key.toString('utf8').charCodeAt(0);
stdin.pause();
stdin.setRawMode(false);
var re = /[\0-\x1F\x7F-\x9F\xAD\u0378\u0379\u037F-\u0383\u038B\u038D\u03A2\u0528-\u0530\u0557\u0558\u0560\u0588\u058B-\u058E\u0590\u05C8-\u05CF\u05EB-\u05EF\u05F5-\u0605\u061C\u061D\u06DD\u070E\u070F\u074B\u074C\u07B2-\u07BF\u07FB-\u07FF\u082E\u082F\u083F\u085C\u085D\u085F-\u089F\u08A1\u08AD-\u08E3\u08FF\u0978\u0980\u0984\u098D\u098E\u0991\u0992\u09A9\u09B1\u09B3-\u09B5\u09BA\u09BB\u09C5\u09C6\u09C9\u09CA\u09CF-\u09D6\u09D8-\u09DB\u09DE\u09E4\u09E5\u09FC-\u0A00\u0A04\u0A0B-\u0A0E\u0A11\u0A12\u0A29\u0A31\u0A34\u0A37\u0A3A\u0A3B\u0A3D\u0A43-\u0A46\u0A49\u0A4A\u0A4E-\u0A50\u0A52-\u0A58\u0A5D\u0A5F-\u0A65\u0A76-\u0A80\u0A84\u0A8E\u0A92\u0AA9\u0AB1\u0AB4\u0ABA\u0ABB\u0AC6\u0ACA\u0ACE\u0ACF\u0AD1-\u0ADF\u0AE4\u0AE5\u0AF2-\u0B00\u0B04\u0B0D\u0B0E\u0B11\u0B12\u0B29\u0B31\u0B34\u0B3A\u0B3B\u0B45\u0B46\u0B49\u0B4A\u0B4E-\u0B55\u0B58-\u0B5B\u0B5E\u0B64\u0B65\u0B78-\u0B81\u0B84\u0B8B-\u0B8D\u0B91\u0B96-\u0B98\u0B9B\u0B9D\u0BA0-\u0BA2\u0BA5-\u0BA7\u0BAB-\u0BAD\u0BBA-\u0BBD\u0BC3-\u0BC5\u0BC9\u0BCE\u0BCF\u0BD1-\u0BD6\u0BD8-\u0BE5\u0BFB-\u0C00\u0C04\u0C0D\u0C11\u0C29\u0C34\u0C3A-\u0C3C\u0C45\u0C49\u0C4E-\u0C54\u0C57\u0C5A-\u0C5F\u0C64\u0C65\u0C70-\u0C77\u0C80\u0C81\u0C84\u0C8D\u0C91\u0CA9\u0CB4\u0CBA\u0CBB\u0CC5\u0CC9\u0CCE-\u0CD4\u0CD7-\u0CDD\u0CDF\u0CE4\u0CE5\u0CF0\u0CF3-\u0D01\u0D04\u0D0D\u0D11\u0D3B\u0D3C\u0D45\u0D49\u0D4F-\u0D56\u0D58-\u0D5F\u0D64\u0D65\u0D76-\u0D78\u0D80\u0D81\u0D84\u0D97-\u0D99\u0DB2\u0DBC\u0DBE\u0DBF\u0DC7-\u0DC9\u0DCB-\u0DCE\u0DD5\u0DD7\u0DE0-\u0DF1\u0DF5-\u0E00\u0E3B-\u0E3E\u0E5C-\u0E80\u0E83\u0E85\u0E86\u0E89\u0E8B\u0E8C\u0E8E-\u0E93\u0E98\u0EA0\u0EA4\u0EA6\u0EA8\u0EA9\u0EAC\u0EBA\u0EBE\u0EBF\u0EC5\u0EC7\u0ECE\u0ECF\u0EDA\u0EDB\u0EE0-\u0EFF\u0F48\u0F6D-\u0F70\u0F98\u0FBD\u0FCD\u0FDB-\u0FFF\u10C6\u10C8-\u10CC\u10CE\u10CF\u1249\u124E\u124F\u1257\u1259\u125E\u125F\u1289\u128E\u128F\u12B1\u12B6\u12B7\u12BF\u12C1\u12C6\u12C7\u12D7\u1311\u1316\u1317\u135B\u135C\u137D-\u137F\u139A-\u139F\u13F5-\u13FF\u169D-\u169F\u16F1-\u16FF\u170D\u1715-\u171F\u1737-\u173F\u1754-\u175F\u176D\u1771\u1774-\u177F\u17DE\u17DF\u17EA-\u17EF\u17FA-\u17FF\u180F\u181A-\u181F\u1878-\u187F\u18AB-\u18AF\u18F6-\u18FF\u191D-\u191F\u192C-\u192F\u193C-\u193F\u1941-\u1943\u196E\u196F\u1975-\u197F\u19AC-\u19AF\u19CA-\u19CF\u19DB-\u19DD\u1A1C\u1A1D\u1A5F\u1A7D\u1A7E\u1A8A-\u1A8F\u1A9A-\u1A9F\u1AAE-\u1AFF\u1B4C-\u1B4F\u1B7D-\u1B7F\u1BF4-\u1BFB\u1C38-\u1C3A\u1C4A-\u1C4C\u1C80-\u1CBF\u1CC8-\u1CCF\u1CF7-\u1CFF\u1DE7-\u1DFB\u1F16\u1F17\u1F1E\u1F1F\u1F46\u1F47\u1F4E\u1F4F\u1F58\u1F5A\u1F5C\u1F5E\u1F7E\u1F7F\u1FB5\u1FC5\u1FD4\u1FD5\u1FDC\u1FF0\u1FF1\u1FF5\u1FFF\u200B-\u200F\u202A-\u202E\u2060-\u206F\u2072\u2073\u208F\u209D-\u209F\u20BB-\u20CF\u20F1-\u20FF\u218A-\u218F\u23F4-\u23FF\u2427-\u243F\u244B-\u245F\u2700\u2B4D-\u2B4F\u2B5A-\u2BFF\u2C2F\u2C5F\u2CF4-\u2CF8\u2D26\u2D28-\u2D2C\u2D2E\u2D2F\u2D68-\u2D6E\u2D71-\u2D7E\u2D97-\u2D9F\u2DA7\u2DAF\u2DB7\u2DBF\u2DC7\u2DCF\u2DD7\u2DDF\u2E3C-\u2E7F\u2E9A\u2EF4-\u2EFF\u2FD6-\u2FEF\u2FFC-\u2FFF\u3040\u3097\u3098\u3100-\u3104\u312E-\u3130\u318F\u31BB-\u31BF\u31E4-\u31EF\u321F\u32FF\u4DB6-\u4DBF\u9FCD-\u9FFF\uA48D-\uA48F\uA4C7-\uA4CF\uA62C-\uA63F\uA698-\uA69E\uA6F8-\uA6FF\uA78F\uA794-\uA79F\uA7AB-\uA7F7\uA82C-\uA82F\uA83A-\uA83F\uA878-\uA87F\uA8C5-\uA8CD\uA8DA-\uA8DF\uA8FC-\uA8FF\uA954-\uA95E\uA97D-\uA97F\uA9CE\uA9DA-\uA9DD\uA9E0-\uA9FF\uAA37-\uAA3F\uAA4E\uAA4F\uAA5A\uAA5B\uAA7C-\uAA7F\uAAC3-\uAADA\uAAF7-\uAB00\uAB07\uAB08\uAB0F\uAB10\uAB17-\uAB1F\uAB27\uAB2F-\uABBF\uABEE\uABEF\uABFA-\uABFF\uD7A4-\uD7AF\uD7C7-\uD7CA\uD7FC-\uF8FF\uFA6E\uFA6F\uFADA-\uFAFF\uFB07-\uFB12\uFB18-\uFB1C\uFB37\uFB3D\uFB3F\uFB42\uFB45\uFBC2-\uFBD2\uFD40-\uFD4F\uFD90\uFD91\uFDC8-\uFDEF\uFDFE\uFDFF\uFE1A-\uFE1F\uFE27-\uFE2F\uFE53\uFE67\uFE6C-\uFE6F\uFE75\uFEFD-\uFF00\uFFBF-\uFFC1\uFFC8\uFFC9\uFFD0\uFFD1\uFFD8\uFFD9\uFFDD-\uFFDF\uFFE7\uFFEF-\uFFFB\uFFFE\uFFFF]/g;
process.stdout.write(key.toString('utf8').replace(re, '') + "\n");
if (charCode == 3 || charCode == 27 || charCode == 113 || charCode == 81) {
return callback(null);
}
idx = +key;
if (idx > 0 && idx <= options.length)
return callback(idx - 1);
process.nextTick(function() {
Choices(prompt, options, callback);
});
}
);
};

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,90 @@
$(document).on('ajaxComplete ready', function () {
// Initialize multiple pickers
$('input[data-provides="visiosoft.field_type.multiple"]:not([data-initialized])').each(function () {
$(this).attr('data-initialized', '');
let input = $(this);
let field = input.data('field_name');
let wrapper = input.closest('.form-group');
let modal = $('#' + field + '-modal');
let selected = $('[name="' + field + '"]').val().split(',');
modal.on('click', '[data-entry]', function (e) {
e.preventDefault();
selected.push(String($(this).data('entry')));
$('[name="' + field + '"]').val($.unique(selected).join(','));
$(this).closest('tr').addClass('success').fadeOut();
wrapper.find('.selected').load(REQUEST_ROOT_PATH + '/streams/multiple-field_type/selected/' + $(this).data('key') + '?uploaded=' + selected.join(','), function () {
wrapper.sort();
});
$(wrapper).find('[data-dismiss="multiple"]').removeClass('hidden');
});
modal.on('click', '[name="action"][value="add_selected"]', function(e) {
e.preventDefault();
$('input[type="checkbox"][data-toggle="action"]:checked').each(function () {
selected.push(String($(this).val()));
$(this).closest('tr').addClass('success').fadeOut();
});
$('[name="' + field + '"]').val(selected.join(','));
wrapper.find('.selected').load(
REQUEST_ROOT_PATH + '/streams/multiple-field_type/selected/' + $(this).data('key') + '?uploaded=' + selected.join(','),
function() {
wrapper.sort();
}
);
});
$(wrapper).on('click', '[data-dismiss="multiple"]', function (e) {
e.preventDefault();
selected.splice(selected.indexOf(String($(this).data('entry'))), 1);
$('[name="' + field + '"]').val(selected.join(','));
$(this).closest('tr').addClass('danger').fadeOut();
});
wrapper.sort = function () {
wrapper.find('.selected table').sortable({
nested: false,
handle: '.handle',
itemSelector: 'tr',
itemPath: '> tbody',
containerSelector: 'table',
placeholder: '<tr class="placeholder"/>',
afterMove: function ($placeholder) {
$placeholder.closest('table').find('button.reorder').removeClass('disabled');
$placeholder.closest('table').find('.dragged').detach().insertBefore($placeholder);
selected = [];
$(wrapper.find('table').find('[data-dismiss="multiple"]')).each(function () {
selected.push(String($(this).data('entry')));
});
$('[name="' + field + '"]').val(selected.join(','));
}
});
}
// Sort initially
wrapper.sort();
});
});

View File

@ -0,0 +1,13 @@
(function (window, document) {
let fields = Array.prototype.slice.call(
document.querySelectorAll('select[data-provides="visiosoft.field_type.multiple"]')
);
fields.forEach(function (field) {
new Choices(field, {
removeItemButton: true,
searchResultLimit: 10,
});
});
})(window, document);

View File

@ -0,0 +1,7 @@
<?php
return [
'title' => 'متعدد',
'name' => 'نوع الحقل المتعدد',
'description' => 'نوع الحقل النتعدد العلاقات.',
];

View File

@ -0,0 +1,23 @@
<?php
return [
'related' => [
'label' => 'الدفق المرتبط',
'instructions' => 'حدد إدخالات الدفق ذات الصلة لعرضها ضمن القائمة المنسدلة.',
],
'mode' => [
'label' => 'وضخ الإدخال',
'option' => [
'tags' => 'الأوسمة',
'lookup' => 'ابحث عن',
],
],
'min' => [
'label' => 'الحد الأدنى للاختيارات',
'instructions' => 'حدد الحد الأدني للاختيارات المسموحة.',
],
'max' => [
'label' => 'الحد الأعلى للاختيارات',
'instructions' => 'حدد عدد الحد الأعلى اللاختيارات المسموحة.',
],
];

View File

@ -0,0 +1,6 @@
<?php
return [
'placeholder' => 'اختر خيار...',
'help' => 'اختر القيم بحيث يكون بينها فاصلة , ثم اضغط "ادخال".',
];

View File

@ -0,0 +1,5 @@
<?php
return [
'select_entries' => 'حدد الإدخالات التي تريد اضافتها.',
];

View File

@ -0,0 +1,7 @@
<?php
return [
'title' => 'Mehrfachbeziehungen',
'name' => 'Mehrfachbeziehungen Feldtyp',
'description' => 'Ein Feldtyp für Mehrfachbeziehungen.',
];

View File

@ -0,0 +1,16 @@
<?php
return [
'related' => [
'label' => 'Verbundener Stream',
'instructions' => 'Geben Sie den verbunden Stream an, dessen Einträge im Dropdown zur Auswahl stehen sollen.',
],
'min' => [
'label' => 'Minimalauswahl',
'instructions' => 'Geben Sie die minimale Anzahl an auszuwählenden Einträgen ein.',
],
'max' => [
'label' => 'Maximalauswahl',
'instructions' => 'Geben Sie die maximal erlaubte Anzahl an ausgewählten Einträgen an.',
],
];

View File

@ -0,0 +1,6 @@
<?php
return [
'placeholder' => 'Wählen Sie eine Option...',
'help' => 'Trennen Sie Ihre Eingaben mit einem Komma oder durch drücken der Taste Enter.',
];

View File

@ -0,0 +1,7 @@
<?php
return [
'title' => 'Multiple',
'name' => 'Multiple Field Type',
'description' => 'A multiple relationship field type.',
];

View File

@ -0,0 +1,29 @@
<?php
return [
'related' => [
'label' => 'Related Stream',
'instructions' => 'Specify the related stream entries to display in the dropdown.',
],
'mode' => [
'label' => 'Input Mode',
'option' => [
'tags' => 'Tags',
'lookup' => 'Lookup',
'checkboxes' => 'Checkboxes',
],
],
'min' => [
'label' => 'Minimum Selections',
'instructions' => 'Specify the minimum number of allowed selections.',
],
'max' => [
'label' => 'Maximum Selections',
'instructions' => 'Specify the maximum number of allowed selections.',
],
'title_name' => [
'label' => 'Title Field',
'placeholder' => 'first_name',
'instructions' => 'Specify the <strong>slug</strong> of field to display for dropdown/search options.<br>You can specify parsable titles like <strong>{entry.first_name} {entry.last_name}</strong><br>The related stream\'s title column will be used by default.',
],
];

View File

@ -0,0 +1,6 @@
<?php
return [
'placeholder' => 'Choose an option...',
'help' => 'Separate values with a comma or by pressing "Enter".',
];

View File

@ -0,0 +1,5 @@
<?php
return [
'select_entries' => 'Select the entries you would like to add.',
];

View File

@ -0,0 +1,6 @@
<?php
return [
'name' => 'Multiple',
'description' => 'Type de champs pour les relations multiples.',
];

View File

@ -0,0 +1,16 @@
<?php
return [
'related' => [
'label' => 'Stream en relation',
'instructions' => 'Choisissez les entrées à afficher dans la sélection.',
],
'min' => [
'label' => 'Nombre de sélection minimum',
'instructions' => 'Entrez un nombre minimum de sélection.',
],
'max' => [
'label' => 'Nombre de sélection maximum',
'instructions' => 'Entrez un nombre maximum de sélection.',
],
];

View File

@ -0,0 +1,6 @@
<?php
return [
'placeholder' => 'Choisissez une option...',
'help' => 'Séparez les valeurs par une virgule ou en tapant "Entrée".',
];

View File

@ -0,0 +1,7 @@
<?php
return [
'title' => 'Többszörös',
'name' => 'Többszörös Mező Típus',
'description' => 'Többszörös kapcsolat mező típus.',
];

View File

@ -0,0 +1,23 @@
<?php
return [
'related' => [
'label' => 'Releváns Folyam',
'instructions' => 'Határozd meg a releváns folyamat a legördülő megjelenítéséhez.',
],
'mode' => [
'label' => 'Bevitel Módja',
'option' => [
'tags' => 'Cimkék',
'lookup' => 'Átvizsgálás',
],
],
'min' => [
'label' => 'Minimális Kiválasztás',
'instructions' => 'Határozd meg a minimálisan kiválasztható elemek számát.',
],
'max' => [
'label' => 'Maximális Kiválasztás',
'instructions' => 'Határozd meg a maximálisan kiválasztható elemek számát.',
],
];

View File

@ -0,0 +1,6 @@
<?php
return [
'placeholder' => 'Válassz elemet...',
'help' => 'Az értékeket szóközzel, vagy enterrel válaszd el.',
];

View File

@ -0,0 +1,5 @@
<?php
return [
'select_entries' => 'Válaszd ki a bejegyzéseket amiket hozzá szeretnél adni.',
];

View File

@ -0,0 +1,7 @@
<?php
return [
'title' => 'Çoklu',
'name' => 'Çoklu Alan Tipi',
'description' => 'Bir çoklu ilişki alan tipi',
];

View File

@ -0,0 +1,23 @@
<?php
return [
'related' => [
'label' => 'İlişkili Stream',
'instructions' => 'Açılırlistede gösterilecek ilişkili stream girişlerini belirtin.',
],
'mode' => [
'label' => 'Giriş Modu',
'option' => [
'tags' => 'Etiketler',
'lookup' => 'Bakış',
],
],
'min' => [
'label' => 'Minimum seçim sayısı',
'instructions' => 'İzin verilen minimum seçim sayısını belirtin.',
],
'max' => [
'label' => 'Maksimum seçim sayısı',
'instructions' => 'İzin verilen maksimum seçim sayısını belirtin.',
],
];

View File

@ -0,0 +1,6 @@
<?php
return [
'placeholder' => 'Bir seçim yapın...',
'help' => 'Girişleri, arasına virgül koyarak veya "Enter" tuşuna basarak ayırabilirsiniz.',
];

View File

@ -0,0 +1,5 @@
<?php
return [
'select_entries' => 'Eklemek istediğiniz girişleri seçin.',
];

View File

@ -0,0 +1,7 @@
<?php
return [
'title' => '多个',
'name' => '多个字段类型',
'description' => '一个多个关系字段类型。',
];

View File

@ -0,0 +1,23 @@
<?php
return [
'related' => [
'label' => '关系表',
'instructions' => '指定在下拉框中数据所属的关系表。',
],
'mode' => [
'label' => '输入类型',
'option' => [
'tags' => '标签',
'lookup' => '查找',
],
],
'min' => [
'label' => '最少选项数',
'instructions' => '指定允许最小的选项数量。',
],
'max' => [
'label' => '最多选项数',
'instructions' => '指定允许最多的选项数量。',
],
];

View File

@ -0,0 +1,6 @@
<?php
return [
'placeholder' => '请选择一个选项',
'help' => '请使用逗号或者回车键来分开不同选项。',
];

View File

@ -0,0 +1,5 @@
<?php
return [
'select_entries' => '选择你想添加的具体实例。',
];

View File

@ -0,0 +1,7 @@
<?php
return [
'title' => '多重',
'name' => '多重欄位型別',
'description' => '一個具有多重關係的欄位型別。',
];

View File

@ -0,0 +1,23 @@
<?php
return [
'related' => [
'label' => '關連的資料流',
'instructions' => '指定在下拉選單中資料所屬的資料流。',
],
'mode' => [
'label' => '輸入模式',
'option' => [
'tags' => '標籤',
'lookup' => '查找',
],
],
'min' => [
'label' => '最小選項數量',
'instructions' => '指定允許的最小選項數量。',
],
'max' => [
'label' => '最大選項數量',
'instructions' => '指定允許的最大選項數量。',
],
];

View File

@ -0,0 +1,6 @@
<?php
return [
'placeholder' => '請選擇一個項目...',
'help' => '請使用英文逗號或換行來區別不同的項目。',
];

View File

@ -0,0 +1,5 @@
<?php
return [
'select_entries' => '選擇您想新增的項目。',
];

View File

@ -0,0 +1,340 @@
/*===============================
= Choices =
===============================*/
$choices-selector: 'choices' !default;
$choices-font-size-lg: 16px !default;
$choices-font-size-md: 14px !default;
$choices-font-size-sm: 12px !default;
$choices-guttering: 0.5rem !default;
$choices-border-radius: .1rem !default;
$choices-border-radius-item: .1rem !default;
$choices-bg-color: #ffffff !default;
$choices-bg-color-disabled: #eceeef !default;
$choices-bg-color-dropdown: #FFFFFF !default;
$choices-text-color: #55595c !default;
$choices-keyline-color: #DDDDDD !default;
$choices-primary-color: #11bef6 !default;
$choices-disabled-color: #eaeaea !default;
$choices-highlight-color: $choices-primary-color !default;
$choices-button-icon-path: '../../icons' !default;
$choices-button-dimension: 8px !default;
$choices-button-offset: 8px !default;
.#{$choices-selector} {
position: relative;
margin-bottom: $choices-guttering;
font-size: $choices-font-size-lg;
&:focus {
outline: none;
}
&:last-child {
margin-bottom: 0;
}
&.is-disabled {
.#{$choices-selector}__inner, .#{$choices-selector}__input {
background-color: $choices-bg-color-disabled;
cursor: not-allowed;
user-select: none;
}
.#{$choices-selector}__item {
cursor: not-allowed;
}
}
}
.#{$choices-selector}[data-type*="select-one"] {
cursor: pointer;
.#{$choices-selector}__inner {
padding-bottom: 7.5px;
}
.#{$choices-selector}__input {
display: block;
width: 100%;
padding: 10px;
border-bottom: 1px solid $choices-keyline-color;
background-color: #FFFFFF;
margin: 0;
}
.#{$choices-selector}__button {
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);
padding: 0;
background-size: 8px;
height: 100%;
position: absolute;
top: 50%;
right: 0;
margin-top: -10px;
margin-right: 25px;
height: 20px;
width: 20px;
border-radius: 10em;
opacity: .5;
&:hover, &:focus {
opacity: 1;
}
&:focus {
box-shadow: 0px 0px 0px 2px $choices-highlight-color;
}
}
&:after {
content: "";
height: 0;
width: 0;
border-style: solid;
border-color: $choices-text-color transparent transparent transparent;
border-width: 5px;
position: absolute;
right: 11.5px;
top: 50%;
margin-top: -2.5px;
pointer-events: none;
}
&.is-open:after {
border-color: transparent transparent $choices-text-color transparent;
margin-top: -7.5px;
}
&[dir="rtl"] {
&:after {
left: 11.5px;
right: auto;
}
.#{$choices-selector}__button {
right: auto;
left: 0;
margin-left: 25px;
margin-right: 0;
}
}
}
.#{$choices-selector}[data-type*="select-multiple"], .#{$choices-selector}[data-type*="text"] {
.#{$choices-selector}__inner {
cursor: text;
}
.#{$choices-selector}__button {
position: relative;
display: inline-block;
margin-top: 0;
margin-right: -$choices-button-offset/2;
margin-bottom: 0;
margin-left: $choices-button-offset;
padding-left: $choices-button-offset*2;
border-left: 1px solid darken($choices-primary-color, 10%);
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);
background-size: $choices-button-dimension;
width: $choices-button-dimension;
line-height: 1;
opacity: .75;
&:hover, &:focus {
opacity: 1;
}
}
}
.#{$choices-selector}__inner {
display: inline-block;
vertical-align: top;
width: 100%;
background-color: $choices-bg-color;
padding: 7.5px 7.5px 3.75px;
border: 1px solid $choices-keyline-color;
border-radius: $choices-border-radius;
font-size: $choices-font-size-md;
min-height: 44px;
overflow: hidden;
.is-focused &, .is-open & {
border-color: darken($choices-keyline-color, 15%);
}
.is-open & {
border-radius: $choices-border-radius $choices-border-radius 0 0;
}
.is-flipped.is-open & {
border-radius: 0 0 $choices-border-radius $choices-border-radius;
}
}
.#{$choices-selector}__list {
margin: 0;
padding-left: 0;
list-style: none;
}
.#{$choices-selector}__list--single {
display: inline-block;
padding: 4px 16px 4px 4px;
width: 100%;
[dir="rtl"] & {
padding-right: 4px;
padding-left: 16px;
}
.#{$choices-selector}__item {
width: 100%;
}
}
.#{$choices-selector}__list--multiple {
display: inline;
.#{$choices-selector}__item {
display: inline-block;
vertical-align: middle;
border-radius: $choices-border-radius-item;
padding: 4px 10px;
font-size: $choices-font-size-sm;
font-weight: 500;
margin-right: 3.75px;
margin-bottom: 3.75px;
background-color: $choices-primary-color;
border: 1px solid darken($choices-primary-color, 5%);
color: #FFFFFF;
word-break: break-all;
&[data-deletable] {
padding-right: 5px;
}
[dir="rtl"] & {
margin-right: 0;
margin-left: 3.75px;
}
&.is-highlighted {
background-color: darken($choices-primary-color, 5%);
border: 1px solid darken($choices-primary-color, 10%);
}
.is-disabled & {
background-color: darken($choices-disabled-color, 25%);
border: 1px solid darken($choices-disabled-color, 35%);
}
}
}
.#{$choices-selector}__list--dropdown {
display: none;
z-index: 1;
position: absolute;
width: 100%;
background-color: $choices-bg-color-dropdown;
border: 1px solid $choices-keyline-color;
top: 100%;
margin-top: -1px;
border-bottom-left-radius: $choices-border-radius;
border-bottom-right-radius: $choices-border-radius;
overflow: hidden;
word-break: break-all;
&.is-active {
display: block;
}
.is-open & {
border-color: darken($choices-keyline-color, 15%);
}
.is-flipped & {
top: auto;
bottom: 100%;
margin-top: 0;
margin-bottom: -1px;
border-radius: .25rem .25rem 0 0;
}
.#{$choices-selector}__list {
position: relative;
max-height: 300px;
overflow: auto;
-webkit-overflow-scrolling: touch;
will-change: scroll-position;
}
.#{$choices-selector}__item {
position: relative;
padding: 10px;
font-size: $choices-font-size-md;
[dir="rtl"] & {
text-align: right;
}
}
.#{$choices-selector}__item--selectable {
@media (min-width: 640px) {
padding-right: 100px;
&:after {
content: attr(data-select-text);
font-size: $choices-font-size-sm;
opacity: 0;
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
}
[dir="rtl"] & {
text-align: right;
padding-left: 100px;
padding-right: 10px;
&:after {
right: auto;
left: 10px;
}
}
}
&.is-highlighted {
background-color: mix(#000000, #FFFFFF, 5%);
&:after {
opacity: .5;
}
}
}
}
.#{$choices-selector}__item {
cursor: default;
}
.#{$choices-selector}__item--selectable {
cursor: pointer;
}
.#{$choices-selector}__item--disabled {
cursor: not-allowed;
user-select: none;
opacity: .5;
}
.#{$choices-selector}__heading {
font-weight: 600;
font-size: $choices-font-size-sm;
padding: 10px;
border-bottom: 1px solid lighten($choices-keyline-color, 10%);
color: lighten(#333, 30%);
}
.#{$choices-selector}__button {
text-indent: -9999px;
-webkit-appearance: none;
appearance: none;
border: 0;
background-color: transparent;
background-repeat: no-repeat;
background-position: center;
cursor: pointer;
&:focus {
outline: none;
}
}
.#{$choices-selector}__input {
display: inline-block;
vertical-align: baseline;
background-color: $choices-bg-color;
font-size: $choices-font-size-md;
margin-bottom: 5px;
border: 0;
border-radius: 0;
max-width: 100%;
padding: 4px 0 4px 2px;
&:focus {
outline: 0;
}
[dir="rtl"] & {
padding-right: 2px;
padding-left: 0;
}
}
.#{$choices-selector}__placeholder {
opacity: .5;
}
/*===== End of Choices ======*/

View File

@ -0,0 +1,21 @@
.multiple-field_type {
.selected {
margin-top: 15px;
table tr td {
padding: 0.5rem;
border-top: none;
border-bottom: none;
&:first-of-type {
width: 0;
padding-right: 15px;
}
&:last-of-type {
padding-left: 15px;
}
}
}
}

View File

@ -0,0 +1,5 @@
.multiple-field_type {
.choices {
z-index: 100;
}
}

View File

@ -0,0 +1,41 @@
<div class="custom-inputs-stacked">
{% for key, title in field_type.options %}
{% if title is iterable %}
<p>
<strong> {{ trans(key) }} </strong>
{% for key, title in title %}
<label class="custom-checkbox" style="display: block;">
<input
type="checkbox"
value="{{ key }}"
name="{{ field_type.input_name }}[]"
{{ field_type.disabled ? 'disabled' }}
{{ key in field_type.config.disabled ? 'disabled' }}
{{ key in field_type.config.checked ? 'checked' }}
{{ key in field_type.ids ? 'checked' }}>
{{ trans(title)|raw }}
</label>
{% endfor %}
</p>
{% else %}
<label class="custom-checkbox" style="display: block;">
<input
type="checkbox"
value="{{ key }}"
name="{{ field_type.input_name }}[]"
{{ field_type.disabled ? 'disabled' }}
{{ key in field_type.config.disabled ? 'disabled' }}
{{ key in field_type.config.checked ? 'checked' }}
{{ key in field_type.ids ? 'checked' }}>
{{ trans(title)|raw }}
</label>
{% endif %}
{% endfor %}
</div>

View File

@ -0,0 +1,11 @@
<select
class="custom-select form-control"
name="{{ field_type.input_name }}">
<option value="" disabled {{ not field_type.value ? 'selected' }}>{{ trans(field_type.placeholder) }}</option>
{% for value, option in field_type.options %}
<option value="{{ value }}" {{ value == field_type.value ? 'selected' }}>{{ trans(option) }}</option>
{% endfor %}
</select>

View File

@ -0,0 +1,18 @@
{{ asset_add('styles.css', 'visiosoft.field_type.multiple::css/lookup.css') }}
{{ asset_add('scripts.js', 'visiosoft.field_type.multiple::js/lookup.js') }}
<a data-toggle="modal" data-target="#{{ field_type.field_name }}-modal" class="btn btn-success btn-xs" href="{{ url('streams/multiple-field_type/index/' ~ field_type.key) }}">
{{ icon('search') }}
{{ trans('streams::button.select') }}</a>
<input type="hidden" name="{{ field_type.input_name }}" value="{{ field_type.ids('id')|join(',') }}" {{ html_attributes(field_type.attributes) }} {{ field_type.disabled ? 'disabled' }} {{ field_type.readonly ? 'readonly' }}>
<div class="selected">
{{ field_type.table|raw }}
</div>
<div class="modal remote" id="{{ field_type.field_name }}-modal">
<div class="modal-dialog modal-wide">
<div class="modal-content"></div>
</div>
</div>

View File

@ -0,0 +1,21 @@
<tbody>
{% for row in table.rows %}
<tr id="{{ loop.index }}">
<td style="width: 30px;">
{{ icon('fa fa-arrows handle') }}
</td>
{% for column in row.columns %}
<td class="{{ column.class }}" {{ html_attributes(column.attributes) }}>
{{ column.value|raw }}
</td>
{% endfor %}
<td class="text-lg-right buttons">
<nobr>{{ buttons(row.buttons)|raw }}</nobr>
</td>
</tr>
{% endfor %}
</tbody>

View File

@ -0,0 +1,32 @@
{{ asset_add("scripts.js", "streams::js/table/jquery-sortable.js") }}
{% if not table.rows.isEmpty() %}
{% block panel %}
<div class="{{ table.options.panel_class }} panel-table">
<div class="table-responsive">
<table
class="{{ table.options.class ?: 'table table-striped' }}"
{{ table.options.sortable ? 'data-sortable' }}
{{ html_attributes(table.options.attributes) }}>
{% block body %}
{{ view("visiosoft.field_type.multiple::table/partials/body", {'table': table}) }}
{% endblock %}
</table>
</div>
</div>
{% endblock %}
{% else %}
{% block no_results %}
<div class="{{ table.options.panel_class }}">
<div class="{{ table.options.panel_body_class }}">
{{ trans(table.options.get('no_results_message', 'streams::message.no_results')) }}
</div>
</div>
{% endblock %}
{% endif %}

View File

@ -0,0 +1,22 @@
{{ asset_add("styles.css", "visiosoft.field_type.multiple::css/choices.css", ["as:jshjohnson/Choices.css"]) }}
{{ asset_add("scripts.js", "visiosoft.field_type.multiple::js/choices.min.js", ["as:jshjohnson/Choices.js"]) }}
{{ asset_add("styles.css", "visiosoft.field_type.multiple::css/tags.css") }}
{{ asset_add("scripts.js", "visiosoft.field_type.multiple::js/tags.js") }}
<select multiple
data-key="{{ field_type.key }}"
name="{{ field_type.input_name }}[]"
data-placeholder="{{ trans(field_type.placeholder) }}"
{{ html_attributes(field_type.attributes) }}
{{ field_type.disabled ? 'disabled' }}
{{ field_type.readonly ? 'readonly' }}>
{% for key, title in field_type.options %}
<option {{ key in field_type.ids ? 'selected' }} value="{{ key }}">{{ title }}</option>
{% endfor %}
</select>
<small class="text-muted">
{{ trans('visiosoft.field_type.multiple::input.help') }}
</small>

View File

@ -0,0 +1,61 @@
<?php namespace Visiosoft\MultipleFieldType\Command;
use Visiosoft\MultipleFieldType\MultipleFieldType;
use Illuminate\Container\Container;
/**
* Class BuildOptions
*
* @link http://pyrocms.com/
* @author PyroCMS, Inc. <support@pyrocms.com>
* @author Ryan Thompson <ryan@pyrocms.com>
*/
class BuildOptions
{
/**
* The field type instance.
*
* @var MultipleFieldType
*/
protected $fieldType;
/**
* Create a new BuildOptions instance.
*
* @param MultipleFieldType $fieldType
*/
function __construct(MultipleFieldType $fieldType)
{
$this->fieldType = $fieldType;
}
/**
* Handle the command.
*
* @param Container $container
*/
public function handle(Container $container)
{
if ($options = $this->fieldType->config('options')) {
$this->fieldType->setOptions($options);
return;
}
$model = $this->fieldType->getRelatedModel();
$handler = $this->fieldType->config('handler', $model->getMultipleFieldTypeOptionsHandler());
if (!class_exists($handler) && !str_contains($handler, '@')) {
$handler = array_get($this->fieldType->getHandlers(), $handler);
}
if (is_string($handler) && !str_contains($handler, '@')) {
$handler .= '@handle';
}
$container->call($handler, ['fieldType' => $this->fieldType]);
}
}

View File

@ -0,0 +1,46 @@
<?php namespace Visiosoft\MultipleFieldType\Command;
use Anomaly\Streams\Platform\Support\Collection;
use Illuminate\Contracts\Cache\Repository;
use Illuminate\Support\Facades\Crypt;
/**
* Class GetConfiguration
*
* @link http://pyrocms.com/
* @author PyroCMS, Inc. <support@pyrocms.com>
* @author Ryan Thompson <ryan@pyrocms.com>
*/
class GetConfiguration
{
/**
* The config key.
*
* @var string
*/
protected $key;
/**
* Create a new GetConfiguration instance.
*
* @param string $key
*/
public function __construct($key)
{
$this->key = $key;
}
/**
* Handle the command.
*
* @param Repository $cache
* @return Collection
*/
public function handle(Repository $cache)
{
return new Collection(
array_merge(Crypt::decrypt($this->key), ['key' => $this->key])
);
}
}

View File

@ -0,0 +1,72 @@
<?php namespace Visiosoft\MultipleFieldType\Handler;
use Visiosoft\MultipleFieldType\MultipleFieldType;
use Anomaly\Streams\Platform\Support\Value;
/**
* Class Related
*
* @link http://pyrocms.com/
* @author PyroCMS, Inc. <support@pyrocms.com>
* @author Ryan Thompson <ryan@pyrocms.com>
*/
class Related
{
/**
* Handle the options.
*
* @param MultipleFieldType $fieldType
* @param Value $value
* @return array
*/
public function handle(MultipleFieldType $fieldType, Value $value)
{
$model = $fieldType->getRelatedModel();
$query = $model->newQuery();
$results = $query->get();
try {
/**
* Try and use a non-parsing pattern.
*/
if (strpos($fieldType->config('title_name', $model->getTitleName()), '{') === false) {
$fieldType->setOptions(
$results->pluck(
$fieldType->config('title_name', $model->getTitleName()),
$fieldType->config('key_name', $model->getKeyName())
)->all()
);
}
/**
* Try and use a parsing pattern.
*/
if (strpos($fieldType->config('title_name', $model->getTitleName()), '{') !== false) {
$fieldType->setOptions(
array_combine(
$results->map(
function ($item) use ($fieldType, $model) {
return data_get($item, $fieldType->config('key_name', $model->getKeyName()));
}
)->all(),
$results->map(
function ($item) use ($fieldType, $model, $value) {
return $value->make($fieldType->config('title_name', $model->getTitleName()), $item);
}
)->all()
)
);
}
} catch (\Exception $e) {
$fieldType->setOptions(
$results->pluck(
$model->getTitleName(),
$model->getKeyName()
)->all()
);
}
}
}

View File

@ -0,0 +1,117 @@
<?php namespace Visiosoft\MultipleFieldType\Http\Controller;
use Visiosoft\MultipleFieldType\Command\GetConfiguration;
use Visiosoft\MultipleFieldType\Command\HydrateLookupTable;
use Visiosoft\MultipleFieldType\Command\HydrateSelectedTable;
use Visiosoft\MultipleFieldType\MultipleFieldType;
use Visiosoft\MultipleFieldType\Table\LookupTableBuilder;
use Visiosoft\MultipleFieldType\Table\SelectedTableBuilder;
use Visiosoft\MultipleFieldType\Table\ValueTableBuilder;
use Anomaly\Streams\Platform\Entry\Contract\EntryInterface;
use Anomaly\Streams\Platform\Http\Controller\AdminController;
use Anomaly\Streams\Platform\Model\EloquentModel;
use Anomaly\Streams\Platform\Support\Collection;
use Illuminate\Contracts\Cache\Repository;
use Illuminate\Contracts\Container\Container;
/**
* Class LookupController
*
* @link http://pyrocms.com/
* @author PyroCMS, Inc. <support@pyrocms.com>
* @author Ryan Thompson <ryan@pyrocms.com>
*/
class LookupController extends AdminController
{
/**
* Return an index of entries from related stream.
*
* @param Container $container
* @param $key
* @return \Symfony\Component\HttpFoundation\Response
*/
public function index(Container $container, $key)
{
/* @var Collection $config */
$config = $this->dispatch(new GetConfiguration($key));
$related = $container->make($config->get('related'));
if ($table = $config->get('lookup_table')) {
$table = $container->make($table);
} else {
$table = $related->newMultipleFieldTypeLookupTableBuilder();
}
/* @var LookupTableBuilder $table */
$table->setConfig($config)
->setModel($related);
return $table->render();
}
/**
* @param Container $container
* @param MultipleFieldType $fieldType
* @param $key
*/
public function json(Container $container, MultipleFieldType $fieldType, $key)
{
/* @var Collection $config */
$config = $this->dispatch(new GetConfiguration($key));
$fieldType->mergeConfig($config->all());
/* @var EloquentModel $model */
$model = $container->make($config->get('related'));
$data = [];
/* @var EntryInterface $item */
foreach ($model->all() as $item) {
$data[] = (object)[
'id' => $item->getId(),
'text' => $item->getTitle(),
];
}
return $this->response->json($data);
}
/**
* Return the selected entries.
*
* @param SelectedTableBuilder $table
* @param MultipleFieldType $fieldType
* @param $key
* @return null|string
*/
public function selected(Container $container, MultipleFieldType $fieldType, $key)
{
/* @var Collection $config */
$config = $this->dispatch(new GetConfiguration($key));
$fieldType->mergeConfig($config->all());
$fieldType->setField($config->get('field'));
$fieldType->setEntry($this->container->make($config->get('entry')));
$related = $container->make($config->get('related'));
if ($table = $config->get('selected_table')) {
$table = $container->make($table);
} else {
$table = $related->newMultipleFieldTypeSelectedTableBuilder();
}
/* @var SelectedTableBuilder $table */
$table->setSelected(array_filter(explode(',', $this->request->get('uploaded'))))
->setModel($config->get('related'))
->setFieldType($fieldType)
->setConfig($config)
->build()
->load();
return $table->getTableContent();
}
}

View File

@ -0,0 +1,364 @@
<?php namespace Visiosoft\MultipleFieldType;
use Visiosoft\MultipleFieldType\Command\BuildOptions;
use Visiosoft\MultipleFieldType\Table\ValueTableBuilder;
use Anomaly\Streams\Platform\Addon\FieldType\FieldType;
use Anomaly\Streams\Platform\Entry\EntryCollection;
use Anomaly\Streams\Platform\Model\EloquentCollection;
use Anomaly\Streams\Platform\Model\EloquentModel;
use Anomaly\Streams\Platform\Stream\Command\GetStream;
use Anomaly\Streams\Platform\Support\Collection;
use Anomaly\Streams\Platform\Ui\Form\FormBuilder;
use Illuminate\Container\Container;
use Illuminate\Contracts\Cache\Repository;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Support\Facades\Crypt;
/**
* Class MultipleFieldType
*
* @link http://pyrocms.com/
* @author PyroCMS, Inc. <support@pyrocms.com>
* @author Ryan Thompson <ryan@pyrocms.com>
*/
class MultipleFieldType extends FieldType
{
use DispatchesJobs;
/**
* No database column.
*
* @var bool
*/
protected $columnType = false;
/**
* The input view.
*
* @var string
*/
protected $inputView = null;
/**
* The filter view.
*
* @var string
*/
protected $filterView = 'visiosoft.field_type.multiple::filter';
/**
* The pre-defined handlers.
*
* @var array
*/
protected $handlers = [
'related' => 'Visiosoft\MultipleFieldType\Handler\Related@handle',
//'fields' => 'Visiosoft\MultipleFieldType\Handler\Fields@handle',
//'assignments' => 'Visiosoft\MultipleFieldType\Handler\Assignments@handle'
];
/**
* The field type rules.
*
* @var array
*/
protected $rules = [
'array',
];
/**
* The field type config.
*
* @var array
*/
protected $config = [
'mode' => 'tags',
];
/**
* The select input options.
*
* @var null|array
*/
protected $options = null;
/**
* The cache repository.
*
* @var Repository
*/
protected $cache;
/**
* The service container.
*
* @var Container
*/
protected $container;
/**
* Create a new MultipleFieldType instance.
*
* @param Repository $cache
* @param Container $container
*/
public function __construct(Repository $cache, Container $container)
{
$this->cache = $cache;
$this->container = $container;
}
/**
* Return the ids.
*
* @return array|mixed|static
*/
public function ids()
{
$value = $this->getValue();
if (is_object($value)) {
$value = $value->pluck('id')->all();
}
return array_filter((array)$value);
}
/**
* Get the rules.
*
* @return array
*/
public function getRules()
{
$rules = parent::getRules();
if ($min = array_get($this->getConfig(), 'min')) {
$rules[] = 'min:' . $min;
}
if ($max = array_get($this->getConfig(), 'max')) {
$rules[] = 'max:' . $max;
}
return $rules;
}
/**
* Return the config key.
*
* @return string
*/
public function key()
{
return Crypt::encrypt(array_merge(
$this->getConfig(),
[
'field' => $this->getField(),
'entry' => get_class($this->getEntry()),
]
));
}
/**
* Value table.
*
* @return string
*/
public function table()
{
$value = $this->getValue();
$related = $this->getRelatedModel();
if ($table = $this->config('value_table')) {
$table = $this->container->make($table);
} else {
$table = $related->newMultipleFieldTypeValueTableBuilder();
}
/* @var ValueTableBuilder $table */
$table->setConfig(new Collection($this->getConfig()))
->setFieldType($this)
->setModel($related);
if (!$value instanceof EntryCollection) {
$table->setSelected((array)$value);
}
if ($value instanceof EntryCollection) {
$table->setSelected($value->ids());
}
return $table
->build()
->load()
->getTableContent();
}
/**
* Get the relation.
*
* @return BelongsToMany
*/
public function getRelation()
{
$entry = $this->getEntry();
$model = $this->getRelatedModel();
return $entry->belongsToMany(
get_class($model),
$this->getPivotTableName(),
'entry_id',
'related_id'
)->orderBy($this->getPivotTableName() . '.sort_order', 'ASC');
}
/**
* Get the options.
*
* @return array
*/
public function getOptions()
{
if ($this->options === null) {
$this->dispatch(new BuildOptions($this));
}
return $this->options;
}
/**
* Set the options.
*
* @param array $options
* @return $this
*/
public function setOptions(array $options)
{
$this->options = $options;
return $this;
}
/**
* Get the pre-defined handlers.
*
* @return array
*/
public function getHandlers()
{
return $this->handlers;
}
/**
* Return the input view.
*
* @return string
*/
public function getInputView()
{
return $this->inputView ?: 'visiosoft.field_type.multiple::' . $this->config('mode');
}
/**
* Get the related model.
*
* @return EloquentModel
*/
public function getRelatedModel()
{
$model = $this->config('related');
if (strpos($model, '.')) {
/* @var StreamInterface $stream */
$stream = $this->dispatch(new GetStream($model));
return $stream->getEntryModel();
}
return $this->container->make($model);
}
/**
* Get the pivot table.
*
* @return string
*/
public function getPivotTableName()
{
return $this->entry->getTableName() . '_' . $this->getField();
}
/**
* Get the post value.
*
* @param null $default
* @return array
*/
public function getPostValue($default = null)
{
if (is_array($value = parent::getPostValue($default))) {
return array_filter($value);
}
return array_filter(explode(',', $value));
}
/**
* Get the class.
*
* @return null|string
*/
public function getClass()
{
if ($class = parent::getClass()) {
return $class;
}
return $this->config('mode') == 'dropdown' ? 'custom-select form-control' : null;
}
/**
* Handle saving the form data ourselves.
*
* @param FormBuilder $builder
*/
public function handle(FormBuilder $builder)
{
$entry = $builder->getFormEntry();
// See the accessor for how IDs are handled.
$entry->{$this->getField()} = $this->getPostValue();
}
/**
* Fired just before version comparison.
*
* @param EloquentCollection $related
*/
public function toArrayForComparison(EloquentCollection $related)
{
return $related->map(
function (EloquentModel $model) {
return array_diff_key(
$model->toArrayWithRelations(),
array_flip(
[
'id',
'sort_order',
'created_at',
'created_by_id',
'updated_at',
'updated_by_id',
'deleted_at',
'deleted_by_id',
]
)
);
}
)->toArray();
}
}

View File

@ -0,0 +1,104 @@
<?php namespace Visiosoft\MultipleFieldType;
use Anomaly\Streams\Platform\Addon\FieldType\FieldTypeAccessor;
use Anomaly\Streams\Platform\Entry\Contract\EntryInterface;
use Illuminate\Database\Eloquent\Collection;
/**
* Class MultipleFieldTypeAccessor
*
* @link http://pyrocms.com/
* @author PyroCMS, Inc. <support@pyrocms.com>
* @author Ryan Thompson <ryan@pyrocms.com>
*/
class MultipleFieldTypeAccessor extends FieldTypeAccessor
{
/**
* The field type object.
* This is for IDE support.
*
* @var MultipleFieldType
*/
protected $fieldType;
/**
* Set the value.
*
* @param $value
*/
public function set($value)
{
if (is_string($value)) {
$value = $this->organizeSyncValue(explode(',', $value));
} elseif (is_array($value)) {
$value = $this->organizeSyncValue($value);
} elseif ($value instanceof Collection) {
$value = $this->organizeSyncValue($value->filter()->all());
} elseif ($value instanceof EntryInterface) {
$value = $this->organizeSyncValue([$value->getId()]);
}
if (!$value) {
$this->fieldType->getRelation()->detach();
return;
}
$this->fieldType->getRelation()->sync($value);
}
/**
* Organize the value for sync.
*
* @param array $value
* @return array
*/
protected function organizeSyncValue(array $value)
{
/**
* First clean our value.
*/
$value = array_filter(array_unique($value));
/**
* Next take the natural array
* key and make it the sort order.
*/
$value = array_combine(
array_values($value),
array_map(
function ($key) {
return [
'sort_order' => $key,
];
},
array_keys($value)
)
);
/**
* Lastly add the file_id
* relation column for sync.
*/
array_walk(
$value,
function (&$value, $key) {
$value['related_id'] = $key;
}
);
return $value;
}
/**
* Get the value.
*
* @return mixed
*/
public function get()
{
return $this->fieldType->getRelation();
}
}

View File

@ -0,0 +1,34 @@
<?php namespace Visiosoft\MultipleFieldType;
use Anomaly\Streams\Platform\Addon\FieldType\FieldTypeQuery;
use Anomaly\Streams\Platform\Ui\Table\Component\Filter\Contract\FilterInterface;
use Illuminate\Database\Eloquent\Builder;
/**
* Class MultipleFieldTypeQuery
*
* @link http://pyrocms.com/
* @author PyroCMS, Inc. <support@pyrocms.com>
* @author Ryan Thompson <ryan@pyrocms.com>
*/
class MultipleFieldTypeQuery extends FieldTypeQuery
{
/**
* Handle the filter query.
*
* @param Builder $query
* @param FilterInterface $filter
*/
public function filter(Builder $query, FilterInterface $filter)
{
$stream = $filter->getStream();
$query->leftJoin(
$stream->getEntryTableName() . '_' . $filter->getField() . ' AS filter_' . $filter->getField(),
$stream->getEntryTableName() . '.id',
'=',
'filter_' . $filter->getField() . '.entry_id'
)->where('filter_' . $filter->getField() . '.related_id', $filter->getValue());
}
}

View File

@ -0,0 +1,73 @@
<?php namespace Visiosoft\MultipleFieldType;
use Anomaly\Streams\Platform\Addon\FieldType\FieldType;
use Anomaly\Streams\Platform\Addon\FieldType\FieldTypeSchema;
use Anomaly\Streams\Platform\Assignment\Contract\AssignmentInterface;
use Illuminate\Database\Schema\Blueprint;
/**
* Class MultipleFieldTypeSchema
*
* @link http://pyrocms.com/
* @author PyroCMS, Inc. <support@pyrocms.com>
* @author Ryan Thompson <ryan@pyrocms.com>
*/
class MultipleFieldTypeSchema extends FieldTypeSchema
{
/**
* Add the field type's pivot table.
*
* @param Blueprint $table
* @param AssignmentInterface $assignment
*/
public function addColumn(Blueprint $table, AssignmentInterface $assignment)
{
$table = $table->getTable() . '_' . $this->fieldType->getField();
$this->schema->dropIfExists($table);
$this->schema->create(
$table,
function (Blueprint $table) {
$table->increments('id');
$table->integer('entry_id');
$table->integer('related_id');
$table->integer('sort_order')->nullable();
$table->unique(
['entry_id', 'related_id'],
md5($table->getTable() . '_' . $this->fieldType->getField().'-unique-relations')
);
}
);
}
/**
* Rename the pivot table.
*
* @param Blueprint $table
* @param FieldType $from
*/
public function renameColumn(Blueprint $table, FieldType $from)
{
$this->schema->rename(
$table->getTable() . '_' . $from->getField(),
$table->getTable() . '_' . $this->fieldType->getField()
);
}
/**
* Drop the pivot table.
*
* @param Blueprint $table
*/
public function dropColumn(Blueprint $table)
{
$this->schema->dropIfExists(
$table->getTable() . '_' . $this->fieldType->getField()
);
}
}

View File

@ -0,0 +1,109 @@
<?php namespace Visiosoft\MultipleFieldType;
use Visiosoft\MultipleFieldType\Handler\Related;
use Visiosoft\MultipleFieldType\Table\LookupTableBuilder;
use Visiosoft\MultipleFieldType\Table\SelectedTableBuilder;
use Visiosoft\MultipleFieldType\Table\ValueTableBuilder;
use Anomaly\Streams\Platform\Addon\AddonServiceProvider;
use Anomaly\Streams\Platform\Entry\Contract\EntryInterface;
use Anomaly\Streams\Platform\Entry\EntryModel;
use Illuminate\Contracts\Container\Container;
/**
* Class MultipleFieldTypeServiceProvider
*
* @link http://pyrocms.com/
* @author PyroCMS, Inc. <support@pyrocms.com>
* @author Ryan Thompson <ryan@pyrocms.com>
*/
class MultipleFieldTypeServiceProvider extends AddonServiceProvider
{
/**
* The singleton bindings.
*
* @var array
*/
protected $singletons = [
MultipleFieldTypeAccessor::class => MultipleFieldTypeAccessor::class,
];
/**
* The addon routes.
*
* @var array
*/
protected $routes = [
'streams/multiple-field_type/json/{key}' => 'Visiosoft\MultipleFieldType\Http\Controller\LookupController@json',
'streams/multiple-field_type/index/{key}' => 'Visiosoft\MultipleFieldType\Http\Controller\LookupController@index',
'streams/multiple-field_type/selected/{key}' => 'Visiosoft\MultipleFieldType\Http\Controller\LookupController@selected',
];
/**
* Register the addon.
*
* @param EntryModel $model
*/
public function register(EntryModel $model)
{
$model->bind(
'new_multiple_field_type_lookup_table_builder',
function (Container $container) {
/* @var EntryInterface $this */
$builder = $this->getBoundModelNamespace() . '\\Support\\MultipleFieldType\\LookupTableBuilder';
if (class_exists($builder)) {
return $container->make($builder);
}
return $container->make(LookupTableBuilder::class);
}
);
$model->bind(
'new_multiple_field_type_value_table_builder',
function (Container $container) {
/* @var EntryInterface $this */
$builder = $this->getBoundModelNamespace() . '\\Support\\MultipleFieldType\\ValueTableBuilder';
if (class_exists($builder)) {
return $container->make($builder);
}
return $container->make(ValueTableBuilder::class);
}
);
$model->bind(
'new_multiple_field_type_selected_table_builder',
function (Container $container) {
/* @var EntryInterface $this */
$builder = $this->getBoundModelNamespace() . '\\Support\\MultipleFieldType\\SelectedTableBuilder';
if (class_exists($builder)) {
return $container->make($builder);
}
return $container->make(SelectedTableBuilder::class);
}
);
$model->bind(
'get_multiple_field_type_options_handler',
function () {
/* @var EntryInterface $this */
$handler = $this->getBoundModelNamespace() . '\\Support\\MultipleFieldType\\OptionsHandler';
if (class_exists($handler)) {
return $handler;
}
return Related::class;
}
);
}
}

View File

@ -0,0 +1,45 @@
<?php namespace Visiosoft\MultipleFieldType\Support\Config;
use Anomaly\SelectFieldType\SelectFieldType;
use Anomaly\Streams\Platform\Stream\Contract\StreamInterface;
use Anomaly\Streams\Platform\Stream\Contract\StreamRepositoryInterface;
/**
* Class RelatedHandler
*
* @link http://pyrocms.com/
* @author PyroCMS, Inc. <support@pyrocms.com>
* @author Ryan Thompson <ryan@pyrocms.com>
*/
class RelatedHandler
{
/**
* Handle the options.
*
* @param SelectFieldType $fieldType
* @param StreamRepositoryInterface $streams
*/
public function handle(SelectFieldType $fieldType, StreamRepositoryInterface $streams)
{
$options = [];
/* @var StreamInterface as $stream */
foreach ($streams->visible() as $stream) {
$addon = ucwords(str_replace('_', ' ', $stream->getNamespace()));
$model = $stream->getEntryModelName();
array_set($options, "{$addon}.{$model}", $stream->getName());
}
foreach ($options as $namespace) {
ksort($namespace);
}
ksort($options);
$fieldType->setOptions($options);
}
}

View File

@ -0,0 +1,29 @@
<?php namespace Visiosoft\MultipleFieldType\Table;
/**
* Class LookupTableActions
*
* @link http://pyrocms.com/
* @author PyroCMS, Inc. <support@pyrocms.com>
* @author Ryan Thompson <ryan@pyrocms.com>
*/
class LookupTableActions
{
/**
* Handle the command.
*
* @param LookupTableBuilder $builder
*/
public function handle(LookupTableBuilder $builder)
{
$builder->setActions(
[
'add_selected' => [
'data-key' => $builder->config('key'),
],
]
);
}
}

View File

@ -0,0 +1,102 @@
<?php namespace Visiosoft\MultipleFieldType\Table;
use Anomaly\Streams\Platform\Support\Collection;
use Anomaly\Streams\Platform\Ui\Table\TableBuilder;
/**
* Class LookupTableBuilder
*
* @link http://pyrocms.com/
* @author PyroCMS, Inc. <support@pyrocms.com>
* @author Ryan Thompson <ryan@pyrocms.com>
*/
class LookupTableBuilder extends TableBuilder
{
/**
* The field type configuration.
*
* @var null|Collection
*/
protected $config = null;
/**
* The ajax flag.
*
* @var bool
*/
protected $ajax = true;
/**
* The table filters.
*
* @var string
*/
protected $filters = LookupTableFilters::class;
/**
* The table columns.
*
* @var string
*/
protected $columns = LookupTableColumns::class;
/**
* The table buttons.
*
* @var string
*/
protected $buttons = LookupTableButtons::class;
/**
* The table actions.
*
* @var string
*/
protected $actions = LookupTableActions::class;
/**
* The table options.
*
* @var array
*/
protected $options = [
'sortable' => false,
'title' => 'visiosoft.field_type.multiple::message.select_entries',
];
/**
* Return a config value.
*
* @param $key
* @param null $default
* @return mixed
*/
public function config($key, $default = null)
{
return $this->config->get($key, $default);
}
/**
* Get the config.
*
* @return Collection|null
*/
public function getConfig()
{
return $this->config;
}
/**
* Set the config.
*
* @param Collection $config
* @return $this
*/
public function setConfig(Collection $config)
{
$this->config = $config;
return $this;
}
}

View File

@ -0,0 +1,31 @@
<?php namespace Visiosoft\MultipleFieldType\Table;
/**
* Class LookupTableButtons
*
* @link http://pyrocms.com/
* @author PyroCMS, Inc. <support@pyrocms.com>
* @author Ryan Thompson <ryan@pyrocms.com>
*/
class LookupTableButtons
{
/**
* Handle the command.
*
* @param LookupTableBuilder $builder
*/
public function handle(LookupTableBuilder $builder)
{
$builder->setButtons(
[
'add' => [
'data-entry' => 'entry.id',
'data-key' => $builder->config('key'),
],
]
);
}
}

View File

@ -0,0 +1,38 @@
<?php namespace Visiosoft\MultipleFieldType\Table;
/**
* Class LookupTableColumns
*
* @link http://pyrocms.com/
* @author PyroCMS, Inc. <support@pyrocms.com>
* @author Ryan Thompson <ryan@pyrocms.com>
*/
class LookupTableColumns
{
/**
* Handle the command.
*
* @param LookupTableBuilder $builder
*/
public function handle(LookupTableBuilder $builder)
{
$stream = $builder->getTableStream();
$column = $stream->getTitleColumn();
if ($column == 'id') {
$builder->setColumns([]);
return;
}
$builder->setColumns(
[
$column,
]
);
}
}

View File

@ -0,0 +1,42 @@
<?php namespace Visiosoft\MultipleFieldType\Table;
/**
* Class LookupTableFilters
*
* @link http://pyrocms.com/
* @author PyroCMS, Inc. <support@pyrocms.com>
* @author Ryan Thompson <ryan@pyrocms.com>
*/
class LookupTableFilters
{
/**
* Handle the command.
*
* @param LookupTableBuilder $builder
*/
public function handle(LookupTableBuilder $builder)
{
$stream = $builder->getTableStream();
$filter = $stream->getTitleColumn();
if ($filter == 'id') {
$builder->setFilters([]);
return;
}
$builder->setFilters(
[
'search' => [
'fields' => [
$filter,
],
],
]
);
}
}

View File

@ -0,0 +1,182 @@
<?php namespace Visiosoft\MultipleFieldType\Table;
use Visiosoft\MultipleFieldType\MultipleFieldType;
use Anomaly\Streams\Platform\Support\Collection;
use Anomaly\Streams\Platform\Ui\Table\TableBuilder;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
/**
* Class SelectedTableBuilder
*
* @link http://pyrocms.com/
* @author PyroCMS, Inc. <support@pyrocms.com>
* @author Ryan Thompson <ryan@pyrocms.com>
*/
class SelectedTableBuilder extends TableBuilder
{
/**
* The field type configuration.
*
* @var null|Collection
*/
protected $config = null;
/**
* The field type.
*
* @var null|MultipleFieldType
*/
protected $fieldType = null;
/**
* The selected entry.
*
* @var array
*/
protected $selected = [];
/**
* The table buttons.
*
* @var array
*/
protected $buttons = [
'remove' => [
'data-dismiss' => 'multiple',
'data-entry' => 'entry.id',
],
];
/**
* The table options.
*
* @var array
*/
protected $options = [
'limit' => 9999,
'show_headers' => false,
'sortable_headers' => false,
'table_view' => 'visiosoft.field_type.multiple::table/table',
];
/**
* Fired just before querying.
*
* @param Builder $query
*/
public function onQuerying(Builder $query)
{
$uploaded = $this->getSelected();
$fieldType = $this->getFieldType();
/*
* If we have the entry available then
* we can determine saved sort order.
*/
$related = $fieldType->getRelatedModel();
$query->whereIn($related->getTableName() . '.id', $uploaded ?: 0);
}
/**
* Return a config value.
*
* @param $key
* @param null $default
* @return mixed
*/
public function config($key, $default = null)
{
return $this->config->get($key, $default);
}
/**
* Get the config.
*
* @return Collection|null
*/
public function getConfig()
{
return $this->config;
}
/**
* Set the config.
*
* @param Collection $config
* @return $this
*/
public function setConfig(Collection $config)
{
$this->config = $config;
return $this;
}
/**
* Get the selected value.
*
* @return array
*/
public function getSelected()
{
return $this->selected;
}
/**
* Get the selected value.
*
* @param array $selected
* @return $this
*/
public function setSelected(array $selected)
{
$this->selected = $selected;
return $this;
}
/**
* Get the field type.
*
* @return MultipleFieldType|null
*/
public function getFieldType()
{
return $this->fieldType;
}
/**
* Set the field type.
*
* @param MultipleFieldType $fieldType
* @return $this
*/
public function setFieldType(MultipleFieldType $fieldType)
{
$this->fieldType = $fieldType;
return $this;
}
/**
* Set the table entries.
*
* @param \Illuminate\Support\Collection $entries
* @return $this
*/
public function setTableEntries(\Illuminate\Support\Collection $entries)
{
if (!$this->getFieldType()) {
$entries = $entries->sort(
function ($a, $b) {
return array_search($a->id, $this->getSelected()) - array_search($b->id, $this->getSelected());
}
);
}
return parent::setTableEntries($entries);
}
}

View File

@ -0,0 +1,189 @@
<?php namespace Visiosoft\MultipleFieldType\Table;
use Visiosoft\MultipleFieldType\MultipleFieldType;
use Anomaly\Streams\Platform\Support\Collection;
use Anomaly\Streams\Platform\Ui\Table\TableBuilder;
use Illuminate\Database\Eloquent\Builder;
/**
* Class ValueTableBuilder
*
* @link http://pyrocms.com/
* @author PyroCMS, Inc. <support@pyrocms.com>
* @author Ryan Thompson <ryan@pyrocms.com>
*/
class ValueTableBuilder extends TableBuilder
{
/**
* The field type configuration.
*
* @var null|Collection
*/
protected $config = null;
/**
* The field type.
*
* @var null|MultipleFieldType
*/
protected $fieldType = null;
/**
* The selected entry.
*
* @var array
*/
protected $selected = [];
/**
* The table buttons.
*
* @var array
*/
protected $buttons = [
'remove' => [
'data-dismiss' => 'multiple',
'data-entry' => 'entry.id',
],
];
/**
* The table options.
*
* @var array
*/
protected $options = [
'limit' => 9999,
'show_headers' => false,
'sortable_headers' => false,
'table_view' => 'visiosoft.field_type.multiple::table/table',
];
/**
* Fired just before querying.
*
* @param Builder $query
*/
public function onQuerying(Builder $query)
{
$uploaded = $this->getSelected();
$fieldType = $this->getFieldType();
/*
* If we have the entry available then
* we can determine saved sort order.
*/
$table = $fieldType->getPivotTableName();
$related = $fieldType->getRelatedModel();
$entry = $fieldType->getEntry();
if ($entry->getId() && $related && !$uploaded) {
$query->join($table, $table . '.related_id', '=', $related->getTableName() . '.id');
$query->where($table . '.entry_id', $entry->getId());
$query->orderBy($table . '.sort_order', 'ASC');
} elseif ($related) {
$query->whereIn($related->getTableName() . '.id', $uploaded ?: [0]);
}
}
/**
* Return a config value.
*
* @param $key
* @param null $default
* @return mixed
*/
public function config($key, $default = null)
{
return $this->config->get($key, $default);
}
/**
* Get the config.
*
* @return Collection|null
*/
public function getConfig()
{
return $this->config;
}
/**
* Set the config.
*
* @param Collection $config
* @return $this
*/
public function setConfig(Collection $config)
{
$this->config = $config;
return $this;
}
/**
* Get the selected value.
*
* @return array
*/
public function getSelected()
{
return $this->selected;
}
/**
* Get the selected value.
*
* @param array $selected
* @return $this
*/
public function setSelected(array $selected)
{
$this->selected = $selected;
return $this;
}
/**
* Get the field type.
*
* @return MultipleFieldType|null
*/
public function getFieldType()
{
return $this->fieldType;
}
/**
* Set the field type.
*
* @param MultipleFieldType $fieldType
* @return $this
*/
public function setFieldType(MultipleFieldType $fieldType)
{
$this->fieldType = $fieldType;
return $this;
}
/**
* Set the table entries.
*
* @param \Illuminate\Support\Collection $entries
* @return $this
*/
public function setTableEntries(\Illuminate\Support\Collection $entries)
{
if (!$this->getFieldType()) {
$entries = $entries->sort(
function ($a, $b) {
return array_search($a->id, $this->getSelected()) - array_search($b->id, $this->getSelected());
}
);
}
return parent::setTableEntries($entries);
}
}

View File

@ -0,0 +1,57 @@
let mix = require('laravel-mix');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
mix
.copy('node_modules/choices/choices.js', 'resources/js')
.sass('resources/scss/choices.scss', 'resources/css')
.sass('resources/scss/lookup.scss', 'resources/css')
.sass('resources/scss/tags.scss', 'resources/css')
.options({
processCssUrls: false
});
// Full API
// mix.js(src, output);
// mix.react(src, output); <-- Identical to mix.js(), but registers React Babel compilation.
// mix.preact(src, output); <-- Identical to mix.js(), but registers Preact compilation.
// mix.coffee(src, output); <-- Identical to mix.js(), but registers CoffeeScript compilation.
// mix.ts(src, output); <-- TypeScript support. Requires tsconfig.json to exist in the same folder as webpack.mix.js
// mix.extract(vendorLibs);
// mix.sass(src, output);
// mix.less(src, output);
// mix.stylus(src, output);
// mix.postCss(src, output, [require('postcss-some-plugin')()]);
// mix.browserSync('my-site.test');
// mix.combine(files, destination);
// mix.babel(files, destination); <-- Identical to mix.combine(), but also includes Babel compilation.
// mix.copy(from, to);
// mix.copyDirectory(fromDir, toDir);
// mix.minify(file);
// mix.sourceMaps(); // Enable sourcemaps
// mix.version(); // Enable versioning.
// mix.disableNotifications();
// mix.setPublicPath('path/to/public');
// mix.setResourceRoot('prefix/for/resource/locators');
// mix.autoload({}); <-- Will be passed to Webpack's ProvidePlugin.
// mix.webpackConfig({}); <-- Override webpack.config.js, without editing the file directly.
// mix.babelConfig({}); <-- Merge extra Babel configuration (plugins, etc.) with Mix's default.
// mix.then(function () {}) <-- Will be triggered each time Webpack finishes building.
// mix.extend(name, handler) <-- Extend Mix's API with your own components.
// mix.options({
// extractVueStyles: false, // Extract .vue component styling to file, rather than inline.
// globalVueStyles: file, // Variables file to be imported in every component.
// processCssUrls: true, // Process/optimize relative stylesheet url()'s. Set to false, if you don't want them touched.
// purifyCss: false, // Remove unused CSS selectors.
// terser: {}, // Terser-specific options. https://github.com/webpack-contrib/terser-webpack-plugin#options
// postCss: [] // Post-CSS options: https://github.com/postcss/postcss/blob/master/docs/plugins.md
// });