// src/index.ts import './index.css'; import 'vanillajs-datepicker/css/datepicker-bs5.css'; // import 'vanillajs-datepicker/css/datepicker-bs5.css'; import editorjsNestedChecklist from '@calumk/editorjs-nested-checklist'; import Checklist from '@editorjs/checklist'; import EditorJS from '@editorjs/editorjs'; import NestedList from '@editorjs/nested-list'; import DragDrop from 'editorjs-drag-drop'; import FontSizeTool from 'editorjs-inline-font-size-tool'; import AlignmentTuneTool from 'editorjs-text-alignment-blocktune'; import ColorPlugin from 'editorjs-text-color-plugin'; import Undo from 'editorjs-undo'; import Datepicker from 'node_modules/vanillajs-datepicker/js/Datepicker.js'; import { greetUser } from '$utils/greet'; // const savedState = localStorage.getItem('project_outline'); let editorCount = 0; // Counter to generate unique IDs for the editorjs_instance divs let dateInputCount = 0; // Counter for generating unique IDs for date-input // Initialize editable fields const editableFields = [ { id: 'project-name', defaultText: 'Project Name' }, { id: 'project-description', defaultText: 'Project Description' }, ]; const projectState = { startDate: '', cards: {}, projectName: '', projectDescription: '', }; window.Webflow ||= []; window.Webflow.push(() => { // Event listener for the "plus" button const addButton = document.querySelector('.button-outline-add.icon.w-inline-block'); if (addButton) { console.log('Add button found'); // Debugging line addButton.addEventListener('click', function (e) { e.preventDefault(); addCard(); }); } // Initialize editable fields handleEditableFields(editableFields); // Initialize VanillaJS Datepicker for this specific clone const start_date_input = document.querySelector('input[name="date-input-start"]'); const datepicker = new Datepicker(start_date_input, { ...datePickerSettings, }); const savedState = localStorage.getItem('project_outline'); if (savedState) { // If there is a saved state // Merge saved state into projectState Object.assign(projectState, JSON.parse(savedState)); console.log('projectState: ', projectState); // Debugging line // Set the date in the date picker if (projectState.startDate) { datepicker.setDate(new Date(projectState.startDate)); } // Populate editable fields editableFields.forEach(({ id, defaultText }) => { const elem = document.getElementById(id); if (elem && projectState[id]) { elem.innerText = projectState[id]; elem.style.color = 'black'; // Set color to black } else if (elem) { elem.innerText = defaultText; elem.style.color = 'dimgray'; // Set color to dimgray } }); // Initialize cards for (const [uniqueEditorID, card] of Object.entries(projectState.cards)) { addCard({ editorData: card.data, endDate: card.endDate }); } } else { addCard(); // Add the first card } // // Initialize datepicker // const date_input_start = document.querySelector('input[name="date-input"]'); // const date_input_end = document.querySelector('input[name="date-input-end"]'); // // const date_input = document.querySelector('input[name="date-input"]'); // // Load saved dates from localStorage // const savedStartDate = localStorage.getItem('savedStartDate'); // const savedEndDate = localStorage.getItem('savedEndDate'); // if (savedStartDate) { // date_input_start.value = savedStartDate; // } // if (savedEndDate) { // date_input_end.value = savedEndDate; // } // const datepicker_start = new Datepicker(date_input_start, { // buttonClass: 'btn', // autohide: true, // format: 'mm/dd/yyyy', // clearButton: true, // }); // const datepicker_end = new Datepicker(date_input_end, { // buttonClass: 'btn', // autohide: true, // format: 'mm/dd/yyyy', // clearButton: true, // }); // // Function to save date to localStorage // function saveDateToLocalStorage(inputElement, key) { // const dateValue = inputElement.value; // localStorage.setItem(key, dateValue); // } // // Attach event listeners to track changes // datepicker_start.element.addEventListener('changeDate', function () { // saveDateToLocalStorage(date_input_start, 'savedStartDate'); // }); // datepicker_end.element.addEventListener('changeDate', function () { // saveDateToLocalStorage(date_input_end, 'savedEndDate'); // }); // // Initialize EditorJS // const editor = new EditorJS({ // ...editorSettings, // Spread the common settings // holder: 'editorjs_instance', // }); // }); // Function to set z-index for active editor instances // Listen for the changeDate event start_date_input.addEventListener('changeDate', function (event) { // Get the selected date const selectedDate = datepicker.getDate('mm/dd/yyyy'); console.log('Selected date: ', selectedDate); // Debugging line // Update the startDate in projectState projectState.startDate = selectedDate; // Save the updated projectState to Local Storage localStorage.setItem('project_outline', JSON.stringify(projectState)); }); document.addEventListener( 'focusin', function (event) { let { target } = event; while (target !== this) { if (target.classList.contains('editorjs_instance')) { console.log('Focusin event triggered for:', target); // Debugging line // Reset z-index for all editor instances document.querySelectorAll('.editorjs_instance').forEach((editor) => { editor.style.zIndex = '1'; }); // Set z-index for the focused editor instance target.style.zIndex = '9998'; return; } target = target.parentNode; } }, true ); }); // Function to apply styles const applyStylesToEditorInstances = () => { const redactorElements = document.querySelectorAll('.codex-editor__redactor'); redactorElements.forEach((element) => { const redactorElement = element as HTMLElement; if (redactorElement) { redactorElement.style.paddingBottom = '0px'; } }); }; // Function to clone the template and append it to the container function addCard(cardData: { editorData?: any; endDate?: string } = {}) { const template = document.getElementById( 'w-node-_26d1be58-4f0a-015c-b856-4ac9fa85fa82-6bdc96f3' ) as HTMLElement; const clone = template.cloneNode(true) as HTMLElement; // Deep clone and cast to HTMLElement clone.style.display = 'block'; // Make it visible clone.id = ''; // Remove the id to avoid duplicates // Generate a unique ID for the editorjs_instance div const editorInstance = clone.querySelector('.editorjs_instance') as HTMLElement; const uniqueEditorID = `editorjs_instance_${editorCount++}`; editorInstance.id = uniqueEditorID; // Generate a unique ID for the date-input field const dateInputField = clone.querySelector('input[name="date-input"]') as HTMLInputElement; const uniqueDateInputID = `date_input_${dateInputCount++}`; dateInputField.id = uniqueDateInputID; // Append the clone to the container const container = document.querySelector( '.w-layout-grid.project-timeline-view-timeline' ) as HTMLElement; container.appendChild(clone); // Initialize EditorJS for this specific clone const editor = new EditorJS({ holder: uniqueEditorID, ...editorSettings, // Spread the common settings onReady: async () => { // If editorData is available, populate the EditorJS instance with it if (cardData.editorData) { await editor.render(cardData.editorData); } // Add the desired classes to the child div of the editor instance const parentElement = document.getElementById('editorjs_instance'); if (parentElement) { const childDiv = parentElement.querySelector('div'); if (childDiv) { childDiv.className = 'codex-editor codex-editor--narrow'; } } // Reorder the toolbar elements const parentDivs = document.querySelectorAll('.ce-toolbar__actions'); parentDivs.forEach((parentDiv) => { const plusElement = parentDiv.querySelector('.ce-toolbar__plus'); const settingsBtnElement = parentDiv.querySelector('.ce-toolbar__settings-btn'); if (plusElement && settingsBtnElement) { parentDiv.insertBefore(settingsBtnElement, plusElement); } }); // Overwrite the padding-bottom style for .codex-editor__redactor // Call this function every time you add a new instance applyStylesToEditorInstances(); new Undo({ editor }); new DragDrop(editor); }, onChange: async () => { // Function that saves the editor's content to local storage when changes are made try { const outputData = await editor.save(); // Check if there's an existing card with the same uniqueEditorID if (projectState.cards[uniqueEditorID]) { // Update only the data for this specific card projectState.cards[uniqueEditorID].data = outputData; } else { // Otherwise, initialize a new card with this uniqueEditorID projectState.cards[uniqueEditorID] = { data: outputData, endDate: '', // Initialize with an empty endDate }; } // Save the updated projectState to Local Storage localStorage.setItem('project_outline', JSON.stringify(projectState)); } catch (error) { console.log('Saving to local storage failed: ', error); } }, // ... other EditorJS options }); // Initialize VanillaJS Datepicker for this specific clone const datepicker = new Datepicker(document.getElementById(uniqueDateInputID), { ...datePickerSettings, }); // If endDate is available, set it in the date picker if (cardData.endDate) { datepicker.setDate(new Date(cardData.endDate)); } // Listen for the changeDate event datepicker.element.addEventListener('changeDate', function (event) { // Get the selected date from the datepicker const selectedDate = datepicker.getDate('mm/dd/yyyy'); // Use the uniqueEditorID to update the specific card's endDate if (projectState.cards[uniqueEditorID]) { projectState.cards[uniqueEditorID].endDate = selectedDate; } // Save the updated projectState to Local Storage localStorage.setItem('project_outline', JSON.stringify(projectState)); }); } function handleEditableFields(elements) { elements.forEach(({ id, defaultText }) => { const elem = document.getElementById(id); // If the element doesn't exist, skip to the next iteration if (!elem) return; // Initialize the element from projectState if (projectState[id]) { elem.innerText = projectState[id]; elem.style.color = 'black'; } // Change text to black when element gains focus elem.addEventListener('focus', function () { if (this.innerText === defaultText) { this.innerText = ''; } this.style.color = 'black'; }); // Restore default text and color when element loses focus elem.addEventListener('blur', function () { if (this.innerText === '') { this.innerText = defaultText; this.style.color = 'dimgray'; // Replace with the color you're using for dim text projectState[id] = ''; // Set the projectState to an empty string } else { this.style.color = 'black'; projectState[id] = this.innerText; // Update projectState with the new text } // Update local storage in both cases localStorage.setItem('project_outline', JSON.stringify(projectState)); }); }); } // Common EditorJS settings const editorSettings = { defaultBlock: 'paragraph', tools: { paragraph: { inlineToolbar: true, tunes: ['anyTuneName'], }, fontSize: FontSizeTool, list: { class: NestedList, inlineToolbar: true, config: { defaultStyle: 'unordered', }, }, checklist: { class: Checklist, inlineToolbar: true, }, nestedchecklist: { class: editorjsNestedChecklist, }, Color: { class: ColorPlugin, // if load from CDN, please try: window.ColorPlugin config: { colorCollections: [ '#EC7878', '#9C27B0', '#673AB7', '#3F51B5', '#0070FF', '#03A9F4', '#00BCD4', '#4CAF50', '#8BC34A', '#CDDC39', '#FFF', ], defaultColor: '#FF1300', type: 'text', customPicker: true, // add a button to allow selecting any colour }, }, Marker: { class: ColorPlugin, // if load from CDN, please try: window.ColorPlugin config: { defaultColor: '#FFBF00', type: 'marker', icon: ` `, }, }, anyTuneName: { class: AlignmentTuneTool, config: { default: 'left', blocks: { header: 'center', list: 'right', }, }, }, }, }; // Datepicker settings const datePickerSettings = { buttonClass: 'btn', autohide: true, format: 'mm/dd/yyyy', clearButton: true, };