@xuda.io/xuda-framework-plugin-tailwind
Version:
Xuda Tailwind UI Framework plugin
612 lines (539 loc) • 19.7 kB
JavaScript
const _colors = {
primary: ' border-gray-300 bg-white bg-red-600 text-gray-700 hover:bg-gray-50 focus:ring-indigo-500 ',
secondary: ' border-gray-300 bg-white bg-red-600 text-gray-700 hover:bg-gray-50 focus:ring-indigo-500 ',
tertiary: ' border-gray-300 bg-white bg-red-600 text-gray-700 hover:bg-gray-50 focus:ring-indigo-500 ',
success: ' border-gray-300 bg-white bg-red-600 text-gray-700 hover:bg-gray-50 focus:ring-indigo-500 ',
warning: ' border-gray-300 bg-white bg-red-600 text-gray-700 hover:bg-gray-50 focus:ring-indigo-500 ',
danger: ' border-gray-300 bg-white bg-red-600 text-gray-700 hover:bg-gray-50 focus:ring-indigo-500 ',
light: ' border-gray-300 bg-white bg-red-600 text-gray-700 hover:bg-gray-50 focus:ring-indigo-500 ',
medium: ' border-gray-300 bg-white bg-red-600 text-gray-700 hover:bg-gray-50 focus:ring-indigo-500 ',
dark: ' border-gray-300 bg-white bg-red-600 text-gray-700 hover:bg-gray-50 focus:ring-indigo-500 ',
};
const _fields = {
init: {
type: 'string',
label: 'Theme Configuration',
value: `
tailwind.config = {
theme: {
screens: {
sm: '480px',
md: '768px',
lg: '976px',
xl: '1440px',
},
colors: {
'blue': '#1fb6ff',
'purple': '#7e5bef',
'pink': '#ff49db',
'orange': '#ff7849',
'green': '#13ce66',
'yellow': '#ffc82c',
'gray-dark': '#273444',
'gray': '#8492a6',
'gray-light': '#d3dce6',
},
fontFamily: {
sans: ['Graphik', 'sans-serif'],
serif: ['Merriweather', 'serif'],
},
extend: {
spacing: {
'128': '32rem',
'144': '36rem',
},
borderRadius: {
'4xl': '2rem',
}
}
}
}
`,
helper: '<a target="_blank" href="https://ionicframework.com/docs/theming/color-generator"> Color Generator </a>',
render: 'code',
render_params: {
lang: 'js',
},
handler: function () {},
},
};
export function tabs(doc) {
return {
theme: {
svg_icon:
'<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-palette" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"/> <path d="M12 21a9 9 0 1 1 0 -18a9 8 0 0 1 9 8a4.5 4 0 0 1 -4.5 4h-2.5a2 2 0 0 0 -1 3.75a1.3 1.3 0 0 1 -1 2.25" /> <circle cx="7.5" cy="10.5" r=".5" fill="currentColor" /> <circle cx="12" cy="7.5" r=".5" fill="currentColor" /> <circle cx="16.5" cy="10.5" r=".5" fill="currentColor" /> </svg>',
label: 'Theme',
type: 'single',
fields: {
init: _fields.init,
},
},
};
}
export const core = class {
init(setup) {
if (!setup?.theme?.init) {
return console.warn('UI framework theme setup not found');
}
var id = 'xuda-framework-plugin-tailwind-script-' + Date.now();
const script = document.createElement('script');
script.setAttribute('id', id);
script.innerHTML = setup.theme.init;
document.head.append(script);
return id;
}
discard(id) {
const element = document.getElementById(id);
element.remove();
}
refresh(container, _ds) {
return $(container);
}
rootTagName() {
return 'div';
}
customUiClasses() {
return [];
}
renderEngineProperties() {
return {
restrict_tags: true,
engine: function () {},
tags: {
col: {
attributes: {
type: {
label: 'Modal Position',
type: 'string',
render: 'select',
options: [
{ label: 'Stretch', value: 'stretch' },
{ label: 'Start', value: 'start' },
{ label: 'Center', value: 'center' },
{ label: 'End', value: 'end' },
],
},
},
render() {
return `<div > <button/> </div>`;
},
},
},
tags2: { img: ['src', 'alt'] },
classes: [],
common_tags: [],
};
}
};
export const bind = class {
setter(elm, value) {
if ($(elm).prop('tagName') === 'SELECT') {
setTimeout(() => {
$(elm).val(_.toString(value));
}, 250);
} else {
switch ($(elm).attr('type')) {
case 'radio':
if ($(elm).attr('value') === value) {
// $(elm).attr('checked', true);
$(elm).prop('checked', true);
} else {
// $(elm).removeAttr('checked');
$(elm).prop('checked', false);
}
break;
case 'checkbox':
if (value) {
// $(elm).attr('checked', true);
$(elm).prop('checked', true);
} else {
// $(elm).removeAttr('checked');
$(elm).prop('checked', false);
}
break;
case 'time':
if (value.length === 5) {
// force adding the :00 seconds
value = value + ':00';
$(elm).val(value);
}
break;
default:
// if ($(elm).prop("tagName") === "SELECT") {
// setTimeout(() => {
// $(elm).val(_.toString(value));
// }, 250);
// } else {
$(elm).val(value);
// }
}
// $(elm).val(value);
}
}
getter(elm) {
switch ($(elm).attr('type')) {
case 'radio':
if ($(elm).attr('checked') || $(elm).is(':checked')) {
$(elm).attr('checked', true);
return $(elm).attr('value');
}
break;
case 'checkbox':
if ($(elm).attr('checked') || $(elm).is(':checked')) {
$(elm).attr('checked', true);
return $(elm).attr('value');
}
break;
default:
return $(elm).val();
}
}
listener(elm, callback) {
$(elm).on('change', (e) => {
callback(e);
});
}
};
export const toast = class {
async create(type, message, title) {
let svg = '';
switch (type) {
case 'error':
svg = `<svg class="w-6 h-6 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>`;
console.info(message);
break;
case 'success':
svg = `<svg class="w-6 h-6 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>`;
break;
case 'info':
svg = `<svg class="w-6 h-6 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>`;
break;
case 'warning':
svg = `<svg class="w-6 h-6 text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg>`;
console.info(message);
break;
default:
}
this.$html = $(`
<div class="w-full flex flex-col items-center space-y-4 sm:items-end">
<div class="max-w-sm w-full bg-white shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden">
<div class="p-4">
<div class="flex items-start">
<div class="flex-shrink-0">
${svg}
</div>
<div class="ms-3 w-0 flex-1 pt-0.5">
<p class="text-sm font-medium text-gray-900"></p>
${title}
<p class="text-sm text-gray-500">
${message}
</p>
</div>
<div class="ml-4 flex-shrink-0 flex">
<button class="bg-white rounded-md inline-flex text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
<span class="sr-only">Close</span>
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
`);
$('#tailwind_toast_controller').append(this.$html.hide().fadeIn());
this.$html.find('button').click(() => {
this.$html.remove();
});
setTimeout(() => {
this.$html.fadeOut('slow', function () {
$(this).remove();
});
}, 5000);
}
init(text) {}
close() {
this.$html.remove();
}
};
export const loader = class {
async create() {
const loadingController = function () {
var id = undefined;
return {
create: function () {
id = Date.now();
},
present: function () {
const $html = $(`<div id="tailwind_loader_${id}" class="absolute z-[999] inset-0 px-4 sm:inset-0 sm:flex sm:items-center sm:justify-center spinny">
<div class="absolute inset-0 transition-opacity">
<div class="absolute inset-0 rounded-md bg-gray-900 opacity-30"></div>
</div>
<div class="transform transition-all sm:max-w-2xl w-full h-full flex items-center" role="dialog" aria-modal="true" aria-labelledby="modal-headline">
<div class="spinner spinner-add ease-linear my-auto mx-auto">
${text ? text : 'Please wait..'}
</div>
</div>
</div>`);
$('#tailwind_loader_' + id).remove();
$('body').prepend($html);
return $html;
},
dismiss: function () {
$('#tailwind_loader_' + id).remove();
},
};
};
const loader = loadingController.create();
loader.present();
return loader;
}
init(text) {}
close(id) {
$('#tailwind_loader_' + id).remove();
}
};
export const popover = class {
async open(e) {
this.SESSION_ID = e.SESSION_ID;
const popoverController = function () {
var id = undefined;
return {
create: function (opt) {
id = Date.now();
},
present: function () {
$('#xu_popover_modal_' + e.SESSION_ID).remove();
const $html = `
<div id="xu_popover_modal_${e.SESSION_ID}" class="absolute z-10 inline-block w-64 text-sm font-light text-gray-500 transition-opacity duration-300 bg-white border border-gray-200 rounded-lg shadow-sm dark:text-gray-400 dark:bg-gray-800 dark:border-gray-600 " >
<xu-popover-content-${e.SESSION_ID}>
</div>
`;
$('body').prepend($html);
return $html;
},
dismiss: function () {
$('#xu_popover_modal_' + e.SESSION_ID).remove();
},
};
};
let popover = new popoverController();
popover.present();
}
async set(e) {}
async close(e) {
$('#xu_popover_modal_' + this.SESSION_ID).remove();
}
};
export const popup = class {
async create(header, subheader, message, buttons) {
// buttons = [
// {
// label: "Dismiss",
// role: "cancel",
// cssClass: "secondary",
// handler: () => {
// callback();
// },
// },
// ];
var html_buttons = '';
buttons.forEach((val) => {
html_buttons += `
<button id="${val.role || val}_button" type="button" class=" ${
_colors[val.cssClass] || _colors.secondary
} mt-3 w-full inline-flex justify-center rounded-md border shadow-sm px-4 py-2 text-base font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
${val.text || val}
</button>
`;
});
this.$html = $(`
<div class="fixed z-10 inset-0 overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true">
<div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" aria-hidden="true"></div>
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span>
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
<!-- Heroicon name: outline/exclamation -->
<svg class="h-6 w-6 text-red-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
</div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title">
${subheader}
</h3>
<div class="mt-2">
<p class="text-sm text-gray-500">
${message}
</p>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
${html_buttons}
</div>
</div>
</div>
</div>
`);
$('body').append(this.$html);
buttons.forEach((val) => {
$(`#${val.role || val}_button`).click(() => {
this.$html.remove();
if (val.handler) {
val.handler();
}
});
});
}
async update(text) {}
async close(loader) {
this.$html.remove();
}
};
export const modal = class {
async create(e) {
let template_close_btn = `
<button type="button" class="">
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
</button>
`;
let template_header = ` <div class="border-b py-3 px-4 flex items-center justify-between">
<h3 class="font-medium leading-6 text-gray-900 sm:text-lg" id="modal-title"> ${e.properties?.name} </h3>
${!e.properties?.disableModalClose ? template_close_btn : ''}
</div>`;
this.template = $(`
<div class="relative z-10">
<div class="fixed inset-0 z-10 overflow-y-auto">
${e.properties?.allowBackdropDismiss ? '<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity backDrop"></div>' : ''}
<div class="flex min-h-full items-${e?.properties?.modalPosition ? e.properties?.modalPosition : 'end'} justify-center sm:items-center">
<div class="relative mx-4 my-2 w-full transform overflow-hidden rounded-lg border border-gray-50 shadow-xl transition-all sm:max-w-lg bg-white">
${!e.properties?.hideHeader ? template_header : ''}
<div class="template_body">
</div>
</div>
</div>
</div>
</div>
`);
this.close_callback = e.close_callback;
this.modal_id = e.modal_id;
this.modal = $('<' + e.modal_id + '>');
$('body').append(this.modal);
$(e.modal_id).html(this.template);
if (!e.properties?.disableModalClose) {
$(e.modal_id)
.find('button')
.click(() => {
this.close();
});
}
if (e?.properties?.allowBackdropDismiss) {
$(e.modal_id)
.find('.backDrop')
.click(() => {
this.close();
});
}
return this;
}
async init(e) {
const xu_modal_controller = document.querySelector('xu-modal-controller');
var $modal = $(e.modal_id);
var params = $(xu_modal_controller).data().xuControllerParams[e.modal_id];
if (e.$container) {
$modal.attr('id', e.$container.attr('id'));
$.each(e.$container.data(), function (key, val) {
$modal.data(key, val);
});
}
$modal.find('.template_body').html(params.$dialogDiv);
}
async present(modal) {
await modal.present();
}
async properties() {
return {
modalPosition: {
label: 'Modal Position',
type: 'string',
render: 'select',
options: [
{ label: 'Stretch', value: 'stretch' },
{ label: 'Start', value: 'start' },
{ label: 'Center', value: 'center' },
{ label: 'End', value: 'end' },
],
},
allowBackdropDismiss: {
label: 'Allow Backdrop Dismiss',
type: 'bool',
render: 'checkbox',
},
allowModalClose: {
label: 'Allow Modal Close',
type: 'bool',
render: 'checkbox',
},
hideHeader: {
label: 'Hide Header',
render: 'checkbox',
type: 'bool',
},
};
}
async close() {
this.modal.remove();
if (this.close_callback) this.close_callback(this.modal_id);
}
};
export const page = class {
async create(e) {
this.$page = $('<div>');
const $header = $(` <div class="flex py-3 px-2 border-b">
<h3 class="font-medium leading-6 text-gray-900 sm:text-lg" id="modal-title">${e.properties.name}</h3>
</div>
`);
if (!e?.properties?.disablePageBack) {
const $button = $(` <button type="button" class="mr-3">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.707 16.707a1 1 0 01-1.414 0l-6-6a1 1 0 010-1.414l6-6a1 1 0 011.414 1.414L5.414 9H17a1 1 0 110 2H5.414l4.293 4.293a1 1 0 010 1.414z" clip-rule="evenodd"></path></svg>
</button>`)
.prependTo($header)
.off('click')
.on('click', async () => {
this.page_back_callback();
});
}
if (!e?.properties?.hideHeader) {
if (!e?.properties?.disablePageBack) {
$header.appendTo(this.$page);
}
}
}
async init(e) {
e.callback = (elm, page_back_callback) => {
$(elm).append(this.$page.children());
$(elm).append(e.div);
this.page_back_callback = page_back_callback;
};
}
async properties() {
return {
allowPageBack: {
label: 'Allow Page Back',
render: 'checkbox',
type: 'bool',
},
hideHeader: {
label: 'Hide Header',
render: 'checkbox',
type: 'bool',
},
};
}
};