This commit is contained in:
2026-02-03 15:31:29 +05:00
commit 326c677e8d
2800 changed files with 1489388 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
/.idea
/vendor
/node_modules
package-lock.json
composer.phar
composer.lock
phpunit.xml
.phpunit.result.cache
.DS_Store
Thumbs.db

View File

@@ -0,0 +1,29 @@
{
"name": "nurmuhammet/dynamic-fields",
"description": "A Laravel Nova field.",
"keywords": [
"laravel",
"nova"
],
"license": "MIT",
"require": {
"php": "^7.3|^8.0"
},
"autoload": {
"psr-4": {
"Nurmuhammet\\DynamicFields\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"Nurmuhammet\\DynamicFields\\FieldServiceProvider"
]
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@@ -0,0 +1 @@

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,23 @@
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <http://feross.org>
* @license MIT
*/
/*!
* vuex v4.1.0
* (c) 2022 Evan You
* @license MIT
*/
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
/**
* @license
* Lodash <https://lodash.com/>
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/

View File

@@ -0,0 +1,4 @@
{
"/js/field.js": "/js/field.js",
"/css/field.css": "/css/field.css"
}

View File

@@ -0,0 +1,40 @@
const mix = require('laravel-mix')
const webpack = require('webpack')
const path = require('path')
class NovaExtension {
name() {
return 'nova-extension'
}
register(name) {
this.name = name
}
webpackPlugins() {
return new webpack.ProvidePlugin({
_: 'lodash',
Errors: 'form-backend-validation',
})
}
webpackConfig(webpackConfig) {
webpackConfig.externals = {
vue: 'Vue',
}
webpackConfig.resolve.alias = {
...(webpackConfig.resolve.alias || {}),
'laravel-nova': path.join(
__dirname,
'../../vendor/laravel/nova/resources/js/mixins/packages.js'
),
}
webpackConfig.output = {
uniqueName: this.name,
}
}
}
mix.extend('nova', new NovaExtension())

View File

@@ -0,0 +1,22 @@
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "mix",
"watch": "mix watch",
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "mix --production",
"nova:install": "npm --prefix='../../vendor/laravel/nova' ci"
},
"devDependencies": {
"@vue/compiler-sfc": "^3.2.22",
"form-backend-validation": "^2.3.3",
"laravel-mix": "^6.0.41",
"lodash": "^4.17.21",
"postcss": "^8.3.11",
"vue-loader": "^16.8.3"
},
"dependencies": {}
}

View File

@@ -0,0 +1 @@
module.exports = {}

View File

@@ -0,0 +1 @@
/* Nova Field CSS */

View File

@@ -0,0 +1,107 @@
<template>
<div>
<div v-for="userField in userFields">
<DefaultField :field="currentFieldFor(userField.name)" :fieldName="userField.label" :errors="errors">
<template #field>
<template v-if="userField.type == 'select'">
<select
:id="userField.name"
v-model="values[userField.name]"
class="w-full block form-control form-input form-control-bordered"
disabled
>
<option value="">Saýla</option>
<option v-for="option in userField.options" :value="option.value">
{{ option.label }}
</option>
</select>
</template>
<template v-else>
<input
:id="userField.name"
:type="userField.type"
class="w-full form-control form-input form-control-bordered"
:class="errorClasses"
v-model="values[userField.name]"
:placeholder="userField.placeholder"
disabled
/>
</template>
<p v-if="hasError" class="my-2 text-danger">
{{ firstError }}
</p>
</template>
</DefaultField>
</div>
</div>
</template>
<script>
import { DependentFormField, HandlesValidationErrors } from 'laravel-nova'
import { capitalize } from 'lodash'
export default {
mixins: [DependentFormField, HandlesValidationErrors],
props: ['resourceName', 'resourceId', 'field'],
data() {
return {
userFields: [],
values: [],
fillWithArrayName: '',
value: '',
}
},
methods: {
/*
* Set the initial, internal value for the field.
*/
setInitialValue() {
this.value = this.field.value || ''
},
setValueFor(name, value) {
this.values[name] = value
},
currentFieldFor(name) {
const new_field = JSON.parse(JSON.stringify(this.currentField))
new_field.name = capitalize(name)
let userField = this.userFields.filter(field => field.name == name)[0]
if (userField['placeholder']) {
new_field.placeholder = userField['placeholder']
}
return new_field
}
},
computed: {
placeholder() {
return this.__('Choose an option')
}
},
mounted() {
this.fillWithArrayName = this.field.fillWithArrayName;
this.userFields = this.field.fields.map(field => {
this.values[field.name.toLowerCase()] = field.default ? field.default : ''
return {
type: field.type,
name: field.name.toLowerCase(),
label: field.label ? capitalize(field.label) : capitalize(field.name),
default: field.default,
placeholder: field.placeholder,
options: field.options
}
})
}
}
</script>

View File

@@ -0,0 +1,139 @@
<template>
<div>
<div v-for="userField in userFields">
<DefaultField :field="currentFieldFor(userField.name)" :fieldName="userField.label" :errors="errors">
<template #field>
<template v-if="userField.type == 'select'">
<select
:id="userField.name"
v-model="values[userField.name]"
:required="userField.required"
class="w-full block form-control form-input form-control-bordered"
>
<option value="">Saýla</option>
<option v-for="option in userField.options" :value="option.value">
{{ option.label }}
</option>
</select>
</template>
<template v-else>
<input
:id="userField.name"
:type="userField.type"
class="w-full form-control form-input form-control-bordered"
:class="errorClasses"
v-model="values[userField.name]"
:required="userField.required"
:placeholder="userField.placeholder"
/>
</template>
<p v-if="hasError" class="my-2 text-danger">
{{ firstError }}
</p>
</template>
</DefaultField>
</div>
</div>
</template>
<script>
import { DependentFormField, HandlesValidationErrors } from 'laravel-nova'
import { capitalize } from 'lodash'
export default {
mixins: [DependentFormField, HandlesValidationErrors],
props: ['resourceName', 'resourceId', 'field'],
data() {
return {
userFields: [],
values: [],
fillWithArrayName: '',
value: '',
}
},
methods: {
/*
* Set the initial, internal value for the field.
*/
setInitialValue() {
this.value = this.field.value || ''
},
/**
* Fill the given FormData object with the field's internal value.
*/
fill(formData) {
if (this.fillWithArrayName) {
this.userFields.forEach(field => {
formData.append(this.fillWithArrayName + '[' + field['name'] + ']', this.values[field['name']])
})
return
}
this.userFields.forEach(field => {
formData.append(field['name'], this.values[field['name']])
})
},
setValueFor(name, value) {
this.values[name] = value
},
currentFieldFor(name) {
const new_field = JSON.parse(JSON.stringify(this.currentField))
new_field.name = capitalize(name)
let userField = this.userFields.filter(field => field.name == name)[0]
if (userField['placeholder']) {
new_field.placeholder = userField['placeholder']
}
if (userField['required']) {
new_field.required = userField['required']
}
return new_field
},
onSyncedField() {
this.userFields = this.formatFields()
},
formatFields() {
return this.currentField.fields.map(field => {
this.values[field.name.toLowerCase()] = this.values[field.name.toLowerCase()] || (field.default || '')
return {
type: field.type,
name: field.name.toLowerCase(),
label: field.label ? capitalize(field.label) : capitalize(field.name),
default: field.default,
required: field.required,
placeholder: field.placeholder,
options: field.options
}
})
}
},
computed: {
placeholder() {
return this.__('Choose an option')
}
},
mounted() {
this.fillWithArrayName = this.field.fillWithArrayName
this.currentField.fields = this.currentField.fields || [];
this.userFields = this.formatFields()
}
}
</script>

View File

@@ -0,0 +1,7 @@
import DetailField from './components/DetailField'
import FormField from './components/FormField'
Nova.booting((app, store) => {
app.component('detail-dynamic-fields', DetailField)
app.component('form-dynamic-fields', FormField)
})

View File

@@ -0,0 +1,58 @@
<?php
namespace Nurmuhammet\DynamicFields;
use Laravel\Nova\Exceptions\NovaException;
use Laravel\Nova\Fields\Field;
use Laravel\Nova\Fields\SupportsDependentFields;
class DynamicFields extends Field
{
use SupportsDependentFields;
/**
* The field's component.
*
* @var string
*/
public $component = 'dynamic-fields';
/**
* Specify that the element should be visible on the index view.
*
* @param (callable():bool)|bool $callback
* @return $this
*
* @throws \Laravel\Nova\Exceptions\NovaException
*/
public function showOnIndex($callback = true)
{
throw NovaException::helperNotSupported(__FUNCTION__, static::class);
}
/**
* Fields to be rendered
*
* @param array $hues
* @return $this
*/
public function fields(array|callable $fields)
{
$fieldsForFrontEnd = $fields;
if (is_callable($fields)) {
$fieldsForFrontEnd = call_user_func($fields);
}
return $this->withMeta(['fields' => $fieldsForFrontEnd]);
}
/**
* Fill with array name
*
* @return $this
*/
public function fillWithArrayName(string $requestArrayName = '')
{
return $this->withMeta(['fillWithArrayName' => $requestArrayName]);
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Nurmuhammet\DynamicFields;
use Illuminate\Support\ServiceProvider;
use Laravel\Nova\Events\ServingNova;
use Laravel\Nova\Nova;
class FieldServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Nova::serving(function (ServingNova $event) {
Nova::script('dynamic-fields', __DIR__.'/../dist/js/field.js');
Nova::style('dynamic-fields', __DIR__.'/../dist/css/field.css');
});
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
}

View File

@@ -0,0 +1,10 @@
let mix = require('laravel-mix')
require('./nova.mix')
mix
.setPublicPath('dist')
.js('resources/js/field.js', 'js')
.vue({ version: 3 })
.css('resources/css/field.css', 'css')
.nova('nurmuhammet/dynamic-fields')