From c1ced676eefd02e8a699932ccba9c1d706b437c6 Mon Sep 17 00:00:00 2001
From: Daniel <50356015+danny007in@users.noreply.github.com>
Date: Wed, 2 Jun 2021 08:51:06 +0530
Subject: [PATCH] added: card-widget
---
src/pages/widgets.html | 89 +++++++++++++++-
src/scss/_cards.scss | 2 +-
src/ts/adminlte.ts | 4 +-
src/ts/card-widget.ts | 228 +++++++++++++++++++++++++++++++++++++++++
src/ts/util/index.ts | 80 ++++++++++++++-
5 files changed, 399 insertions(+), 4 deletions(-)
create mode 100644 src/ts/card-widget.ts
diff --git a/src/pages/widgets.html b/src/pages/widgets.html
index ce52478a7..079c63234 100644
--- a/src/pages/widgets.html
+++ b/src/pages/widgets.html
@@ -5,7 +5,7 @@
@@include('./_head.html', {
"path": "..",
- "title": "AdminLTE 4 | General Form Elements"
+ "title": "AdminLTE 4 | Widgets"
})
@@ -400,6 +400,93 @@
+
+
+ Cards
+ Abilities
+
+
+
+
+
+
+ The body of the card
+
+
+
+
+
+
+
+
+
+
+
+ The body of the card
+
+
+
+
+
+
+
+
+
+
+
+ The body of the card
+
+
+
+
+
+
+
+
+
+
+
+ The body of the card
+
+
+
+
+
+
+
+
diff --git a/src/scss/_cards.scss b/src/scss/_cards.scss
index 0ccad2d8d..ee37cb91b 100644
--- a/src/scss/_cards.scss
+++ b/src/scss/_cards.scss
@@ -44,7 +44,7 @@
overflow: auto;
}
- [data-card-widgett="collapse"] {
+ [data-card-widget="collapse"] {
display: none;
}
diff --git a/src/ts/adminlte.ts b/src/ts/adminlte.ts
index 7243f6fd5..aceb39764 100644
--- a/src/ts/adminlte.ts
+++ b/src/ts/adminlte.ts
@@ -4,6 +4,7 @@ import SidebarHover from './sidebar-hover'
import SidebarOverlay from './sidebar-overlay'
import Treeview from './treeview'
import DirectChat from './direct-chat'
+import CardWidget from './card-widget'
export {
Layout,
@@ -11,7 +12,8 @@ export {
SidebarHover,
SidebarOverlay,
Treeview,
- DirectChat
+ DirectChat,
+ CardWidget
}
//
diff --git a/src/ts/card-widget.ts b/src/ts/card-widget.ts
new file mode 100644
index 000000000..4cb24a2cf
--- /dev/null
+++ b/src/ts/card-widget.ts
@@ -0,0 +1,228 @@
+/**
+ * --------------------------------------------
+ * AdminLTE card-widget.js
+ * License MIT
+ * --------------------------------------------
+ */
+
+import {
+ domReady,
+ slideUp,
+ slideDown
+} from './util/index'
+
+/**
+ * Constants
+ * ====================================================
+ */
+
+const CLASS_NAME_CARD = 'card'
+const CLASS_NAME_COLLAPSED = 'collapsed-card'
+const CLASS_NAME_COLLAPSING = 'collapsing-card'
+const CLASS_NAME_EXPANDING = 'expanding-card'
+const CLASS_NAME_WAS_COLLAPSED = 'was-collapsed'
+const CLASS_NAME_MAXIMIZED = 'maximized-card'
+
+const SELECTOR_DATA_REMOVE = '[data-card-widget="remove"]'
+const SELECTOR_DATA_COLLAPSE = '[data-card-widget="collapse"]'
+const SELECTOR_DATA_MAXIMIZE = '[data-card-widget="maximize"]'
+const SELECTOR_CARD = `.${CLASS_NAME_CARD}`
+const SELECTOR_CARD_HEADER = '.card-header'
+const SELECTOR_CARD_BODY = '.card-body'
+const SELECTOR_CARD_FOOTER = '.card-footer'
+
+const Default = {
+ animationSpeed: 500,
+ collapseTrigger: SELECTOR_DATA_COLLAPSE,
+ removeTrigger: SELECTOR_DATA_REMOVE,
+ maximizeTrigger: SELECTOR_DATA_MAXIMIZE,
+ collapseIcon: 'fa-minus',
+ expandIcon: 'fa-plus',
+ maximizeIcon: 'fa-expand',
+ minimizeIcon: 'fa-compress'
+}
+
+interface Settings {
+ animationSpeed: number;
+ collapseTrigger: string;
+ removeTrigger: string;
+ maximizeTrigger: string;
+ collapseIcon: string;
+ expandIcon: string;
+ maximizeIcon: string;
+ minimizeIcon: string;
+}
+
+class CardWidget {
+ _element: HTMLElement
+ _parent: HTMLElement | null
+ _settings: Settings
+ constructor(element: HTMLElement, settings: Settings) {
+ this._element = element
+ this._parent = element.closest(SELECTOR_CARD)
+
+ if (element.classList.contains(CLASS_NAME_CARD)) {
+ this._parent = element
+ }
+
+ this._settings = Object.assign({}, Default, settings)
+ }
+
+ collapse() {
+ this._parent?.classList.add(CLASS_NAME_COLLAPSING)
+
+ const elm = this._parent?.querySelectorAll(`${SELECTOR_CARD_BODY}, ${SELECTOR_CARD_FOOTER}`)
+
+ if (elm !== undefined) {
+ for (const el of elm) {
+ if (el instanceof HTMLElement) {
+ slideUp(el, this._settings.animationSpeed)
+ }
+ }
+ }
+
+ setTimeout(() => {
+ this._parent?.classList.add(CLASS_NAME_COLLAPSED)
+ this._parent?.classList.remove(CLASS_NAME_COLLAPSING)
+ }, this._settings.animationSpeed)
+
+ const icon = this._parent?.querySelector(`${SELECTOR_CARD_HEADER} ${this._settings.collapseTrigger} .${this._settings.collapseIcon}`)
+
+ icon?.classList.add(this._settings.expandIcon)
+ icon?.classList.remove(this._settings.collapseIcon)
+ }
+
+ expand() {
+ this._parent?.classList.add(CLASS_NAME_EXPANDING)
+
+ const elm = this._parent?.querySelectorAll(`${SELECTOR_CARD_BODY}, ${SELECTOR_CARD_FOOTER}`)
+
+ if (elm !== undefined) {
+ for (const el of elm) {
+ if (el instanceof HTMLElement) {
+ slideDown(el, this._settings.animationSpeed)
+ }
+ }
+ }
+
+ setTimeout(() => {
+ this._parent?.classList.remove(CLASS_NAME_COLLAPSED)
+ this._parent?.classList.remove(CLASS_NAME_EXPANDING)
+ }, this._settings.animationSpeed)
+
+ const icon = this._parent?.querySelector(`${SELECTOR_CARD_HEADER} ${this._settings.collapseTrigger} .${this._settings.expandIcon}`)
+
+ icon?.classList.add(this._settings.collapseIcon)
+ icon?.classList.remove(this._settings.expandIcon)
+ }
+
+ remove() {
+ if (this._parent) {
+ slideUp(this._parent, this._settings.animationSpeed)
+ }
+ }
+
+ toggle() {
+ if (this._parent?.classList.contains(CLASS_NAME_COLLAPSED)) {
+ this.expand()
+ return
+ }
+
+ this.collapse()
+ }
+
+ maximize() {
+ if (this._parent) {
+ const maxElm = this._parent.querySelector(`${this._settings.maximizeTrigger} .${this._settings.maximizeIcon}`)
+ maxElm?.classList.add(this._settings.minimizeIcon)
+ maxElm?.classList.remove(this._settings.maximizeIcon)
+
+ this._parent.style.height = `${this._parent.scrollHeight}px`
+ this._parent.style.width = `${this._parent.scrollWidth}px`
+ this._parent.style.transition = 'all .15s'
+
+ setTimeout(() => {
+ document.querySelector('html')?.classList.add(CLASS_NAME_MAXIMIZED)
+ this._parent?.classList.add(CLASS_NAME_MAXIMIZED)
+ if (this._parent?.classList.contains(CLASS_NAME_COLLAPSED)) {
+ this._parent.classList.add(CLASS_NAME_WAS_COLLAPSED)
+ }
+ }, 150)
+ }
+ }
+
+ minimize() {
+ if (this._parent) {
+ const minElm = this._parent.querySelector(`${this._settings.maximizeTrigger} .${this._settings.minimizeIcon}`)
+
+ minElm?.classList.add(this._settings.maximizeIcon)
+ minElm?.classList.remove(this._settings.minimizeIcon)
+
+ this._parent.style.cssText = `height: ${this._parent.style.height} !important; width: ${this._parent.style.width} !important; transition: all .15s;`
+ // console.log('🚀 ~ file: card-widget.ts ~ line 164 ~ CardWidget ~ minimize ~ this._parent.style.height', this._parent.style.height)
+
+ setTimeout(() => {
+ document.querySelector('html')?.classList.remove(CLASS_NAME_MAXIMIZED)
+ if (this._parent) {
+ this._parent.classList.remove(CLASS_NAME_MAXIMIZED)
+
+ if (this._parent?.classList.contains(CLASS_NAME_WAS_COLLAPSED)) {
+ this._parent.classList.remove(CLASS_NAME_WAS_COLLAPSED)
+ }
+ }
+ }, 10)
+ }
+ }
+
+ toggleMaximize() {
+ if (this._parent?.classList.contains(CLASS_NAME_MAXIMIZED)) {
+ this.minimize()
+ return
+ }
+
+ this.maximize()
+ }
+}
+
+/**
+ *
+ * Data Api implementation
+ * ====================================================
+ */
+
+domReady(() => {
+ const collapseBtn = document.querySelectorAll(SELECTOR_DATA_COLLAPSE)
+
+ for (const btn of collapseBtn) {
+ btn.addEventListener('click', event => {
+ event.preventDefault()
+ const target = event.target as HTMLElement
+ const data = new CardWidget(target, Default)
+ data.toggle()
+ })
+ }
+
+ const removeBtn = document.querySelectorAll(SELECTOR_DATA_REMOVE)
+
+ for (const btn of removeBtn) {
+ btn.addEventListener('click', event => {
+ event.preventDefault()
+ const target = event.target as HTMLElement
+ const data = new CardWidget(target, Default)
+ data.remove()
+ })
+ }
+
+ const maxBtn = document.querySelectorAll(SELECTOR_DATA_MAXIMIZE)
+
+ for (const btn of maxBtn) {
+ btn.addEventListener('click', event => {
+ event.preventDefault()
+ const target = event.target as HTMLElement
+ const data = new CardWidget(target, Default)
+ data.toggleMaximize()
+ })
+ }
+})
+
+export default CardWidget
diff --git a/src/ts/util/index.ts b/src/ts/util/index.ts
index 37e15963b..fa14c2dfc 100644
--- a/src/ts/util/index.ts
+++ b/src/ts/util/index.ts
@@ -14,7 +14,85 @@ const windowReady = (callBack: () => void): void => {
}
}
+/* SLIDE UP */
+const slideUp = (target: HTMLElement, duration = 500) => {
+ target.style.transitionProperty = 'height, margin, padding'
+ target.style.transitionDuration = `${duration}ms`
+ target.style.boxSizing = 'border-box'
+ target.style.height = `${target.offsetHeight}px`
+ target.style.overflow = 'hidden'
+
+ window.setTimeout(() => {
+ target.style.height = '0'
+ target.style.paddingTop = '0'
+ target.style.paddingBottom = '0'
+ target.style.marginTop = '0'
+ target.style.marginBottom = '0'
+ }, 1)
+
+ window.setTimeout(() => {
+ target.style.display = 'none'
+ target.style.removeProperty('height')
+ target.style.removeProperty('padding-top')
+ target.style.removeProperty('padding-bottom')
+ target.style.removeProperty('margin-top')
+ target.style.removeProperty('margin-bottom')
+ target.style.removeProperty('overflow')
+ target.style.removeProperty('transition-duration')
+ target.style.removeProperty('transition-property')
+ }, duration)
+}
+
+/* SLIDE DOWN */
+const slideDown = (target: HTMLElement, duration = 500) => {
+ target.style.removeProperty('display')
+ let {display} = window.getComputedStyle(target)
+ if (display === 'none') {
+ display = 'block'
+ }
+
+ target.style.display = display
+ const height = target.offsetHeight
+ target.style.overflow = 'hidden'
+ target.style.height = '0'
+ target.style.paddingTop = '0'
+ target.style.paddingBottom = '0'
+ target.style.marginTop = '0'
+ target.style.marginBottom = '0'
+
+ window.setTimeout(() => {
+ target.style.boxSizing = 'border-box'
+ target.style.transitionProperty = 'height, margin, padding'
+ target.style.transitionDuration = `${duration}ms`
+ target.style.height = `${height}px`
+ target.style.removeProperty('padding-top')
+ target.style.removeProperty('padding-bottom')
+ target.style.removeProperty('margin-top')
+ target.style.removeProperty('margin-bottom')
+ }, 1)
+
+ window.setTimeout(() => {
+ target.style.removeProperty('height')
+ target.style.removeProperty('overflow')
+ target.style.removeProperty('transition-duration')
+ target.style.removeProperty('transition-property')
+ }, duration)
+}
+
+/* TOOGLE */
+const slideToggle = (target: HTMLElement, duration = 500) => {
+ if (window.getComputedStyle(target).display === 'none') {
+ slideDown(target, duration)
+ return
+ }
+
+ slideUp(target, duration)
+}
+
export {
domReady,
- windowReady
+ windowReady,
+ slideUp,
+ slideDown,
+ slideToggle
}