### Initialize, Join, Subscribe, and Publish with Funky.Channel
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/components/channel.md
Quick start guide for initializing Funky.Channel, joining a room, subscribing to messages, and publishing new messages. Requires Funky.WebSocket and user information.
```javascript
Funky.Channel.init({
websocket: Funky.WebSocket,
userId: currentUser.id,
userName: currentUser.name
});
Funky.Channel.join('room:general');
Funky.Channel.subscribe('room:general', function(message, sender) {
console.log(sender.name + ':', message.text);
});
Funky.Channel.publish('room:general', {
type: 'chat',
text: 'Hello everyone!'
});
```
--------------------------------
### Create Feature Announcement Tour (One-Time)
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/components/tour.md
This example demonstrates how to create a feature announcement tour that is displayed only once to each user (`showOnce: true`) and starts automatically (`autoStart: true`). It highlights new features with specific steps and includes a callback function (`onComplete`) to show a success toast message.
```javascript
Funky.Tour.init('new-export-feature', {
showOnce: true,
autoStart: true,
steps: [
{
target: '#export-menu',
title: '🎉 New Export Options!',
content: 'We\'ve added new export formats including PDF and Excel.',
position: 'bottom'
},
{
target: '#export-pdf',
title: 'PDF Export',
content: 'Export your reports as beautifully formatted PDFs.'
},
{
target: '#export-excel',
title: 'Excel Export',
content: 'Full Excel support with formulas preserved.'
}
],
onComplete: function(tour) {
Funky.Toast.success('Enjoy the new features!');
}
});
```
--------------------------------
### JavaScript Carousel Initialization Examples
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/components/carousel.md
Demonstrates various ways to initialize the Funky Carousel component using JavaScript. It covers basic setup, responsive configurations, autoplay, synchronization between carousels, center mode, fullscreen capabilities, and full-width layouts.
```javascript
var carousel = Funky.Carousel.init('#gallery', {
slidesToShow: 1,
dots: true,
arrows: true,
lazyLoad: 'ondemand'
});
```
```javascript
var carousel = Funky.Carousel.init('#products', {
slidesToShow: 4,
slidesToScroll: 2,
gap: 24,
responsive: [
{ breakpoint: 1200, settings: { slidesToShow: 3 } },
{ breakpoint: 768, settings: { slidesToShow: 2 } },
{ breakpoint: 480, settings: { slidesToShow: 1 } }
]
});
```
```javascript
var carousel = Funky.Carousel.init('#hero', {
slidesToShow: 1,
autoplay: true,
autoplaySpeed: 6000,
pauseOnHover: true,
showProgress: true,
infinite: true
});
// Programmatic autoplay control
carousel.toggleAutoplay(); // Toggle on/off
carousel.startAutoplay(); // Start
carousel.stopAutoplay(); // Stop
```
```javascript
// Main carousel
var main = Funky.Carousel.init('#main-gallery', {
slidesToShow: 1,
fade: true,
syncWith: '#thumb-gallery'
});
// Thumbnail carousel
var thumbs = Funky.Carousel.init('#thumb-gallery', {
slidesToShow: 5,
gap: 8,
syncWith: '#main-gallery'
});
```
```javascript
var carousel = Funky.Carousel.init('#showcase', {
centerMode: true,
centerPadding: '80px',
slidesToShow: 1,
infinite: true
});
```
```javascript
var carousel = Funky.Carousel.init('#gallery', {
slidesToShow: 1,
fullscreen: true, // Show fullscreen button
dots: true,
arrows: true
});
// Programmatic fullscreen control
carousel.enterFullscreen(); // Enter fullscreen
carousel.exitFullscreen(); // Exit fullscreen
carousel.toggleFullscreen(); // Toggle
```
```javascript
// Carousel stretches edge-to-edge across the viewport
var carousel = Funky.Carousel.init('#hero', {
fullWidth: true, // Container fills viewport width
fullWidthSlides: true, // Each slide takes 100% width
infinite: true,
autoplay: true,
autoplaySpeed: 5000,
arrows: true,
dots: true
});
```
```javascript
var carousel = Funky.Carousel.init('#dynamic');
// Add slide
document.getElementById('add').onclick = function() {
carousel.addSlide('
New Slide
');
};
// Remove current slide
document.getElementById('remove').onclick = function() {
carousel.removeSlide(carousel.getCurrentIndex());
};
```
--------------------------------
### Hover Tooltips/Feedback HTML Example
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/components/zero-click.md
HTML examples demonstrating how to configure hover-based tooltips and feedback using the `data-zero-click` attribute. These examples show how to trigger `Toast.info` on mouse enter and `Toast.warning` on mouse leave.
```html
Important Content
```
--------------------------------
### HTML: Funky Card Component Structure
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/playground/components/code-preview/index.html
Provides the basic HTML structure for a 'funky-card' component. It includes a header, body, and a 'Get Started' button with a `data-action` attribute for event handling. This structure is intended to be styled with CSS.
```html
User Profile
Welcome to Funky!
```
--------------------------------
### Basic Setup with Funky.Table and Bulk Actions
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/components/bulk-actions.md
Demonstrates how to initialize a Funky.Table with multi-select enabled and then create a BulkActions instance. It includes defining actions and implementing the `onAction` callback to handle different actions like delete, export, and archive.
```javascript
// Create table with multi-select enabled
var table = Funky.Table.init('#tradesTable', {
selectable: 'multi',
idField: 'trade_id',
columns: [
{ data: 'trade_id', title: 'ID' },
{ data: 'symbol', title: 'Symbol' },
{ data: 'quantity', title: 'Qty' }
],
data: trades
});
// Attach bulk actions
var bulk = Funky.BulkActions.create({
table: table,
actions: [
{ id: 'delete', label: 'Delete', icon: 'fa-trash', variant: 'danger' },
{ id: 'export', label: 'Export', icon: 'fa-download' },
{ id: 'archive', label: 'Archive', icon: 'fa-archive', variant: 'warning' }
],
onAction: function(actionId, selectedItems, table) {
var ids = selectedItems.map(function(row) { return row.trade_id; });
switch (actionId) {
case 'delete':
Funky.Api.post('/api/trades/bulk-delete', { ids: ids }).then(function() {
Funky.Toast.success('Deleted ' + ids.length + ' trades');
table.reload();
});
break;
case 'export':
window.location.href = '/api/trades/export?ids=' + ids.join(',');
break;
case 'archive':
Funky.Api.post('/api/trades/archive', { ids: ids }).then(function() {
Funky.Toast.success('Archived ' + ids.length + ' trades');
});
break;
}
}
});
```
--------------------------------
### Get All Queued Requests with Filter (JavaScript)
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/core/request-queue.md
Retrieves all requests currently in the Funky.RequestQueue. It shows how to filter these requests, for example, to get only those with a 'pending' status.
```javascript
var pending = Funky.RequestQueue.getAll({ status: 'pending' });
```
--------------------------------
### Complete Page Setup with Funky.CRUD Initialization (HTML & JavaScript)
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/components/crud.md
This snippet demonstrates a full page setup for managing clients using Funky.CRUD. It includes HTML for a stats bar, toolbar, and data table, along with JavaScript to initialize the CRUD functionality with specific configurations for table display, statistics, view, form schema, and import options. It also includes event handlers for 'New Client' and 'Import Clients' buttons.
```html
ID
Name
Email
Status
Created
Actions
```
--------------------------------
### Wizard Method Examples (JavaScript)
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/COMPONENTS.md
Demonstrates common methods for controlling the wizard flow, such as navigating between steps, retrieving data, and resetting the wizard.
```javascript
wizard.next(); // Go to next step
wizard.prev(); // Go to previous step
wizard.goTo('step-id'); // Go to specific step
wizard.getData(); // Get collected form data
wizard.reset(); // Reset to first step
```
--------------------------------
### Get the Start of a Day
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/core/date.md
Calculates the start of a given day by setting the time to 00:00:00.000. This function is useful for normalizing dates to the beginning of the day for comparisons or calculations.
```javascript
var d = Funky.Date.startOfDay(new Date('2024-06-15T14:30:00'));
// Sat Jun 15 2024 00:00:00
```
--------------------------------
### Basic WIP Overlay Initialization and Display (JavaScript)
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/components/wip-overlay.md
Demonstrates the basic usage of Funky.WIPOverlay by initializing it with a custom message and then showing the overlay. This is suitable for simple implementations where a single overlay is needed.
```javascript
// Simple usage without ID - uses default instance
Funky.WIPOverlay.init({
message: '🚧 Coming Soon 🚧',
subMessage: 'This feature is under development'
});
Funky.WIPOverlay.show();
```
--------------------------------
### Get Start of Week - JavaScript
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/core/date.md
Calculates the start of the week for a given date, with an option to specify the starting day of the week (Sunday or Monday). It takes a Date object and an optional number (0 for Sunday, 1 for Monday) and returns a Date object.
```javascript
// Week starting Sunday
var d = Funky.Date.startOfWeek(new Date('2024-06-15'), 0);
// Sun Jun 09 2024 00:00:00
// Week starting Monday
var d = Funky.Date.startOfWeek(new Date('2024-06-15'), 1);
// Mon Jun 10 2024 00:00:00
```
--------------------------------
### Initialize Funky.QuickNav Component (JavaScript)
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/playground/components/quick-nav/index.html
Initializes the Funky.QuickNav component with various configuration options. It handles destruction of existing instances, adds default custom actions, and sets up PubSub event listeners for QuickNav interactions. Dependencies include Funky.QuickNav, Funky.Toast, and Funky.PubSub. Outputs logged messages and updates UI state.
```javascript
var emit = window.PlaygroundCanvas ? window.PlaygroundCanvas.emit : function() {};
var getProps = window.PlaygroundCanvas ? window.PlaygroundCanvas.getProps : function() { return {}; };
var logs = [];
var customActionCounter = 0;
var currentBadgeValue = 0;
var lastAddedActionId = null;
function log(message, data) {
var timestamp = new Date().toLocaleTimeString();
var entry = timestamp + ' - ' + message;
if (data !== undefined) {
entry += ': ' + JSON.stringify(data);
}
logs.unshift(entry);
if (logs.length > 50) logs.pop();
var logEl = document.getElementById('quicknav-log');
if (logEl) {
logEl.textContent = logs.join('\n');
}
}
function updateState() {
var stateEl = document.getElementById('quicknav-state');
if (!stateEl || !Funky.QuickNav) return;
var isVisible = Funky.QuickNav.isVisible ? Funky.QuickNav.isVisible() : false;
var isExpanded = Funky.QuickNav.isExpanded ? Funky.QuickNav.isExpanded() : false;
var position = Funky.QuickNav.getPosition ? Funky.QuickNav.getPosition() : 'unknown';
var sections = Funky.QuickNav.getSections ? Funky.QuickNav.getSections() : [];
var actions = Funky.QuickNav.getActions ? Funky.QuickNav.getActions() : [];
var currentIndex = Funky.QuickNav.getCurrentSectionIndex ? Funky.QuickNav.getCurrentSectionIndex() : -1;
var stayExpanded = Funky.QuickNav.getStayExpanded ? Funky.QuickNav.getStayExpanded() : false;
stateEl.textContent = JSON.stringify({
isVisible: isVisible,
isExpanded: isExpanded,
position: position,
sectionCount: sections.length,
actionCount: actions.length,
currentSectionIndex: currentIndex,
stayExpanded: stayExpanded
}, null, 2);
}
Funky.Pages.register('component-quick-nav', {
init: function(state) {
var props = getProps();
if (!Funky.QuickNav) {
log('ERROR', 'Funky.QuickNav not available');
return;
}
if (Funky.QuickNav._getState && Funky.QuickNav._getState().initialized) {
Funky.QuickNav.destroy();
}
var config = {
position: props.position || 'bottom-right',
collapsed: props.collapsed !== false,
backToTop: true,
sections: true,
showTrigger: 'always',
collapseOnAction: true,
collapseDelay: 5000,
spa: {
autoRefresh: true,
persistActions: true
},
preferences: {
enabled: false
}
};
Funky.QuickNav.init(config);
log('QuickNav initialized', { position: config.position });
if (props.showHome !== false) {
Funky.QuickNav.addAction({
id: 'demo-home',
icon: 'fas fa-home',
label: 'Home',
order: 10,
onClick: function() {
log('Home action clicked');
Funky.Toast.info('Home action triggered!');
}
});
}
if (props.showSettings !== false) {
Funky.QuickNav.addAction({
id: 'demo-settings',
icon: 'fas fa-cog',
label: 'Settings',
order: 20,
onClick: function() {
log('Settings action clicked');
Funky.Toast.info('Settings action triggered!');
}
});
}
if (Funky.PubSub) {
Funky.PubSub.on('funky:quick-nav:expanded', function() {
log('QuickNav expanded');
updateState();
emit('event', { event: 'quicknav:expanded', payload: {} });
});
Funky.PubSub.on('funky:quick-nav:collapsed', function() {
log('QuickNav collapsed');
updateState();
emit('event', { event: 'quicknav:collapsed', payload: {} });
});
Funky.PubSub.on('funky:quick-nav:shown', function() {
log('QuickNav shown');
updateState();
emit('event', { event: 'quicknav:shown', payload: {} });
});
Funky.PubSub.on('funky:quick-nav:hidden', function() {
log('QuickNav hidden');
updateState();
emit('event', { event: 'quicknav:hidden', payload: {} });
});
Funky.PubSub.on('funky:quick-nav:navigate', function(data) {
log('QuickNav navigate', data);
updateState();
emit('event', { event: 'quicknav:navigate', payload: data });
});
Funky.PubSub.on('funky:quick-nav:section:navigated', function(data) {
log('Section navigated', data);
updateState();
emit('event', { event: 'quicknav:section:navigated', payload: data });
});
Funky.PubSub.on('funky:quick-nav:action:click', function(data) {
log('Action clicked', { id: data.id });
emit('event', { event: 'quicknav:action:click', payload: data });
});
Funky.PubSub.on('funky:quick-nav:action:added', function(data) {
log('Action added', { id: data.id });
updateState();
});
Funky.PubSub.on('funky:quick-nav:action:removed', function(data) {
log('Action removed', { id: data.id });
updateState();
});
Funky.PubSub.on('funky:quick-nav:position:changed', function(data) {
log('Position changed', data);
updateState();
emit('event', { event: 'quicknav:position:changed', payload: data });
});
Funky.PubSub.on('funky:quick-nav:badge:updated', function(data) {
log('Badge updated', data);
});
Funky.PubSub.on('funky:quick-nav:badge:cleared', function(data) {
log('Badge cleared', data);
});
}
updateState();
document.getElementById('btn-expand').onclick = function() {
Funky.QuickNav.expand();
log('expand');
};
}
});
```
--------------------------------
### Install Funky Frame Clipboard
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/playground/components/clipboard/index.html
Install the Funky Frame library using npm or yarn to enable clipboard functionality in your project. This is the initial setup step required before using any of its features.
```bash
npm install funky-frame
```
```bash
yarn add funky-frame
```
--------------------------------
### Welcome Flow Example - JavaScript
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/components/zero-click.md
An example of a 'Welcome Flow' using Funky ZeroClick. It defines actions to show a success toast with the user's name and hide a WIP overlay after a delay, triggered by a user login event.
```javascript
Funky.ZeroClick.on('funky:user:logged-in', [
{ component: 'Toast', method: 'success', args: ['Welcome back, {{user.name}}!'] },
{ component: 'WIPOverlay', method: 'hide', delay: 500 }
]);
```
--------------------------------
### Start Funky Frame Interactive Playground
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/README.md
This command starts the Funky Frame development server, which hosts an interactive playground. This allows you to test components directly in the browser without additional setup.
```bash
npm start
```
--------------------------------
### FunkyTests API: beforeEach and afterEach Example (JavaScript)
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/llm_context/TESTING.md
This JavaScript example shows how to use the `beforeEach` and `afterEach` functions provided by the FunkyTests API. These functions are executed before and after each test within a `describe` block, respectively, facilitating test setup and cleanup.
```javascript
beforeEach(function() {
// setup
});
afterEach(function() {
// teardown
});
```
--------------------------------
### Create Funky.CardGrid Instance with Options
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/components/card-grid.md
Demonstrates the initialization of a CardGrid instance using the `init` factory method. This example shows how to provide data, specify the number of columns, and enable multi-selection.
```javascript
var grid = Funky.CardGrid.init('#container', {
items: myData,
columns: 3,
selectable: 'multi'
});
```
--------------------------------
### Built-in Actions: Initialize with Theme Toggle and Help
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/components/quick-nav.md
Initializes QuickNav with specific built-in actions enabled, such as 'theme-toggle' for switching between light and dark themes, and 'help' for emitting a help event. Requires Funky.QuickNav.init().
```javascript
Funky.QuickNav.init({
builtinActions: ['theme-toggle', 'help']
});
```
--------------------------------
### FunkyTests Test File Structure Example (JavaScript)
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/llm_context/TESTING.md
This JavaScript code demonstrates the standard structure for a FunkyTests test file. It includes setup for describe, it, expect, beforeEach, and afterEach functions, along with example tests for initialization and methods of a 'ComponentName'.
```javascript
/**
* ComponentName Tests
*/
(function() {
'use strict';
var describe = FunkyTests.describe;
var it = FunkyTests.it;
var expect = FunkyTests.expect;
var beforeEach = FunkyTests.beforeEach;
var afterEach = FunkyTests.afterEach;
describe('Funky.ComponentName', function() {
var container;
var instance;
beforeEach(function() {
// Setup before each test
container = document.createElement('div');
container.id = 'test-container';
document.body.appendChild(container);
});
afterEach(function() {
// Cleanup after each test
if (instance && instance.destroy) {
instance.destroy();
}
if (container.parentNode) {
container.parentNode.removeChild(container);
}
});
describe('initialization', function() {
it('should create instance', function() {
instance = Funky.ComponentName.init('#test-container', {});
expect(instance).toBeTruthy();
});
it('should apply config options', function() {
instance = Funky.ComponentName.init('#test-container', {
option: 'value'
});
expect(instance.config.option).toBe('value');
});
});
describe('methods', function() {
beforeEach(function() {
instance = Funky.ComponentName.init('#test-container', {});
});
it('should have required methods', function() {
expect(typeof instance.destroy).toBe('function');
});
});
});
})();
```
--------------------------------
### Trade Creation Wizard Example
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/components/wizard.md
An example demonstrating how to create and use a trade creation wizard.
```APIDOC
## Trade Creation Wizard Example
```javascript
var tradeWizard = Funky.Wizard.create({
modalId: 'tradeWizardModal',
title: 'Create Trade',
steps: [
{
type: 'form',
stateKey: 'trade',
title: 'Trade Details',
schemaPath: 'CreateTrade'
},
{
type: 'checkboxes',
stateKey: 'allocations',
title: 'Select Allocations',
getOptions: fetchAllocations
},
{
type: 'custom',
stateKey: 'review',
title: 'Review & Confirm',
render: renderReviewStep
}
],
onSave: function(state, mode) {
var endpoint = mode === 'create' ? '/api/trades' : '/api/trades/' + state.id;
var method = mode === 'create' ? 'post' : 'put';
return Funky.Api[method](endpoint, state.trade).then(function() {
Funky.Toast.success('Trade ' + (mode === 'create' ? 'created' : 'updated'));
});
}
});
// Open in create mode
$('#newTradeBtn').on('click', function() { tradeWizard.create(); });
// Open in edit mode
$('#editTradeBtn').on('click', function() {
Funky.Api.get('/api/trades/' + tradeId).then(function(trade) {
tradeWizard.edit(trade.data);
});
});
```
```
--------------------------------
### Focus Editor Instance (JavaScript)
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/components/wysimark.md
Provides an example of how to programmatically focus the Wysimark editor instance using the `focus()` method. This can be used to guide user interaction.
```javascript
editor.focus();
```
--------------------------------
### Funky.Events Quick Start Examples (JavaScript)
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/core/events.md
Demonstrates the basic usage of Funky.Events for common DOM event handling tasks. Includes examples for attaching listeners, one-time events, emitting custom events, event delegation, and DOM readiness checks.
```javascript
var element = document.getElementById('myElement');
var list = document.getElementById('myList');
// Basic event handling
E.on(element, 'click', function(e) {
console.log('Clicked!');
});
// One-time event
E.once(element, 'load', function(e) {
console.log('Loaded once!');
});
// Custom events
E.emit(element, 'custom.event', { data: 'value' });
// Event delegation
var cleanup = E.delegate(list, '.item', 'click', function(e) {
console.log('Item clicked');
});
// DOM ready
E.ready(function() {
console.log('DOM is ready');
});
```
--------------------------------
### Initialize Funky Kanban Board
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/playground/components/kanban/index.html
Initializes the Funky Kanban board with specified container and configuration. It logs the board creation and emits an initialization event. Dependencies include Funky.Kanban and potentially Funky.PubSub for prop changes.
```javascript
if (Funky.Kanban) {
kanbanInstance = Funky.Kanban.init(container, boardConfig);
log('Board created with ' + defaultColumns.length + ' columns and ' + cardCount + ' cards');
emit('event', { event: 'kanban:init', payload: { columns: defaultColumns.length, cards: cardCount } });
updateState();
}
```
--------------------------------
### Initialize Navigation Demo
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/playground/components/navigation/index.html
Initializes the navigation demo by setting up event listeners for UI elements and applying initial properties. It retrieves configuration from a parent window or defaults and updates the UI accordingly. It also handles communication with the parent window for navigation state changes.
```javascript
(function() { 'use strict'; var getProps = window.PlaygroundCanvas ? window.PlaygroundCanvas.getProps : function() { return {}; }; var emit = window.PlaygroundCanvas ? window.PlaygroundCanvas.emit : function() {}; var propsChangedHandler = null; var pendingCallbacks = {}; var callbackId = 0; function log(message, type) { console.log('[Navigation Demo]', message); emit('event', { event: 'navigation:' + (type || 'info'), payload: { message: message } }); } function getOptions() { var props = getProps(); return { position: props.position || 'left', collapsed: props.collapsed === true, persistScroll: props.persistScroll !== false, persistCollapse: props.persistCollapse !== false, storageKey: props.storageKey || 'nav-demo' }; } function updatePropsDisplay() { var opts = getOptions(); var el = function(id) { return document.getElementById(id); }; if (el('setting-position')) el('setting-position').textContent = opts.position; if (el('setting-collapsed')) el('setting-collapsed').textContent = String(opts.collapsed); if (el('setting-persist-scroll')) el('setting-persist-scroll').textContent = String(opts.persistScroll); if (el('setting-persist-collapse')) el('setting-persist-collapse').textContent = String(opts.persistCollapse); if (el('setting-storage-key')) el('setting-storage-key').textContent = opts.storageKey; } function updateActiveButton() { var position = document.body.getAttribute('data-nav-position') || 'left'; var btns = document.querySelectorAll('.navigation-demo__position-btn'); btns.forEach(function(btn) { btn.classList.toggle('active', btn.dataset.position === position); }); } function showApiResult(method, result) { var resultEl = document.getElementById('api-result'); if (!resultEl) return; var typeStr = typeof result; var valueStr = JSON.stringify(result); resultEl.innerHTML = '
'; log(method + ' = ' + valueStr, 'api'); } // Call parent's NavPosition method via postMessage function callParentNavMethod(namespace, method, callback) { var id = ++callbackId; pendingCallbacks[id] = callback; try { window.parent.postMessage({ type: 'funky-nav-call', id: id, namespace: namespace, method: method }, '*'); } catch (e) { callback('Error: ' + e.message); delete pendingCallbacks[id]; } } // Handle responses from parent function handleMessage(event) { if (event.data && event.data.type === 'funky-nav-response') { var callback = pendingCallbacks[event.data.id]; if (callback) { callback(event.data.result); delete pendingCallbacks[event.data.id]; } } } function setParentNavPosition(position) { // Send message to parent to change its nav position try { window.parent.postMessage({ type: 'funky-nav-set-position', position: position }, '*'); } catch (e) { console.warn('[Navigation Demo] Could not message parent:', e); } } function setupPositionButtons() { var btns = document.querySelectorAll('.navigation-demo__position-btn'); btns.forEach(function(btn) { btn.addEventListener('click', function() { var position = btn.dataset.position; // Change parent's nav position setParentNavPosition(position); // Also update local for demo consistency document.body.setAttribute('data-nav-position', position); updateActiveButton(); log('Set position to: ' + position, 'position'); }); }); } function setupApiButtons() { var btns = document.querySelectorAll('[data-api]'); btns.forEach(function(btn) { btn.addEventListener('click', function() { var method = btn.dataset.api; var namespace = method.indexOf('Scroll') >= 0 ? 'Navigation' : 'NavPosition'; var displayMethod = 'Funky.' + namespace + '.' + method + '()'; // First try local Funky namespace var localResult = null; try { if (namespace === 'Navigation') { if (Funky.Navigation && Funky.Navigation[method]) { Funky.Navigation[method](); localResult = 'Called (void)'; } } else if (Funky.NavPosition && Funky.NavPosition[method]) { localResult = Funky.NavPosition[method](); } } catch (e) { // Ignore local errors, will try parent } if (localResult !== null) { showApiResult(displayMethod, localResult); } else { // Try parent via postMessage callParentNavMethod(namespace, method, function(result) { showApiResult(displayMethod, result); }); } }); }); } function applyPropsToParent() { var opts = getOptions(); // Apply position to local and parent document.body.setAttribute('data-nav-position', opts.position); setParentNavPosition(opts.position); updateActiveButton(); // Send collapsed state to parent try { window.parent.postMessage({ type: 'funky-nav-set-collapsed', collapsed: opts.collapsed }, '*'); } catch (e) { console.warn('[Navigation Demo] Could not message parent:', e); } // Send persistScroll state to parent try { window.parent.postMessage({ type: 'funky-nav-set-persistScroll', persistScroll: opts.persistScroll }, '*'); } catch (e) { console.warn('[Navigation Demo] Could not message parent:', e); } // Send persistCollapse state to parent try { window.parent.postMessage({ type: 'funky-nav-set-persistCollapse', persistCollapse: opts.persistCollapse }, '*'); } catch (e) { console.warn('[Navigation Demo] Could not message parent:', e); } // Send storageKey to parent try { window.parent.postMessage({ type: 'funky-nav-set-storageKey', storageKey: opts.storageKey }, '*'); } catch (e) { console.warn('[Navigation Demo] Could not message parent:', e); } } // Initialize window event listener window.addEventListener('message', handleMessage); // Initial setup updatePropsDisplay(); updateActiveButton(); setupPositionButtons(); setupApiButtons(); applyPropsToParent(); })();
```
--------------------------------
### Get Calendar View Range
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/core/date.md
Calculates the start and end dates for a given calendar view ('month', 'week', or 'day'). It requires a reference date, the view type, and an optional week start day. The returned range might include dates outside the specified month or week.
```javascript
var range = Funky.Date.getViewRange(new Date('2024-06-15'), 'month', 1);
// range.start = first visible day (may be in May)
// range.end = last visible day (may be in July)
```
--------------------------------
### Get Visible Date Range
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/components/calendar.md
Returns the date range currently visible in the calendar's active view. The output is an object containing `start` and `end` Date objects.
```javascript
var range = calendar.getRange();
console.log('Visible from:', range.start, 'to', range.end);
```
--------------------------------
### Handlebars Date Formatting Examples
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/core/live-binding.md
Shows how to format dates and times using Handlebars. This includes standard date and time formats, as well as relative time formatting (e.g., '5m ago').
```handlebars
{{created|date}}
{{created|time}}
{{updated|relative}}
```
--------------------------------
### Initialize and Configure Context Menu (JavaScript)
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/playground/components/context-menu/index.html
Initializes the Funky ContextMenu module, synchronizes input values from properties, and sets up event listeners for applying configuration changes and triggering menu actions. It handles default menu items and allows overriding them via component properties.
```javascript
var emit = window.PlaygroundCanvas ? window.PlaygroundCanvas.emit : function() {};
var getProps = window.PlaygroundCanvas ? window.PlaygroundCanvas.getProps : function() { return {}; };
var logs = [];
var attachments = {};
var propsChangedHandler = null;
var dynamicItemCounter = 0;
function log(message) {
logs.unshift(new Date().toLocaleTimeString() + ' - ' + message);
if (logs.length > 15) logs.pop();
var logEl = document.getElementById('context-log');
if (logEl) logEl.textContent = logs.join('\n');
}
function syncInputsFromProps(props) {
var minWidthInput = document.getElementById('min-width');
var maxWidthInput = document.getElementById('max-width');
var longPressInput = document.getElementById('long-press-delay');
if (minWidthInput && props.minWidth != null) minWidthInput.value = props.minWidth;
if (maxWidthInput && props.maxWidth != null) maxWidthInput.value = props.maxWidth;
if (longPressInput && props.longPressDelay != null) longPressInput.value = props.longPressDelay;
}
function parseItemsFromProps(props) {
if (!props.items) return null;
try {
return typeof props.items === 'string' ? JSON.parse(props.items) : props.items;
} catch (e) {
return null;
}
}
Funky.Pages.register('component-context-menu', {
init: function(state) {
var props = getProps();
var ContextMenu = Funky.ContextMenu;
if (!ContextMenu) {
log('Error: Funky.ContextMenu not available');
return;
}
log('ContextMenu module initialized');
syncInputsFromProps(props);
var minWidthInput = document.getElementById('min-width');
var maxWidthInput = document.getElementById('max-width');
var longPressInput = document.getElementById('long-press-delay');
var apiStatus = document.getElementById('api-status');
var defaultItems = [
{ id: 'cut', label: 'Cut', icon: 'fa-scissors', shortcut: '\u2318X' },
{ id: 'copy', label: 'Copy', icon: 'fa-copy', shortcut: '\u2318C' },
{ id: 'paste', label: 'Paste', icon: 'fa-paste', shortcut: '\u2318V' },
{ divider: true },
{ id: 'select-all', label: 'Select All', icon: 'fa-check-double', shortcut: '\u2318A' },
{ divider: true },
{ id: 'preferences', label: 'Preferences', icon: 'fa-cog' }
];
function createBasicMenu() {
if (attachments.basic) {
ContextMenu.destroy('#context-basic');
}
var currentProps = getProps();
var itemsFromProps = parseItemsFromProps(currentProps);
var menuItems = itemsFromProps || defaultItems;
attachments.basic = ContextMenu.attach('#context-basic', {
minWidth: parseInt(minWidthInput.value, 10) || 160,
maxWidth: parseInt(maxWidthInput.value, 10) || 320,
longPressDelay: parseInt(longPressInput.value, 10) || 500,
items: menuItems,
onSelect: function(id, target, event) {
log('Basic: Selected "' + id + '"');
emit('event', { event: 'context-menu:select', payload: { context: 'basic', id: id } });
},
onShow: function(target, event) {
log('Basic: Menu opened');
},
onHide: function() {
log('Basic: Menu closed');
}
});
log('Basic menu attached: minWidth=' + minWidthInput.value + ', maxWidth=' + maxWidthInput.value + ', longPressDelay=' + longPressInput.value);
}
createBasicMenu();
document.getElementById('btn-apply-config').addEventListener('click', function() {
createBasicMenu();
emit('event', { event: 'context-menu:config', payload: { minWidth: parseInt(minWidthInput.value, 10), maxWidth: parseInt(maxWidthInput.value, 10), longPressDelay: parseInt(longPressInput.value, 10) }});
});
document.getElementById('btn-show').addEventListener('click', function(e) {
var targetArea = document.getElementById('context-basic');
var targetRect = targetArea.getBoundingClientRect();
var x = targetRect.left + targetRect.width / 2;
var y = targetRect.top + targetRect.height / 2;
if (attachments.basic && attachments.basic._getItems) {
var items = attachments.basic._getItems(targetArea, null);
var showOptions = Object.assign({}, attachments.basic.options, { items: items });
ContextMenu._showMenu(x, y, showOptions, targetArea, null);
apiStatus.textContent = 'Status: show() triggered menu at target area center';
log('API: show() called - opened menu at (' + Math.round(x) + ', ' + Math.round(y) + ')');
} else {
apiStatus.textContent = 'Status: No menu attached';
}
});
document.getElementById('btn-hide').addEventListener('click', function() {
ContextMenu.hide();
apiStatus.textContent = 'Status: hide() called';
log('API: hide() called');
});
}
});
```
--------------------------------
### Funky.Dom: DOM Traversal Examples
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/core/dom.md
Provides examples of using Funky.Dom's traversal methods to navigate the DOM tree. It demonstrates finding the closest ancestor element using `.closest()`, retrieving sibling elements with `.siblings()`, and getting an element's index among its siblings using `.index()`.
```javascript
// Find ancestor
var row = D.one('.cell').closest('.table-row');
var modal = D.one('.btn-close').closest('.modal');
// Get siblings
var allSiblings = D.one('.tab-active').siblings();
var siblingTabs = D.one('.tab-active').siblings('.tab');
// Get position
var position = D.one('.list-item.selected').index();
```
--------------------------------
### Minimal Table-Only Setup with Funky.CRUD Initialization (JavaScript)
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/components/crud.md
This snippet shows a minimal setup for Funky.CRUD, focusing solely on displaying data in a table. It initializes the CRUD for a 'log' entity, specifying the table selector and its AJAX data source and column configurations. This setup omits features like stats, views, forms, and import functionality.
```javascript
Funky.CRUD.init({
entity: 'log',
tableSelector: '#logsTable',
tableConfig: {
ajax: '/api/logs',
columns: [
{ data: 'timestamp', render: Funky.Renderers.datetime() },
{ data: 'level' },
{ data: 'message', render: Funky.Renderers.truncate(100) }
]
}
// No stats, view, form, or import
});
```
--------------------------------
### Container Tracker Control Functions (JavaScript)
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/playground/components/scroll-tracker/index.html
Offers functions to start, stop, and get the state of the container tracker. These operations are managed through a `containerTracker` object and provide feedback via UI updates and event logging.
```javascript
function handleContainerStart() { if (containerTracker) { containerTracker.start(); showResult('controls-result', 'Container tracker started', 'success'); logEvent('container:start', {}); } }
function handleContainerStop() { if (containerTracker) { containerTracker.stop(); showResult('controls-result', 'Container tracker stopped', 'success'); logEvent('container:stop', {}); } }
function handleContainerState() { if (containerTracker) { var state = containerTracker.getState(); showResult('controls-result', state, 'info'); logEvent('container:getState', state); } }
```
--------------------------------
### Get Date Boundaries
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/playground/components/date/index.html
Calculates the start and end of a day, week, or month for a given input date. This is useful for setting time ranges or performing calculations within specific periods.
```javascript
const inputDate = new Date('2023-10-26T14:30:00');
console.log('startOfDay:', dateUtils.startOfDay(inputDate));
console.log('endOfDay:', dateUtils.endOfDay(inputDate));
console.log('startOfWeek:', dateUtils.startOfWeek(inputDate));
console.log('endOfWeek:', dateUtils.endOfWeek(inputDate));
console.log('startOfMonth:', dateUtils.startOfMonth(inputDate));
console.log('endOfMonth:', dateUtils.endOfMonth(inputDate));
```
--------------------------------
### Migration from Funky.WIPOverlay v1.x to v2.0 (JavaScript)
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/components/wip-overlay.md
Compares the v1.x singleton API with the recommended v2.0 instance-based API for Funky.WIPOverlay. The v2.0 API requires an `id` for initialization and subsequent operations like `show`, `hide`, `toggle`, `isShown`, and `destroy`, offering better management, especially in SPAs.
```javascript
// v1.x style (still works)
Funky.WIPOverlay.init({ message: 'Test' });
Funky.WIPOverlay.show();
Funky.WIPOverlay.hide();
// v2.0 style (recommended for SPAs)
Funky.WIPOverlay.init({ id: 'my-overlay', message: 'Test' });
Funky.WIPOverlay.show('my-overlay');
Funky.WIPOverlay.hide('my-overlay');
```
--------------------------------
### Get Form Instance by ID or Container
Source: https://github.com/thisusedtobeanemail/funky-frame/blob/main/md/js/core/form.md
Provides examples of how to retrieve an existing Funky.Form instance using its ID (string), container selector (string), or the container element itself. This is useful for interacting with a form after it has been created.
```javascript
var form = Funky.Form.getInstance('my-form-id');
var form = Funky.Form.getInstance('#container');
var form = Funky.Form.getInstance(containerElement);
```