mirror of
https://github.com/frappe/books.git
synced 2024-11-09 23:30:56 +00:00
added date control
This commit is contained in:
parent
bc781728aa
commit
1f5e3d8fcb
@ -249,7 +249,13 @@ module.exports = class Database {
|
||||
let values = fields.map(field => {
|
||||
let value = doc[field.fieldname];
|
||||
if (value instanceof Date) {
|
||||
return value.toISOString();
|
||||
if (field.fieldtype==='Date') {
|
||||
// date
|
||||
return value.toISOString().substr(0, 10);
|
||||
} else {
|
||||
// datetime
|
||||
return value.toISOString();
|
||||
}
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ module.exports = class FormPage extends Page {
|
||||
doctype: doctype,
|
||||
parent: this.body,
|
||||
container: this,
|
||||
actions: ['submit', 'delete']
|
||||
actions: ['submit', 'delete', 'duplicate', 'settings']
|
||||
});
|
||||
|
||||
this.on('show', async (params) => {
|
||||
|
@ -17,9 +17,9 @@ module.exports = class Desk {
|
||||
this.container = frappe.ui.add('div', 'container-fluid', body);
|
||||
|
||||
this.containerRow = frappe.ui.add('div', 'row', this.container)
|
||||
this.sidebar = frappe.ui.add('div', 'col-md-2 p-3 sidebar d-none d-md-block', this.containerRow);
|
||||
this.sidebar = frappe.ui.add('div', 'col-md-2 sidebar d-none d-md-block', this.containerRow);
|
||||
this.sidebarList = frappe.ui.add('div', 'list-group list-group-flush', this.sidebar);
|
||||
this.body = frappe.ui.add('div', 'col-md-10 p-4 main', this.containerRow);
|
||||
this.body = frappe.ui.add('div', 'col-md-10 main', this.containerRow);
|
||||
|
||||
this.pages = {
|
||||
lists: {},
|
||||
|
@ -22,7 +22,7 @@ module.exports = class Navbar {
|
||||
this.nav = frappe.ui.add('ul', 'navbar-nav mr-auto', this.navbar_collapse);
|
||||
}
|
||||
|
||||
add_item(label, route) {
|
||||
addItem(label, route) {
|
||||
let item = frappe.ui.add('li', 'nav-item', this.nav);
|
||||
item.link = frappe.ui.add('a', 'nav-link', item);
|
||||
item.link.textContent = label;
|
||||
|
@ -1,28 +1,36 @@
|
||||
@import "node_modules/bootstrap/scss/bootstrap";
|
||||
@import "node_modules/awesomplete/awesomplete";
|
||||
@import "node_modules/flatpickr/dist/flatpickr";
|
||||
@import "node_modules/flatpickr/dist/themes/airbnb";
|
||||
|
||||
$spacer-1: 0.25rem;
|
||||
$spacer-2: 0.5rem;
|
||||
$spacer-3: 1rem;
|
||||
$spacer-4: 2rem;
|
||||
|
||||
$page-width: 500px;
|
||||
|
||||
html {
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.main {
|
||||
margin-left: -1px;
|
||||
border-left: 1px solid $gray-300;
|
||||
min-height: calc(100vh - 50px);
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
margin-right: -1px;
|
||||
|
||||
.list-group-item {
|
||||
padding: $spacer-2 $spacer-3;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.list-group-flush {
|
||||
margin: -$spacer-3;
|
||||
margin-left: -$spacer-3;
|
||||
margin-right: -$spacer-3;
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +38,13 @@ html {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.page {
|
||||
max-width: $page-width;
|
||||
padding-bottom: $spacer-4;
|
||||
}
|
||||
|
||||
.page-head {
|
||||
padding-top: $spacer-3;
|
||||
padding-bottom: $spacer-3;
|
||||
|
||||
.btn {
|
||||
@ -44,7 +58,7 @@ html {
|
||||
}
|
||||
|
||||
.form-body {
|
||||
max-width: 600px;
|
||||
max-width: $page-width;
|
||||
|
||||
.form-toolbar {
|
||||
height: $spacer-4;
|
||||
@ -56,19 +70,27 @@ html {
|
||||
}
|
||||
}
|
||||
|
||||
.list-row {
|
||||
.checkbox {
|
||||
margin-right: $spacer-2;
|
||||
}
|
||||
|
||||
a, a:hover, a:visited, a:active {
|
||||
color: $gray-800;
|
||||
.list-body {
|
||||
margin-left: -15px;
|
||||
margin-right: -15px;
|
||||
|
||||
.list-row {
|
||||
padding: $spacer-2;
|
||||
border-bottom: 1px solid $gray-200;
|
||||
.checkbox {
|
||||
margin-right: $spacer-2;
|
||||
}
|
||||
|
||||
a, a:hover, a:visited, a:active {
|
||||
color: $gray-800;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu-right {
|
||||
right: 0;
|
||||
left: auto;
|
||||
|
||||
.dropdown-item {
|
||||
padding: $spacer-2 $spacer-3;
|
||||
}
|
||||
|
||||
.awesomplete {
|
||||
@ -114,6 +136,11 @@ mark {
|
||||
margin-top: $spacer-2;
|
||||
}
|
||||
|
||||
.data-table thead td {
|
||||
border-bottom: 0px !important;
|
||||
background-color: $gray-200 !important;
|
||||
}
|
||||
|
||||
.data-table-col .edit-cell {
|
||||
padding: 0px !important;
|
||||
|
||||
|
@ -1,91 +1,55 @@
|
||||
const frappe = require('frappejs');
|
||||
const bootstrap = require('bootstrap');
|
||||
const $ = require('jquery');
|
||||
|
||||
class Dropdown {
|
||||
constructor({parent, label, btn_class = 'btn-secondary', items = []}) {
|
||||
constructor({parent, label, items = [], right}) {
|
||||
Object.assign(this, arguments[0]);
|
||||
Dropdown.instances += 1;
|
||||
this.id = 'dropdownMenuButton-' + Dropdown.instances;
|
||||
|
||||
this.dropdown_items = [];
|
||||
this.setup_background_click();
|
||||
this.make();
|
||||
|
||||
// init items
|
||||
if (this.items) {
|
||||
for (item of this.items) {
|
||||
this.add_item(item.label, item.action);
|
||||
for (let item of this.items) {
|
||||
this.addItem(item.label, item.action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setup_background_click() {
|
||||
if (!document.dropdown_setup) {
|
||||
frappe.dropdowns = [];
|
||||
// setup hiding all dropdowns on click
|
||||
document.addEventListener('click', (event) => {
|
||||
for (let d of frappe.dropdowns) {
|
||||
if (d.button !== event.target) {
|
||||
d.collapse();
|
||||
}
|
||||
}
|
||||
});
|
||||
document.dropdown_setup = true;
|
||||
}
|
||||
frappe.dropdowns.push(this);
|
||||
}
|
||||
|
||||
make() {
|
||||
this.dropdown = frappe.ui.add('div', 'dropdown', this.parent);
|
||||
this.make_button();
|
||||
this.dropdown_menu = frappe.ui.add('div', 'dropdown-menu', this.dropdown);
|
||||
this.$dropdown = $(`<div class="dropdown ${this.right ? 'float-right' : ''}">
|
||||
<button class="btn btn-outline-secondary dropdown-toggle"
|
||||
type="button" id="${this.id}" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="false">${this.label}
|
||||
</button>
|
||||
<div class="dropdown-menu ${this.right ? 'dropdown-menu-right' : ''}" aria-labelledby="${this.id}"></div>
|
||||
</div>`).appendTo(this.parent)
|
||||
this.dropdown = this.$dropdown.get(0);
|
||||
this.dropdownMenu = this.dropdown.querySelector('.dropdown-menu');
|
||||
}
|
||||
|
||||
make_button() {
|
||||
this.button = frappe.ui.add('button', 'btn ' + this.btn_class,
|
||||
this.dropdown);
|
||||
frappe.ui.add_class(this.button, 'dropdown-toggle');
|
||||
this.button.textContent = this.label;
|
||||
this.button.addEventListener('click', () => {
|
||||
this.toggle();
|
||||
});
|
||||
}
|
||||
|
||||
expand() {
|
||||
this.dropdown.classList.add('show');
|
||||
this.dropdown_menu.classList.add('show');
|
||||
}
|
||||
|
||||
collapse() {
|
||||
this.dropdown.classList.remove('show');
|
||||
this.dropdown_menu.classList.remove('show');
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.dropdown.classList.toggle('show');
|
||||
this.dropdown_menu.classList.toggle('show');
|
||||
}
|
||||
|
||||
add_item(label, action) {
|
||||
let item = frappe.ui.add('button', 'dropdown-item', this.dropdown_menu);
|
||||
addItem(label, action) {
|
||||
let item = frappe.ui.add('button', 'dropdown-item', this.dropdownMenu);
|
||||
item.textContent = label;
|
||||
item.setAttribute('type', 'button');
|
||||
if (typeof action === 'string') {
|
||||
item.src = action;
|
||||
item.addEventListener('click', async () => {
|
||||
await frappe.router.setRoute(action);
|
||||
this.toggle();
|
||||
});
|
||||
} else {
|
||||
item.addEventListener('click', async () => {
|
||||
await action();
|
||||
this.toggle();
|
||||
});
|
||||
}
|
||||
this.dropdown_items.push(item);
|
||||
}
|
||||
|
||||
float_right() {
|
||||
frappe.ui.add_class(this.dropdown, 'float-right');
|
||||
frappe.ui.add_class(this.dropdown_menu, 'dropdown-menu-right');
|
||||
floatRight() {
|
||||
frappe.ui.addClass(this.dropdown, 'float-right');
|
||||
}
|
||||
}
|
||||
|
||||
Dropdown.instances = 0;
|
||||
|
||||
module.exports = Dropdown;
|
@ -6,7 +6,7 @@ module.exports = {
|
||||
let element = document.createElement(tag);
|
||||
if (className) {
|
||||
for (let c of className.split(' ')) {
|
||||
this.add_class(element, c);
|
||||
this.addClass(element, c);
|
||||
}
|
||||
}
|
||||
if (parent) {
|
||||
@ -19,7 +19,7 @@ module.exports = {
|
||||
element.parentNode.removeChild(element);
|
||||
},
|
||||
|
||||
add_class(element, className) {
|
||||
addClass(element, className) {
|
||||
if (element.classList) {
|
||||
element.classList.add(className);
|
||||
} else {
|
||||
|
@ -64,7 +64,7 @@ class BaseControl {
|
||||
}
|
||||
|
||||
makeInput() {
|
||||
this.input = frappe.ui.add('input', 'form-control', this.get_input_parent());
|
||||
this.input = frappe.ui.add('input', 'form-control', this.getInputParent());
|
||||
this.input.autocomplete = "off";
|
||||
this.input.id = this.id;
|
||||
}
|
||||
@ -75,7 +75,7 @@ class BaseControl {
|
||||
}
|
||||
}
|
||||
|
||||
get_input_parent() {
|
||||
getInputParent() {
|
||||
return this.formGroup || this.parent;
|
||||
}
|
||||
|
||||
@ -86,6 +86,7 @@ class BaseControl {
|
||||
setRequiredAttribute() {
|
||||
if (this.required) {
|
||||
this.input.required = true;
|
||||
this.input.classList.add('font-weight-bold');
|
||||
}
|
||||
}
|
||||
|
||||
|
14
client/view/controls/date.js
Normal file
14
client/view/controls/date.js
Normal file
@ -0,0 +1,14 @@
|
||||
const flatpickr = require('flatpickr');
|
||||
const BaseControl = require('./base');
|
||||
|
||||
class DateControl extends BaseControl {
|
||||
make() {
|
||||
super.make();
|
||||
this.input.setAttribute('type', 'text');
|
||||
flatpickr.default(this.input, {
|
||||
dateFormat: "Y-m-d"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = DateControl;
|
@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="js/bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -1,13 +1,14 @@
|
||||
const control_classes = {
|
||||
Data: require('./data'),
|
||||
Text: require('./text'),
|
||||
Select: require('./select'),
|
||||
Link: require('./link'),
|
||||
Date: require('./date'),
|
||||
Currency: require('./currency'),
|
||||
Float: require('./float'),
|
||||
Int: require('./int'),
|
||||
Currency: require('./currency'),
|
||||
Link: require('./link'),
|
||||
Password: require('./password'),
|
||||
Table: require('./table')
|
||||
Select: require('./select'),
|
||||
Table: require('./table'),
|
||||
Text: require('./text')
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -7,7 +7,7 @@ const Modal = require('frappejs/client/ui/modal');
|
||||
class TableControl extends BaseControl {
|
||||
make() {
|
||||
if (!this.datatable) {
|
||||
this.wrapper = frappe.ui.add('div', 'table-wrapper', this.get_input_parent());
|
||||
this.wrapper = frappe.ui.add('div', 'table-wrapper', this.getInputParent());
|
||||
this.wrapper.innerHTML =
|
||||
`<div class="datatable-wrapper"></div>
|
||||
<div class="table-toolbar">
|
||||
|
@ -3,7 +3,7 @@ const frappe = require('frappejs');
|
||||
|
||||
class TextControl extends BaseControl {
|
||||
makeInput() {
|
||||
this.input = frappe.ui.add('textarea', 'form-control', this.get_input_parent());
|
||||
this.input = frappe.ui.add('textarea', 'form-control', this.getInputParent());
|
||||
}
|
||||
make() {
|
||||
super.make();
|
||||
|
@ -44,18 +44,37 @@ module.exports = class BaseForm extends Observable {
|
||||
|
||||
makeToolbar() {
|
||||
if (this.actions.includes('submit')) {
|
||||
this.btnSubmit = this.container.addButton(frappe._("Save"), 'primary', async (event) => {
|
||||
this.container.addButton(frappe._("Save"), 'primary', async (event) => {
|
||||
await this.submit();
|
||||
})
|
||||
}
|
||||
|
||||
if (this.actions.includes('delete')) {
|
||||
this.btnDelete = this.container.addButton(frappe._("Delete"), 'secondary', async (e) => {
|
||||
if (!this.meta.isSingle && this.actions.includes('delete')) {
|
||||
let menu = this.container.getDropdown(frappe._('Menu'));
|
||||
menu.addItem(frappe._("Delete"), async (e) => {
|
||||
await this.doc.delete();
|
||||
this.showAlert('Deleted', 'success');
|
||||
this.trigger('delete');
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.meta.isSingle && this.actions.includes('duplicate')) {
|
||||
let menu = this.container.getDropdown(frappe._('Menu'));
|
||||
menu.addItem(frappe._('Duplicate'), async () => {
|
||||
let newDoc = await frappe.getDuplicate(this.doc);
|
||||
console.log(newDoc);
|
||||
await frappe.router.setRoute('edit', newDoc.doctype, newDoc.name);
|
||||
newDoc.set('name', '');
|
||||
});
|
||||
}
|
||||
|
||||
if (this.meta.settings && this.actions.includes('settings')) {
|
||||
let menu = this.container.getDropdown(frappe._('Menu'));
|
||||
menu.addItem(frappe._('Settings...'), () => {
|
||||
frappe.router.setRoute('edit', frappe.slug(this.meta.settings), this.meta.settings);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bindKeyboard() {
|
||||
|
@ -74,7 +74,7 @@ module.exports = class BaseList {
|
||||
|
||||
makeToolbar() {
|
||||
this.makeSearch();
|
||||
this.btnNew = this.page.addButton(frappe._('New'), 'btn-outline-primary', async () => {
|
||||
this.btnNew = this.page.addButton(frappe._('New'), 'btn-primary', async () => {
|
||||
await frappe.router.setRoute('new', frappe.slug(this.doctype));
|
||||
})
|
||||
this.btnDelete = this.page.addButton(frappe._('Delete'), 'btn-outline-secondary hide', async () => {
|
||||
@ -140,7 +140,7 @@ module.exports = class BaseList {
|
||||
|
||||
getRow(i) {
|
||||
if (!this.rows[i]) {
|
||||
this.rows[i] = frappe.ui.add('div', 'list-row py-2', this.body);
|
||||
this.rows[i] = frappe.ui.add('div', 'list-row', this.body);
|
||||
}
|
||||
return this.rows[i];
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
const frappe = require('frappejs');
|
||||
const Observable = require('frappejs/utils/observable');
|
||||
const Dropdown = require('frappejs/client/ui/dropdown');
|
||||
|
||||
module.exports = class Page extends Observable {
|
||||
constructor(title) {
|
||||
super();
|
||||
this.title = title;
|
||||
this.make();
|
||||
this.dropdowns = {};
|
||||
}
|
||||
|
||||
make() {
|
||||
@ -29,6 +31,13 @@ module.exports = class Page extends Observable {
|
||||
return this.button;
|
||||
}
|
||||
|
||||
getDropdown(label) {
|
||||
if (!this.dropdowns[label]) {
|
||||
this.dropdowns[label] = new Dropdown({parent: this.head, label: label, right: true});
|
||||
}
|
||||
return this.dropdowns[label];
|
||||
}
|
||||
|
||||
async show(params) {
|
||||
if (frappe.router.current_page) {
|
||||
frappe.router.current_page.hide();
|
||||
|
@ -1,19 +1,16 @@
|
||||
const utils = require('../utils');
|
||||
const number_format = require('../utils/number_format');
|
||||
const format = require('../utils/format');
|
||||
const model = require('../model');
|
||||
const BaseDocument = require('../model/document');
|
||||
const BaseMeta = require('../model/meta');
|
||||
const _session = require('../session');
|
||||
const errors = require('./errors');
|
||||
|
||||
|
||||
module.exports = {
|
||||
init_libs(frappe) {
|
||||
Object.assign(frappe, utils);
|
||||
Object.assign(frappe, number_format);
|
||||
Object.assign(frappe, format);
|
||||
frappe.model = model;
|
||||
frappe.BaseDocument = BaseDocument;
|
||||
frappe.BaseMeta = BaseMeta;
|
||||
frappe._session = _session;
|
||||
frappe.errors = errors;
|
||||
}
|
||||
|
21
index.js
21
index.js
@ -14,7 +14,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
initGlobals() {
|
||||
this.meta_cache = {};
|
||||
this.metaCache = {};
|
||||
this.modules = {};
|
||||
this.docs = {};
|
||||
this.flags = {
|
||||
@ -41,10 +41,10 @@ module.exports = {
|
||||
},
|
||||
|
||||
getMeta(doctype) {
|
||||
if (!this.meta_cache[doctype]) {
|
||||
this.meta_cache[doctype] = new (this.getMetaClass(doctype))();
|
||||
if (!this.metaCache[doctype]) {
|
||||
this.metaCache[doctype] = new (this.getMetaClass(doctype))();
|
||||
}
|
||||
return this.meta_cache[doctype];
|
||||
return this.metaCache[doctype];
|
||||
},
|
||||
|
||||
getMetaClass(doctype) {
|
||||
@ -71,6 +71,19 @@ module.exports = {
|
||||
return await this.getDoc(doctype, doctype);
|
||||
},
|
||||
|
||||
async getDuplicate(doc) {
|
||||
const newDoc = await this.getNewDoc(doc.doctype);
|
||||
for (let field of this.getMeta(doc.doctype).getValidFields()) {
|
||||
if (field.fieldname === 'name') continue;
|
||||
if (field.fieldtype === 'Table') {
|
||||
newDoc[field.fieldname] = (doc[field.fieldname] || []).map(d => Object.assign({}, d));
|
||||
} else {
|
||||
newDoc[field.fieldname] = doc[field.fieldname];
|
||||
}
|
||||
}
|
||||
return newDoc;
|
||||
},
|
||||
|
||||
newDoc(data) {
|
||||
let controllerClass = this.getControllerClass(data.doctype);
|
||||
let doc = new controllerClass(data);
|
||||
|
@ -70,8 +70,12 @@ module.exports = class BaseDocument extends Observable {
|
||||
|
||||
setDefaults() {
|
||||
for (let field of this.meta.fields) {
|
||||
if (!this[field.fieldname] && field.default) {
|
||||
this[field.fieldname] = field.default;
|
||||
if (field.fieldtype === 'Date') {
|
||||
this[field.fieldname] = (new Date()).toISOString().substr(0, 10);
|
||||
} else if (!this[field.fieldname]) {
|
||||
if(field.default) {
|
||||
this[field.fieldname] = field.default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,16 +12,17 @@
|
||||
"awesomplete": "^1.1.2",
|
||||
"body-parser": "^1.18.2",
|
||||
"bootstrap": "^4.0.0",
|
||||
"clusterize.js": "^0.18.0",
|
||||
"commander": "^2.13.0",
|
||||
"express": "^4.16.2",
|
||||
"flatpickr": "^4.3.2",
|
||||
"jquery": "^3.3.1",
|
||||
"mysql": "^2.15.0",
|
||||
"node-fetch": "^1.7.3",
|
||||
"popper.js": "^1.12.9",
|
||||
"sortablejs": "^1.7.0",
|
||||
"sqlite3": "^3.1.13",
|
||||
"walk": "^2.3.9",
|
||||
"clusterize.js": "^0.18.0",
|
||||
"sortablejs": "^1.7.0"
|
||||
"walk": "^2.3.9"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -12,7 +12,7 @@ const bodyParser = require('body-parser');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
async start({backend, connection_params, static, models_path}) {
|
||||
async start({backend, connection_params, models_path}) {
|
||||
await this.init();
|
||||
|
||||
this.init_models(models_path);
|
||||
|
20
utils/format.js
Normal file
20
utils/format.js
Normal file
@ -0,0 +1,20 @@
|
||||
const number_format = require('./number_format');
|
||||
|
||||
module.exports = {
|
||||
format(value, field) {
|
||||
if (field.fieldtype==='Currency') {
|
||||
value = number_format.format_number(value);
|
||||
} else if (field.fieldtype==='Date') {
|
||||
if (value instanceof Date) {
|
||||
value = value.toISOString().substr(0, 10);
|
||||
}
|
||||
} else {
|
||||
if (value===null || value===undefined) {
|
||||
value = '';
|
||||
} else {
|
||||
value = value + '';
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
@ -1,16 +1,4 @@
|
||||
module.exports = {
|
||||
format(value, field) {
|
||||
if (field.fieldtype==='Currency') {
|
||||
return frappe.format_number(value);
|
||||
} else {
|
||||
if (value===null || value===undefined) {
|
||||
return '';
|
||||
} else {
|
||||
return value + '';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
slug(text) {
|
||||
return text.toLowerCase().replace(/ /g, '_');
|
||||
},
|
||||
|
@ -1346,6 +1346,10 @@ flat-cache@^1.2.1:
|
||||
graceful-fs "^4.1.2"
|
||||
write "^0.2.1"
|
||||
|
||||
flatpickr@^4.3.2:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/flatpickr/-/flatpickr-4.3.2.tgz#6a477043c075ef36c3ff54fadb49b936a64d635f"
|
||||
|
||||
flatten@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
|
||||
|
Loading…
Reference in New Issue
Block a user