From 08bfb9210b601107d6701c77ee42e8a0686da38b Mon Sep 17 00:00:00 2001 From: TemuulenBM Date: Tue, 7 Nov 2023 10:27:17 +0800 Subject: [PATCH 01/11] Initializing and starting the development of the `` component --- addon/components/full-calendar.hbs | 1 + addon/components/full-calendar.js | 43 ++++++++++++++++ app/components/full-calendar.js | 1 + package.json | 9 ++-- pnpm-lock.yaml | 51 +++++++++++++++++++ .../components/full-calendar-test.js | 26 ++++++++++ 6 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 addon/components/full-calendar.hbs create mode 100644 addon/components/full-calendar.js create mode 100644 app/components/full-calendar.js create mode 100644 tests/integration/components/full-calendar-test.js diff --git a/addon/components/full-calendar.hbs b/addon/components/full-calendar.hbs new file mode 100644 index 0000000..a8ad7e5 --- /dev/null +++ b/addon/components/full-calendar.hbs @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/addon/components/full-calendar.js b/addon/components/full-calendar.js new file mode 100644 index 0000000..88db6f2 --- /dev/null +++ b/addon/components/full-calendar.js @@ -0,0 +1,43 @@ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { action } from '@ember/object'; +import { Calendar } from '@fullcalendar/core'; +import dayGridPlugin from '@fullcalendar/daygrid'; +import interactionPlugin from '@fullcalendar/interaction'; + +export default class FullCalendarComponent extends Component { + /** + * @var {HTMLElement} calendarEl + */ + @tracked calendarEl; + + /** + * @var {Calendar} calendar + * @package @fullcalendar/core + */ + @tracked calendar; + + /** + * Initializes and renders the calendar component + * + * @param {HTMLElement} calendarEl + */ + @action setupCalendar(calendarEl) { + // track calendar htmlelement + this.calendarEl = calendarEl; + + // initialize calendar + this.calendar = new Calendar(calendarEl, { + plugins: [dayGridPlugin, interactionPlugin], + initialView: 'dayGridMonth', + headerToolbar: { + left: 'prev,next today', + center: 'title', + right: 'dayGridMonth,timeGridWeek,listWeek', + }, + }); + + // render calendar + this.calendar.render(); + } +} diff --git a/app/components/full-calendar.js b/app/components/full-calendar.js new file mode 100644 index 0000000..424bf0e --- /dev/null +++ b/app/components/full-calendar.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/ember-ui/components/full-calendar'; diff --git a/package.json b/package.json index a0d7798..99f1728 100644 --- a/package.json +++ b/package.json @@ -41,9 +41,14 @@ "@fortawesome/fontawesome-svg-core": "^6.4.0", "@fortawesome/free-brands-svg-icons": "^6.4.0", "@fortawesome/free-solid-svg-icons": "^6.4.0", + "@fullcalendar/core": "^6.1.9", + "@fullcalendar/daygrid": "^6.1.9", + "@fullcalendar/interaction": "^6.1.9", "@makepanic/ember-power-calendar-date-fns": "^0.4.2", + "@tailwindcss/forms": "^0.5.3", "air-datepicker": "^3.3.5", "autonumeric": "^4.6.2", + "autoprefixer": "^10.4.15", "broccoli-funnel": "^3.0.8", "broccoli-merge-trees": "^4.2.0", "chart.js": "^4.2.1", @@ -80,15 +85,13 @@ "ember-wormhole": "^0.6.0", "imask": "^6.4.3", "intl-tel-input": "^18.1.3", - "autoprefixer": "^10.4.15", "postcss-at-rules-variables": "^0.3.0", "postcss-conditionals-renewed": "^1.0.0", "postcss-each": "^1.1.0", "postcss-import": "^15.1.0", "postcss-mixins": "^9.0.4", "postcss-preset-env": "^9.1.1", - "tailwindcss": "^3.1.8", - "@tailwindcss/forms": "^0.5.3" + "tailwindcss": "^3.1.8" }, "devDependencies": { "@ember/optional-features": "^2.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fa03a4e..6a7efbd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,5 +1,9 @@ lockfileVersion: '6.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + dependencies: '@ember/render-modifiers': specifier: ^2.0.4 @@ -28,6 +32,15 @@ dependencies: '@fortawesome/free-solid-svg-icons': specifier: ^6.4.0 version: 6.4.0 + '@fullcalendar/core': + specifier: ^6.1.9 + version: 6.1.9 + '@fullcalendar/daygrid': + specifier: ^6.1.9 + version: 6.1.9(@fullcalendar/core@6.1.9) + '@fullcalendar/interaction': + specifier: ^6.1.9 + version: 6.1.9(@fullcalendar/core@6.1.9) '@makepanic/ember-power-calendar-date-fns': specifier: ^0.4.2 version: 0.4.2 @@ -2211,6 +2224,28 @@ packages: '@fortawesome/fontawesome-common-types': 6.4.0 dev: false + /@fullcalendar/core@6.1.9: + resolution: {integrity: sha512-eeG+z9BWerdsU9Ac6j16rpYpPnE0wxtnEHiHrh/u/ADbGTR3hCOjCD9PxQOfhOTHbWOVs7JQunGcksSPu5WZBQ==} + dependencies: + preact: 10.12.1 + dev: false + + /@fullcalendar/daygrid@6.1.9(@fullcalendar/core@6.1.9): + resolution: {integrity: sha512-o/6joH/7lmVHXAkbaa/tUbzWYnGp/LgfdiFyYPkqQbjKEeivNZWF1WhHqFbhx0zbFONSHtrvkjY2bjr+Ef2quQ==} + peerDependencies: + '@fullcalendar/core': ~6.1.9 + dependencies: + '@fullcalendar/core': 6.1.9 + dev: false + + /@fullcalendar/interaction@6.1.9(@fullcalendar/core@6.1.9): + resolution: {integrity: sha512-I3FGnv0kKZpIwujg3HllbKrciNjTqeTYy3oJG226oAn7lV6wnrrDYMmuGmA0jPJAGN46HKrQqKN7ItxQRDec4Q==} + peerDependencies: + '@fullcalendar/core': ~6.1.9 + dependencies: + '@fullcalendar/core': 6.1.9 + dev: false + /@glimmer/compiler@0.27.0: resolution: {integrity: sha512-SJUUEpkFCL+GTpEK6c2EhZQJant67ahGLF6M1xRmIsq6E+AtbHgu+y8mWvFbtpb7lx4gqNKpXSEwlHUTTuxVGw==} dependencies: @@ -3258,6 +3293,7 @@ packages: /async-each@1.0.6: resolution: {integrity: sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==} + requiresBuild: true dev: false optional: true @@ -3661,6 +3697,7 @@ packages: /binary-extensions@1.13.1: resolution: {integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: false optional: true @@ -3675,6 +3712,7 @@ packages: /bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + requiresBuild: true dependencies: file-uri-to-path: 1.0.0 dev: false @@ -4672,6 +4710,7 @@ packages: /chokidar@2.1.8: resolution: {integrity: sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==} deprecated: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies + requiresBuild: true dependencies: anymatch: 2.0.0 async-each: 1.0.6 @@ -7834,6 +7873,7 @@ packages: /file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + requiresBuild: true dev: false optional: true @@ -8337,6 +8377,7 @@ packages: /glob-parent@3.1.0: resolution: {integrity: sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==} + requiresBuild: true dependencies: is-glob: 3.1.0 path-dirname: 1.0.2 @@ -8925,6 +8966,7 @@ packages: /is-binary-path@1.0.1: resolution: {integrity: sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: binary-extensions: 1.13.1 dev: false @@ -9040,6 +9082,7 @@ packages: /is-glob@3.1.0: resolution: {integrity: sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: is-extglob: 2.1.1 dev: false @@ -10064,6 +10107,7 @@ packages: /nan@2.18.0: resolution: {integrity: sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==} + requiresBuild: true dev: false optional: true @@ -10576,6 +10620,7 @@ packages: /path-dirname@1.0.2: resolution: {integrity: sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==} + requiresBuild: true dev: false optional: true @@ -11235,6 +11280,10 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 + /preact@10.12.1: + resolution: {integrity: sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==} + dev: false + /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -11526,6 +11575,7 @@ packages: /readdirp@2.2.1: resolution: {integrity: sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==} engines: {node: '>=0.10'} + requiresBuild: true dependencies: graceful-fs: 4.2.11 micromatch: 3.1.10 @@ -13217,6 +13267,7 @@ packages: /upath@1.2.0: resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} engines: {node: '>=4'} + requiresBuild: true dev: false optional: true diff --git a/tests/integration/components/full-calendar-test.js b/tests/integration/components/full-calendar-test.js new file mode 100644 index 0000000..170cd38 --- /dev/null +++ b/tests/integration/components/full-calendar-test.js @@ -0,0 +1,26 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'dummy/tests/helpers'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | full-calendar', function (hooks) { + setupRenderingTest(hooks); + + test('it renders', async function (assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.set('myAction', function(val) { ... }); + + await render(hbs``); + + assert.dom(this.element).hasText(''); + + // Template block usage: + await render(hbs` + + template block text + + `); + + assert.dom(this.element).hasText('template block text'); + }); +}); From 183628e38e4dc430390f95bc29db2b1f89bcea65 Mon Sep 17 00:00:00 2001 From: TemuulenBM Date: Tue, 7 Nov 2023 17:37:38 +0800 Subject: [PATCH 02/11] full-calendar --- addon/components/full-calendar.hbs | 2 +- addon/components/full-calendar.js | 75 ++++++++++++++++++- addon/components/full-calendar/draggable.hbs | 3 + addon/components/full-calendar/draggable.js | 18 +++++ addon/helpers/json-hash.js | 5 +- addon/styles/addon.css | 1 + addon/styles/components/full-calendar.css | 45 +++++++++++ app/components/full-calendar/draggable.js | 1 + .../full-calendar/draggable-test.js | 26 +++++++ 9 files changed, 170 insertions(+), 6 deletions(-) create mode 100644 addon/components/full-calendar/draggable.hbs create mode 100644 addon/components/full-calendar/draggable.js create mode 100644 addon/styles/components/full-calendar.css create mode 100644 app/components/full-calendar/draggable.js create mode 100644 tests/integration/components/full-calendar/draggable-test.js diff --git a/addon/components/full-calendar.hbs b/addon/components/full-calendar.hbs index a8ad7e5..d61be8a 100644 --- a/addon/components/full-calendar.hbs +++ b/addon/components/full-calendar.hbs @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/addon/components/full-calendar.js b/addon/components/full-calendar.js index 88db6f2..a3f1512 100644 --- a/addon/components/full-calendar.js +++ b/addon/components/full-calendar.js @@ -1,6 +1,7 @@ import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; +import { classify } from '@ember/string'; import { Calendar } from '@fullcalendar/core'; import dayGridPlugin from '@fullcalendar/daygrid'; import interactionPlugin from '@fullcalendar/interaction'; @@ -17,27 +18,95 @@ export default class FullCalendarComponent extends Component { */ @tracked calendar; + /** + * Default events to trigger for + * @var {Array} + */ + @tracked events = ['dateClick', 'drop', 'eventReceive', 'eventClick', 'eventDragStop', 'eventDrop', 'eventAdd', 'eventChange', 'eventRemove']; + + /** + * Tracked calendar event listeners + * @var {Array} + */ + @tracked _listeners = []; + /** * Initializes and renders the calendar component - * - * @param {HTMLElement} calendarEl + * + * @param {HTMLElement} calendarEl */ @action setupCalendar(calendarEl) { // track calendar htmlelement this.calendarEl = calendarEl; + // get events + let events = this.args.events || []; + // initialize calendar this.calendar = new Calendar(calendarEl, { + events, plugins: [dayGridPlugin, interactionPlugin], initialView: 'dayGridMonth', + editable:true, headerToolbar: { left: 'prev,next today', center: 'title', - right: 'dayGridMonth,timeGridWeek,listWeek', + right: '', }, }); + console.log('[calender]', this.calendar); + + // trigger callback on initialize + if (typeof this.args.onInit === 'function') { + this.args.onInit(this.calendar); + } + // render calendar this.calendar.render(); + + // listen for events + this.createCalendarEventListeners(); + } + + triggerCalendarEvent(eventName, ...params) { + if (typeof this[eventName] === 'function') { + this[eventName](...params); + } + + if (typeof this.args[eventName] === 'function') { + this.args[eventName](...params); + } + } + + createCalendarEventListeners() { + for (let i = 0; i < this.events.length; i++) { + const eventName = this.events.objectAt(i); + const callbackName = `on${classify(eventName)}`; + + if (typeof this.args[callbackName] === 'function') { + // track for destroy purposes + this._listeners.pushObject({ + eventName, + callbackName, + }); + + // create listener + this.calendar.on(eventName, this.triggerCalendarEvent.bind(this, callbackName)); + } + } + + // check for custom events + // @todo + } + + destroyCalendarEventListeners() { + for (let i = 0; i < this._listeners.length; i++) { + const listener = this._listeners.objectAt(i); + const { eventName, callbackName } = listener; + + // kill listener + this.calendar.off(eventName, this.triggerCalendarEvent.bind(this, callbackName)); + } } } diff --git a/addon/components/full-calendar/draggable.hbs b/addon/components/full-calendar/draggable.hbs new file mode 100644 index 0000000..2f2af9f --- /dev/null +++ b/addon/components/full-calendar/draggable.hbs @@ -0,0 +1,3 @@ +
+ {{yield}} +
\ No newline at end of file diff --git a/addon/components/full-calendar/draggable.js b/addon/components/full-calendar/draggable.js new file mode 100644 index 0000000..d59eb5a --- /dev/null +++ b/addon/components/full-calendar/draggable.js @@ -0,0 +1,18 @@ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { action } from '@ember/object'; +import { Draggable } from '@fullcalendar/interaction'; + +export default class DraggableFullcalendarEventComponent extends Component { + @tracked draggable; + @tracked eventData = {}; + + constructor() { + super(...arguments); + this.eventData = this.args.eventData ?? {}; + } + + @action makeDraggable(element) { + this.draggable = new Draggable(element); + } +} diff --git a/addon/helpers/json-hash.js b/addon/helpers/json-hash.js index 3f470d0..739ba0e 100644 --- a/addon/helpers/json-hash.js +++ b/addon/helpers/json-hash.js @@ -1,5 +1,6 @@ import { helper } from '@ember/component/helper'; -export default helper(function jsonHash(positional /*, named*/) { - return positional; +export default helper(function jsonHash(positional, named) { + let json = JSON.stringify(named); + return json; }); diff --git a/addon/styles/addon.css b/addon/styles/addon.css index 1837c6b..70788ec 100644 --- a/addon/styles/addon.css +++ b/addon/styles/addon.css @@ -38,6 +38,7 @@ @import 'components/dashboard.css'; @import 'components/kanban.css'; @import 'components/notification-tray.css'; +@import 'components/full-calendar.css'; /** Third party */ @import 'air-datepicker/air-datepicker.css'; diff --git a/addon/styles/components/full-calendar.css b/addon/styles/components/full-calendar.css new file mode 100644 index 0000000..ade67c8 --- /dev/null +++ b/addon/styles/components/full-calendar.css @@ -0,0 +1,45 @@ +.fleetbase-full-calendar .fc-toolbar-title { + @apply text-base text-gray-900; +} + +body[data-theme='dark'] .fleetbase-full-calendar.fc .fc-toolbar-title { + @apply text-gray-200; +} + +.fleetbase-full-calendar.fc td, +.fleetbase-full-calendar.fc th { + @apply border-gray-400; +} + +body[data-theme='dark'] .fleetbase-full-calendar.fc td, +body[data-theme='dark'] .fleetbase-full-calendar.fc th { + @apply border-gray-700; +} + +.fleetbase-full-calendar.fc table.fc-scrollgrid { + @apply border-gray-400; +} + +body[data-theme='dark'] .fleetbase-full-calendar.fc table.fc-scrollgrid { + @apply border-gray-700; +} + +.fleetbase-full-calendar.fc .fc-button { + @apply inline-flex items-center px-3 py-2 text-sm font-medium leading-4 transition duration-150 ease-in-out border border-transparent rounded-md; +} + +.fleetbase-full-calendar.fc .fc-button .fc-icon { + font-size: 1rem; +} + +.fleetbase-full-calendar.fc .fc-toolbar { + justify-content: start; +} + +.fleetbase-full-calendar.fc .fc-toolbar.fc-header-toolbar { + margin-bottom: 1rem; +} + +.fleetbase-full-calendar.fc .fc-toolbar > .fc-toolbar-chunk { + margin-right: 1.25rem; +} diff --git a/app/components/full-calendar/draggable.js b/app/components/full-calendar/draggable.js new file mode 100644 index 0000000..320ca98 --- /dev/null +++ b/app/components/full-calendar/draggable.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/ember-ui/components/full-calendar/draggable'; diff --git a/tests/integration/components/full-calendar/draggable-test.js b/tests/integration/components/full-calendar/draggable-test.js new file mode 100644 index 0000000..39317bf --- /dev/null +++ b/tests/integration/components/full-calendar/draggable-test.js @@ -0,0 +1,26 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'dummy/tests/helpers'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | full-calendar/draggable', function (hooks) { + setupRenderingTest(hooks); + + test('it renders', async function (assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.set('myAction', function(val) { ... }); + + await render(hbs``); + + assert.dom(this.element).hasText(''); + + // Template block usage: + await render(hbs` + + template block text + + `); + + assert.dom(this.element).hasText('template block text'); + }); +}); From 3a99ee6500936d978638652c07467b63a2789785 Mon Sep 17 00:00:00 2001 From: TemuulenBM Date: Wed, 8 Nov 2023 12:35:41 +0800 Subject: [PATCH 03/11] full-calendar restyle --- addon/styles/components/full-calendar.css | 62 +++++++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/addon/styles/components/full-calendar.css b/addon/styles/components/full-calendar.css index ade67c8..d7bcc80 100644 --- a/addon/styles/components/full-calendar.css +++ b/addon/styles/components/full-calendar.css @@ -1,5 +1,5 @@ .fleetbase-full-calendar .fc-toolbar-title { - @apply text-base text-gray-900; + @apply text-base text-gray-900 font-semibold; } body[data-theme='dark'] .fleetbase-full-calendar.fc .fc-toolbar-title { @@ -8,7 +8,7 @@ body[data-theme='dark'] .fleetbase-full-calendar.fc .fc-toolbar-title { .fleetbase-full-calendar.fc td, .fleetbase-full-calendar.fc th { - @apply border-gray-400; + @apply border-gray-200; } body[data-theme='dark'] .fleetbase-full-calendar.fc td, @@ -17,7 +17,7 @@ body[data-theme='dark'] .fleetbase-full-calendar.fc th { } .fleetbase-full-calendar.fc table.fc-scrollgrid { - @apply border-gray-400; + @apply border-gray-200; } body[data-theme='dark'] .fleetbase-full-calendar.fc table.fc-scrollgrid { @@ -25,7 +25,14 @@ body[data-theme='dark'] .fleetbase-full-calendar.fc table.fc-scrollgrid { } .fleetbase-full-calendar.fc .fc-button { - @apply inline-flex items-center px-3 py-2 text-sm font-medium leading-4 transition duration-150 ease-in-out border border-transparent rounded-md; + @apply inline-flex items-center px-3 py-2 text-sm font-medium leading-4 transition duration-150 ease-in-out border border-transparent rounded-md text-gray-800; + cursor: default !important; +} + +.fleetbase-full-calendar.fc .fc-button:disabled, +.fleetbase-full-calendar.fc .fc-button-primary:disabled, +body[data-theme='dark'] .fleetbase-full-calendar.fc .fc-button-primary:disabled { + @apply opacity-50 cursor-not-allowed; } .fleetbase-full-calendar.fc .fc-button .fc-icon { @@ -43,3 +50,50 @@ body[data-theme='dark'] .fleetbase-full-calendar.fc table.fc-scrollgrid { .fleetbase-full-calendar.fc .fc-toolbar > .fc-toolbar-chunk { margin-right: 1.25rem; } + +body[data-theme='dark'] .fleetbase-full-calendar.fc .fc-button-primary { + @apply text-gray-300 bg-gray-700 border-gray-900 shadow; +} + +body[data-theme='dark'] .fleetbase-full-calendar.fc .fc-button-primary:hover:not(:disabled) { + @apply text-gray-200 bg-gray-600; +} + +body[data-theme='dark'] .fleetbase-full-calendar.fc .fc-button-primary:focus { + @apply outline-0; +} + +body[data-theme='dark'] .fleetbase-full-calendar.fc .fc-button-primary:active:not(:disabled) { + @apply text-gray-300 bg-gray-600; +} + +body[data-theme='dark'] .fleetbase-full-calendar.fc .fc-button .fc-icon { + @apply text-gray-300; +} + +.fleetbase-full-calendar.fc .fc-button-primary { + @apply bg-white border-gray-300 shadow-sm text-gray-800; +} + +.fleetbase-full-calendar.fc .fc-button-primary:hover:not(:disabled) { + @apply text-gray-500 +} + +.fleetbase-full-calendar.fc .fc-button-primary:focus { + @apply border-gray-300 outline-0; +} + +.fleetbase-full-calendar.fc .fc-button-primary:active:not(:disabled) { + @apply text-gray-800 bg-gray-50; +} + +.fleetbase-full-calendar.fc .fc-button .fc-icon { + @apply text-gray-900; +} + +.fleetbase-full-calendar.fc .fc-button-primary:not(:disabled).fc-button-active:focus, +.fleetbase-full-calendar.fc .fc-button-primary:not(:disabled):active:focus, +body[data-theme='dark'] .fleetbase-full-calendar.fc .fc-button-primary:not(:disabled).fc-button-active:focus, +body[data-theme='dark'] .fleetbase-full-calendar.fc .fc-button-primary:not(:disabled):active:focus { + box-shadow: none; +} \ No newline at end of file From 26d9457adfba35e5774fa92b582762ae86de961b Mon Sep 17 00:00:00 2001 From: TemuulenBM Date: Wed, 8 Nov 2023 15:30:49 +0800 Subject: [PATCH 04/11] fixed linter issue --- addon/components/full-calendar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/components/full-calendar.js b/addon/components/full-calendar.js index a3f1512..850bad4 100644 --- a/addon/components/full-calendar.js +++ b/addon/components/full-calendar.js @@ -47,7 +47,7 @@ export default class FullCalendarComponent extends Component { events, plugins: [dayGridPlugin, interactionPlugin], initialView: 'dayGridMonth', - editable:true, + editable: true, headerToolbar: { left: 'prev,next today', center: 'title', From 02f5c15de841800c1eab23aaf83681fae269f1ef Mon Sep 17 00:00:00 2001 From: TemuulenBM Date: Wed, 8 Nov 2023 15:32:21 +0800 Subject: [PATCH 05/11] removed console log of calendar instance --- addon/components/full-calendar.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/addon/components/full-calendar.js b/addon/components/full-calendar.js index 850bad4..cf97394 100644 --- a/addon/components/full-calendar.js +++ b/addon/components/full-calendar.js @@ -55,8 +55,6 @@ export default class FullCalendarComponent extends Component { }, }); - console.log('[calender]', this.calendar); - // trigger callback on initialize if (typeof this.args.onInit === 'function') { this.args.onInit(this.calendar); From 5fae5ab31f9ece6698910d9470334d10f1ea9f9d Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Wed, 8 Nov 2023 18:10:58 +0800 Subject: [PATCH 06/11] completed `` component feature and additional updates --- addon/components/drawer.hbs | 18 ++ addon/components/drawer.js | 178 ++++++++++++++++++ addon/components/table/cell/point.hbs | 5 + addon/components/table/cell/point.js | 51 +++++ addon/styles/addon.css | 1 + addon/styles/components/drawer.css | 121 ++++++++++++ addon/styles/layout/next.css | 7 +- app/components/drawer.js | 1 + app/components/table/cell/point.js | 1 + tests/integration/components/drawer-test.js | 26 +++ .../components/table/cell/point-test.js | 26 +++ 11 files changed, 430 insertions(+), 5 deletions(-) create mode 100644 addon/components/drawer.hbs create mode 100644 addon/components/drawer.js create mode 100644 addon/components/table/cell/point.hbs create mode 100644 addon/components/table/cell/point.js create mode 100644 addon/styles/components/drawer.css create mode 100644 app/components/drawer.js create mode 100644 app/components/table/cell/point.js create mode 100644 tests/integration/components/drawer-test.js create mode 100644 tests/integration/components/table/cell/point-test.js diff --git a/addon/components/drawer.hbs b/addon/components/drawer.hbs new file mode 100644 index 0000000..23c0ce0 --- /dev/null +++ b/addon/components/drawer.hbs @@ -0,0 +1,18 @@ + \ No newline at end of file diff --git a/addon/components/drawer.js b/addon/components/drawer.js new file mode 100644 index 0000000..6dd7481 --- /dev/null +++ b/addon/components/drawer.js @@ -0,0 +1,178 @@ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { action } from '@ember/object'; +import { later } from '@ember/runloop'; +import getWithDefault from '@fleetbase/ember-core/utils/get-with-default'; + +export default class DrawerComponent extends Component { + @tracked drawerNode; + @tracked drawerContainerNode; + @tracked drawerPanelNode; + @tracked gutterNode; + @tracked noBackdrop = true; + @tracked isOpen = true; + @tracked isResizable = true; + @tracked isResizing = false; + @tracked isMinimized = false; + @tracked mouseX = 0; + @tracked mouseY = 0; + @tracked height = 300; + @tracked _rendered = false; + + context = { + toggle: this.toggle, + open: this.open, + close: this.close, + toggleMinimize: this.toggleMinimize, + minimize: this.minimize, + maximize: this.maximize, + isOpen: this.isOpen, + }; + + @action setupComponent(element) { + this.drawerNode = element; + this.height = getWithDefault(this.args, 'height', this.height); + this.isMinimized = getWithDefault(this.args, 'isMinimized', this.isMinimized); + + later( + this, + () => { + this.isOpen = getWithDefault(this.args, 'isOpen', this.isOpen); + this.isResizable = getWithDefault(this.args, 'isResizable', this.isResizable); + this.noBackdrop = getWithDefault(this.args, 'noBackdrop', this.noBackdrop); + + if (typeof this.args.onLoad === 'function') { + this.args.onLoad(this.context); + } + + this._rendered = true; + }, + 300 + ); + } + + @action setupNode(property, node) { + this[`${property}Node`] = node; + } + + @action toggle() { + this.isOpen = !this.isOpen; + } + + @action open() { + this.isOpen = true; + } + + @action close() { + this.isOpen = false; + } + + @action toggleMinimize() { + this.isMinimized = !this.isMaximized; + } + + @action minimize() { + this.isMinimized = true; + } + + @action maximize() { + this.isMinimized = false; + } + + @action startResize(event) { + const disableResize = getWithDefault(this.args, 'disableResize', false); + const onResizeStart = getWithDefault(this.args, 'onResizeStart', null); + const { drawerPanelNode, isResizable } = this; + + if (disableResize === true || !isResizable || !drawerPanelNode) { + return; + } + + // if minimized undo + if (this.isMinimized) { + return this.maximize(); + } + + const bounds = drawerPanelNode.getBoundingClientRect(); + + // Set the overlay width/height + this.overlayWidth = bounds.width; + this.overlayHeight = bounds.height; + + // Start resizing + this.isResizing = true; + + // Get the current mouse position + this.mouseX = event.clientX; + this.mouseY = event.clientY; + + // Attach the listeners + document.addEventListener('mousemove', this.resize); + document.addEventListener('mouseup', this.stopResize); + + // Send up event + if (typeof onResizeStart === 'function') { + onResizeStart({ event, drawerPanelNode }); + } + } + + @action resize(event) { + const disableResize = getWithDefault(this.args, 'disableResize', false); + const onResize = getWithDefault(this.args, 'onResize', null); + const { drawerPanelNode, isResizable } = this; + + if (disableResize === true || !isResizable || !drawerPanelNode) { + return; + } + + const dx = event.clientX - this.mouseX; + const dy = event.clientY - this.mouseY; + const multiplier = -1; + const width = dx * multiplier + this.overlayWidth; + const height = dy * multiplier + this.overlayHeight; + const minResizeHeight = getWithDefault(this.args, 'minResizeHeight', 0); + const maxResizeHeight = getWithDefault(this.args, 'maxResizeHeight', 600); + + // Min resize width + if (height <= minResizeHeight) { + drawerPanelNode.style.height = `${minResizeHeight}px`; + return; + } + + // Max resize width + if (height >= maxResizeHeight) { + drawerPanelNode.style.height = `${maxResizeHeight}px`; + return; + } + + // Style changes + drawerPanelNode.style.userSelect = 'none'; + drawerPanelNode.style.height = `${height}px`; + document.body.style.cursor = 'row-resize'; + + // Send callback + if (typeof onResize === 'function') { + onResize({ event, drawerPanelNode }); + } + } + + @action stopResize(event) { + const onResizeEnd = getWithDefault(this.args, 'onResizeEnd', null); + const { drawerPanelNode } = this; + + // End resizing + this.isResizing = false; + + // Remove style changes + document.body.style.removeProperty('cursor'); + drawerPanelNode.style.userSelect = 'auto'; + + // Remove the handlers of `mousemove` and `mouseup` + document.removeEventListener('mousemove', this.resize); + document.removeEventListener('mouseup', this.stopResize); + + if (typeof onResizeEnd === 'function') { + onResizeEnd({ event, drawerPanelNode }); + } + } +} diff --git a/addon/components/table/cell/point.hbs b/addon/components/table/cell/point.hbs new file mode 100644 index 0000000..029b3d2 --- /dev/null +++ b/addon/components/table/cell/point.hbs @@ -0,0 +1,5 @@ +{{#if this.isClickable}} + {{this.display}} +{{else}} + {{this.display}} +{{/if}} \ No newline at end of file diff --git a/addon/components/table/cell/point.js b/addon/components/table/cell/point.js new file mode 100644 index 0000000..ceefc69 --- /dev/null +++ b/addon/components/table/cell/point.js @@ -0,0 +1,51 @@ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { isArray } from '@ember/array'; +import { get, action } from '@ember/object'; +import { later } from '@ember/runloop'; + +const isPoint = (point) => { + return point && typeof point === 'object' && isArray(point.coordinates); +}; + +export default class TableCellPointComponent extends Component { + @tracked display = ''; + @tracked isClickable = false; + + constructor(owner, { row, column }) { + super(...arguments); + this.isClickable = typeof column === 'object' && (typeof column.onClick === 'function' || typeof column.action === 'function'); + this.displayPointFromRow(row, column); + } + + displayPointFromRow(row, column) { + later( + this, + () => { + const pointColumn = column.valuePath; + + if (pointColumn) { + const point = get(row, pointColumn); + + if (isPoint(point)) { + this.display = `${point.coordinates[1]} ${point.coordinates[0]}`; + } + } + }, + 300 + ); + } + + @action onClick() { + const { column } = this.args; + const { onClick, action } = column; + + if (typeof onClick === 'funciton') { + onClick(this.args.row, ...arguments); + } + + if (typeof action === 'funciton') { + action(this.args.row); + } + } +} diff --git a/addon/styles/addon.css b/addon/styles/addon.css index d5d26c3..36411b6 100644 --- a/addon/styles/addon.css +++ b/addon/styles/addon.css @@ -39,6 +39,7 @@ @import 'components/kanban.css'; @import 'components/notification-tray.css'; @import 'components/fleet-listing.css'; +@import 'components/drawer.css'; /** Third party */ @import 'air-datepicker/air-datepicker.css'; diff --git a/addon/styles/components/drawer.css b/addon/styles/components/drawer.css new file mode 100644 index 0000000..b361717 --- /dev/null +++ b/addon/styles/components/drawer.css @@ -0,0 +1,121 @@ +.next-drawer { + @apply absolute inset-0 opacity-0 transition duration-300 ease-in-out pointer-events-auto bg-gray-900 bg-opacity-50; + width: 100%; + height: 100%; + z-index: 800; +} + +.next-drawer.drawer-no-backdrop { + @apply bg-transparent pointer-events-none; +} + +.next-drawer.drawer-no-backdrop > * { + @apply pointer-events-auto; +} + +.next-drawer.drawer-is-open { + @apply opacity-100; +} + +.next-drawer.drawer-is-open > .next-drawer-panel-container { + transform: translateY(0); +} + +.next-drawer.drawer-is-minimized { + @apply bg-transparent pointer-events-none; +} + +.next-drawer.drawer-is-minimized > * { + @apply pointer-events-auto; +} + +.next-drawer.drawer-is-minimized > .next-drawer-panel-container > .next-drawer-panel { + height: 0px !important; +} + +.next-drawer.drawer-is-minimized > .next-drawer-panel-container { + @apply pointer-events-auto; +} + +.next-drawer > .next-drawer-panel-container { + @apply left-0 right-0 bottom-0; + transform: translateY(100%); +} + +.next-drawer > .next-drawer-panel-container { + @apply absolute transform transition ease-in-out duration-500 pointer-events-auto; +} + +.next-drawer > .next-drawer-panel-container > .next-drawer-panel { + @apply transform transition ease-in-out duration-500 pointer-events-auto bg-white shadow-next-nav; +} + +.next-drawer.drawer-is-open .next-drawer-panel-container .next-drawer-panel { + @apply shadow-none; +} + +body[data-theme='dark'] .next-drawer > .next-drawer-panel-container > .next-drawer-panel { + @apply bg-gray-900; +} + +.next-drawer > .next-drawer-panel-container > .gutter { + height: auto; + width: 100%; + background-image: none; + background-color: inherit; + cursor: row-resize; + background-position: unset; + background-repeat: unset; + position: relative; + z-index: 9999; + @apply border-2 border-gray-200 border-solid; +} + +body[data-theme="dark"] .next-drawer > .next-drawer-panel-container > .gutter { + @apply border-gray-500; +} + +.next-drawer > .next-drawer-panel-container > .gutter:hover, +.next-drawer.drawer-is-resizing > .next-drawer-panel-container > .gutter, +body[data-theme="dark"] .next-drawer > .next-drawer-panel-container > .gutter:hover, +body[data-theme="dark"] .next-drawer.drawer-is-resizing > .next-drawer-panel-container > .gutter { + @apply border-sky-500 bg-opacity-75; +} + +.next-drawer > .next-drawer-panel-container > .gutter > .notch { + position: absolute; + top: 0; + left: 0; + right: 0; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 3.5rem; + height: 1.25rem; + margin-left: auto; + margin-right: auto; + margin-top: -11px; + z-index: 9999999; + padding: 0 0.75rem; + cursor: grab !important; + @apply bg-gray-200 border-2 border-white shadow-md drop-shadow-md rounded-lg; +} + +body[data-theme="dark"] .next-drawer > .next-drawer-panel-container > .gutter > .notch { + @apply bg-gray-600 border-gray-600; +} + +.next-drawer > .next-drawer-panel-container > .gutter > .notch > .bar { + display: block; + height: 0.0955555555rem; + width: 100%; + margin: 0.09rem 0; + border-radius: 2px; + cursor: grab !important; + @apply bg-gray-400; +} + +body[data-theme='dark'] .next-drawer > .next-drawer-panel-container > .gutter > .notch > .bar { + @apply bg-gray-400; +} diff --git a/addon/styles/layout/next.css b/addon/styles/layout/next.css index eeea887..096f7e5 100644 --- a/addon/styles/layout/next.css +++ b/addon/styles/layout/next.css @@ -857,8 +857,9 @@ body[data-theme='dark'] .next-map-container-table-container { @apply outline-none; } +body[data-theme='dark'] .next-map-container-view-switch > button.active, .next-map-container-view-switch > button.active { - @apply bg-blue-500 text-white font-semibold; + @apply bg-sky-500 text-white font-semibold; } body[data-theme='dark'] .next-map-container-view-switch > button { @@ -871,10 +872,6 @@ body[data-theme='dark'] .next-map-container-view-switch > button:active { @apply outline-none; } -body[data-theme='dark'] .next-map-container-view-switch > button.active { - @apply bg-blue-500 text-white font-semibold; -} - .next-view-container { display: flex; flex-shrink: initial; diff --git a/app/components/drawer.js b/app/components/drawer.js new file mode 100644 index 0000000..a78f6ee --- /dev/null +++ b/app/components/drawer.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/ember-ui/components/drawer'; diff --git a/app/components/table/cell/point.js b/app/components/table/cell/point.js new file mode 100644 index 0000000..62c696e --- /dev/null +++ b/app/components/table/cell/point.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/ember-ui/components/table/cell/point'; diff --git a/tests/integration/components/drawer-test.js b/tests/integration/components/drawer-test.js new file mode 100644 index 0000000..b8100be --- /dev/null +++ b/tests/integration/components/drawer-test.js @@ -0,0 +1,26 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'dummy/tests/helpers'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | drawer', function (hooks) { + setupRenderingTest(hooks); + + test('it renders', async function (assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.set('myAction', function(val) { ... }); + + await render(hbs``); + + assert.dom(this.element).hasText(''); + + // Template block usage: + await render(hbs` + + template block text + + `); + + assert.dom(this.element).hasText('template block text'); + }); +}); diff --git a/tests/integration/components/table/cell/point-test.js b/tests/integration/components/table/cell/point-test.js new file mode 100644 index 0000000..87dd53f --- /dev/null +++ b/tests/integration/components/table/cell/point-test.js @@ -0,0 +1,26 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'dummy/tests/helpers'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | table/cell/point', function (hooks) { + setupRenderingTest(hooks); + + test('it renders', async function (assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.set('myAction', function(val) { ... }); + + await render(hbs``); + + assert.dom(this.element).hasText(''); + + // Template block usage: + await render(hbs` + + template block text + + `); + + assert.dom(this.element).hasText('template block text'); + }); +}); From 8cce2652de1dab22129a59bc1e0083ede946dcba Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Thu, 9 Nov 2023 11:19:26 +0800 Subject: [PATCH 07/11] patch `toggleMinimize()` method of drawer component --- addon/components/drawer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/components/drawer.js b/addon/components/drawer.js index 6dd7481..c6cf51b 100644 --- a/addon/components/drawer.js +++ b/addon/components/drawer.js @@ -68,7 +68,7 @@ export default class DrawerComponent extends Component { } @action toggleMinimize() { - this.isMinimized = !this.isMaximized; + this.isMinimized = !this.isMinimized; } @action minimize() { From c23bc1f291ad6d57b56f1c4b7de46a13bdeb437a Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Thu, 9 Nov 2023 11:34:08 +0800 Subject: [PATCH 08/11] only delay `displayPointFromRow` in point cell for 50ms --- addon/components/table/cell/point.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/components/table/cell/point.js b/addon/components/table/cell/point.js index ceefc69..f39829a 100644 --- a/addon/components/table/cell/point.js +++ b/addon/components/table/cell/point.js @@ -32,7 +32,7 @@ export default class TableCellPointComponent extends Component { } } }, - 300 + 50 ); } From ee8a97ed73be7fb8f9f8c4eb4a1db0afe2c6fe5d Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Thu, 9 Nov 2023 11:35:09 +0800 Subject: [PATCH 09/11] bump version for next release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ce434ed..3a4fa81 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fleetbase/ember-ui", - "version": "0.2.3", + "version": "0.2.4", "description": "Fleetbase UI provides all the interface components, helpers, services and utilities for building a Fleetbase extension into the Console.", "keywords": [ "fleetbase-ui", From 2898201b321b67d747aca0c01591e9ad672fb10d Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Thu, 9 Nov 2023 12:05:19 +0800 Subject: [PATCH 10/11] fix typo on Point cell callbacks --- addon/components/drawer.js | 6 ++---- addon/components/table/cell/point.js | 17 ++++++++++------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/addon/components/drawer.js b/addon/components/drawer.js index c6cf51b..12076e2 100644 --- a/addon/components/drawer.js +++ b/addon/components/drawer.js @@ -125,21 +125,19 @@ export default class DrawerComponent extends Component { return; } - const dx = event.clientX - this.mouseX; const dy = event.clientY - this.mouseY; const multiplier = -1; - const width = dx * multiplier + this.overlayWidth; const height = dy * multiplier + this.overlayHeight; const minResizeHeight = getWithDefault(this.args, 'minResizeHeight', 0); const maxResizeHeight = getWithDefault(this.args, 'maxResizeHeight', 600); - // Min resize width + // Min resize height if (height <= minResizeHeight) { drawerPanelNode.style.height = `${minResizeHeight}px`; return; } - // Max resize width + // Max resize height if (height >= maxResizeHeight) { drawerPanelNode.style.height = `${maxResizeHeight}px`; return; diff --git a/addon/components/table/cell/point.js b/addon/components/table/cell/point.js index f39829a..9921d8e 100644 --- a/addon/components/table/cell/point.js +++ b/addon/components/table/cell/point.js @@ -37,15 +37,18 @@ export default class TableCellPointComponent extends Component { } @action onClick() { - const { column } = this.args; - const { onClick, action } = column; + const column = this.args.column; - if (typeof onClick === 'funciton') { - onClick(this.args.row, ...arguments); - } + if (column) { + const { onClick, action } = column; + + if (typeof onClick === 'function') { + onClick(this.args.row, ...arguments); + } - if (typeof action === 'funciton') { - action(this.args.row); + if (typeof action === 'function') { + action(this.args.row); + } } } } From 1fa45518dcc0a53a1f83bc8c88b69243f4ec889a Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Thu, 9 Nov 2023 12:16:29 +0800 Subject: [PATCH 11/11] added docblocks for drawer component --- addon/components/drawer.js | 68 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/addon/components/drawer.js b/addon/components/drawer.js index 12076e2..fadebff 100644 --- a/addon/components/drawer.js +++ b/addon/components/drawer.js @@ -4,21 +4,62 @@ import { action } from '@ember/object'; import { later } from '@ember/runloop'; import getWithDefault from '@fleetbase/ember-core/utils/get-with-default'; +/** + * DrawerComponent provides a UI drawer element with several features such as + * open, close, toggle, and resize capabilities. + */ export default class DrawerComponent extends Component { + /** Node of the drawer element. */ @tracked drawerNode; + + /** Node of the drawer container element. */ @tracked drawerContainerNode; + + /** Node of the drawer panel element. */ @tracked drawerPanelNode; + + /** Node of the gutter element. */ @tracked gutterNode; + + /** Indicates if the backdrop is not present. */ @tracked noBackdrop = true; + + /** Indicates if the drawer is open. */ @tracked isOpen = true; + + /** Indicates if the drawer is resizable. */ @tracked isResizable = true; + + /** Indicates if the drawer is currently being resized. */ @tracked isResizing = false; + + /** Indicates if the drawer is minimized. */ @tracked isMinimized = false; + + /** Current X position of the mouse. */ @tracked mouseX = 0; + + /** Current Y position of the mouse. */ @tracked mouseY = 0; + + /** Height of the drawer. */ @tracked height = 300; + + /** Indicates if the drawer has been rendered. */ @tracked _rendered = false; + /** Context object providing drawer control functions and state. */ + context = { + toggle: this.toggle, + open: this.open, + close: this.close, + toggleMinimize: this.toggleMinimize, + minimize: this.minimize, + maximize: this.maximize, + isOpen: this.isOpen, + }; + + /** Context object providing drawer control functions and state. */ context = { toggle: this.toggle, open: this.open, @@ -29,6 +70,10 @@ export default class DrawerComponent extends Component { isOpen: this.isOpen, }; + /** + * Sets up the component, establishes default properties, and calls the onLoad callback if provided. + * @param {HTMLElement} element - The element to be used as the drawer node. + */ @action setupComponent(element) { this.drawerNode = element; this.height = getWithDefault(this.args, 'height', this.height); @@ -51,34 +96,49 @@ export default class DrawerComponent extends Component { ); } + /** + * Assigns a DOM node to a tracked property. + * @param {string} property - The property name to which the node will be assigned. + * @param {HTMLElement} node - The DOM node to be tracked. + */ @action setupNode(property, node) { this[`${property}Node`] = node; } + /** Toggles the open state of the drawer. */ @action toggle() { this.isOpen = !this.isOpen; } + /** Opens the drawer. */ @action open() { this.isOpen = true; } + /** Closes the drawer. */ @action close() { this.isOpen = false; } + /** Toggles the minimized state of the drawer. */ @action toggleMinimize() { this.isMinimized = !this.isMinimized; } + /** Minimizes the drawer. */ @action minimize() { this.isMinimized = true; } + /** Maximizes the drawer. */ @action maximize() { this.isMinimized = false; } + /** + * Starts the resize process for the drawer. + * @param {MouseEvent} event - The mouse event that initiates the resize. + */ @action startResize(event) { const disableResize = getWithDefault(this.args, 'disableResize', false); const onResizeStart = getWithDefault(this.args, 'onResizeStart', null); @@ -116,6 +176,10 @@ export default class DrawerComponent extends Component { } } + /** + * Resizes the drawer during a mousemove event. + * @param {MouseEvent} event - The mouse event that triggers the resize. + */ @action resize(event) { const disableResize = getWithDefault(this.args, 'disableResize', false); const onResize = getWithDefault(this.args, 'onResize', null); @@ -154,6 +218,10 @@ export default class DrawerComponent extends Component { } } + /** + * Stops the resizing process and cleans up event listeners. + * @param {MouseEvent} event - The mouse event that ends the resize. + */ @action stopResize(event) { const onResizeEnd = getWithDefault(this.args, 'onResizeEnd', null); const { drawerPanelNode } = this;