mirror of
https://github.com/usetrmnl/trmnl-picker.git
synced 2026-04-29 13:44:02 -07:00
Generate /doc using documentation.js; drop redundant docs from readme
This commit is contained in:
@@ -2,11 +2,11 @@
|
||||
|
||||
A lightweight JavaScript library for managing TRMNL device model and palette selection.
|
||||
|
||||
This was extracted from our Core web app for [BYOS](https://docs.usetrmnl.com/go/diy/byos) (Bring Your Own Server) and other applications to take advantage of.
|
||||
This was extracted from our Core web app so that [BYOS](https://docs.usetrmnl.com/go/diy/byos) (Bring Your Own Server) and other applications can take advantage of it.
|
||||
|
||||
## Live Demo
|
||||
|
||||
Try the interactive demo: [https://usetrmnl.github.io/trmnl-picker/example/](https://usetrmnl.github.io/trmnl-picker/example/)
|
||||
Try the interactive demo at [https://usetrmnl.github.io/trmnl-picker/example/](https://usetrmnl.github.io/trmnl-picker/example/)
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -56,33 +56,11 @@ The library expects a form with specific data-* attributes. Apply your favorite
|
||||
|
||||
### 2. Initialize Picker
|
||||
|
||||
You have two options: let the library fetch from the TRMNL API automatically, or provide your own data.
|
||||
|
||||
#### Option A: Automatic API Fetching (Recommended)
|
||||
|
||||
The library can automatically fetch models and palettes from the TRMNL API:
|
||||
|
||||
```javascript
|
||||
const picker = await TRMNLPicker.create('picker-form')
|
||||
```
|
||||
|
||||
Data is fetched from these API endpoints:
|
||||
|
||||
- `https://usetrmnl.com/api/models`
|
||||
- `https://usetrmnl.com/api/palettes`
|
||||
|
||||
See https://usetrmnl.com/api-docs/ for complete API documentation.
|
||||
|
||||
#### Option B: Provide Your Own Data
|
||||
|
||||
If you already have the data, or need to customize it, you can pass `models` and/or `palettes` as options to the constructor:
|
||||
|
||||
```javascript
|
||||
const models = [...]
|
||||
const palettes = [...]
|
||||
|
||||
const picker = await TRMNLPicker.create('picker-form', { models, palettes })
|
||||
```
|
||||
Data is automatically fetched from the TRMNL API.
|
||||
|
||||
#### With localStorage Persistence
|
||||
|
||||
@@ -95,11 +73,6 @@ const picker = await TRMNLPicker.create('picker-form', {
|
||||
})
|
||||
```
|
||||
|
||||
The picker will automatically:
|
||||
- Load the last selected preferences from localStorage on initialization
|
||||
- Save any changes to localStorage whenever the user makes a selection
|
||||
- Fall back to defaults if no saved state exists
|
||||
|
||||
### 3. Listen for Changes
|
||||
|
||||
```javascript
|
||||
@@ -115,175 +88,8 @@ document.getElementById('picker-form').addEventListener('trmnl:change', (event)
|
||||
|
||||
## API Reference
|
||||
|
||||
### Static Constructor Method
|
||||
See the [API documentation](https://usetrmnl.github.io/trmnl-picker/doc/) for complete information.
|
||||
|
||||
#### `TRMNLPicker.create(formIdOrElement, options)`
|
||||
|
||||
```javascript
|
||||
// Fetch both models and palettes from TRMNL API
|
||||
const picker = await TRMNLPicker.create('picker-form')
|
||||
|
||||
// Or pass a DOM element directly
|
||||
const formElement = document.getElementById('picker-form')
|
||||
const picker = await TRMNLPicker.create(formElement)
|
||||
|
||||
// Or provide custom data
|
||||
const picker = await TRMNLPicker.create('picker-form', { models, palettes })
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `formIdOrElement` (string | Element, required): Form element ID or DOM element itself
|
||||
- `options` (object, optional): Configuration options
|
||||
- `options.models` (array, optional): Array of model objects. If not provided, fetches from `https://usetrmnl.com/api/models`
|
||||
- See the [TRMNL API docs](https://usetrmnl.com/api-docs/index.html) for model object schema
|
||||
- `options.palettes` (array, optional): Array of palette objects. If not provided, fetches from `https://usetrmnl.com/api/palettes`
|
||||
- See the [TRMNL API docs](https://usetrmnl.com/api-docs/index.html) for palette object schema
|
||||
- `options.localStorageKey` (string, optional): localStorage key for persisting picker state across page reloads
|
||||
|
||||
**Returns:** `Promise<TRMNLPicker>` - Promise that resolves to the picker instance
|
||||
|
||||
**Throws:** Error if API fetch fails or data is invalid
|
||||
|
||||
### Methods and Properties
|
||||
|
||||
#### `setParams(params)`
|
||||
|
||||
Programmatically update picker state.
|
||||
|
||||
```javascript
|
||||
picker.setParams({
|
||||
modelName: 'og_plus', // Model name to select
|
||||
paletteId: '123', // Palette ID to select
|
||||
isPortrait: true, // Set portrait orientation
|
||||
isDarkMode: false // Set dark mode
|
||||
})
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `params.modelName` (string, optional): Model name to select
|
||||
- `params.paletteId` (string, optional): Palette ID to select
|
||||
- `params.isPortrait` (boolean, optional): Set portrait orientation
|
||||
- `params.isDarkMode` (boolean, optional): Enable/disable dark mode
|
||||
|
||||
**Note:** Changing the model automatically resets the palette to the first valid palette for that model.
|
||||
|
||||
#### `state` (getter)
|
||||
|
||||
Get current picker state with full model and palette objects.
|
||||
|
||||
```javascript
|
||||
const { model, palette, isPortrait, isDarkMode } = picker.state
|
||||
```
|
||||
|
||||
**Returns:**
|
||||
```javascript
|
||||
{
|
||||
model: { ... },
|
||||
palette: { ... },
|
||||
isPortrait: false,
|
||||
isDarkMode: false
|
||||
}
|
||||
```
|
||||
|
||||
#### `params` (getter)
|
||||
|
||||
Get serializable parameters (useful for persistence or API calls).
|
||||
|
||||
```javascript
|
||||
const params = picker.params
|
||||
// { modelName: 'og_plus', paletteId: '123', isPortrait: false, isDarkMode: false }
|
||||
|
||||
// Save to localStorage
|
||||
localStorage.setItem('picker', JSON.stringify(picker.params))
|
||||
```
|
||||
|
||||
**Returns:** Object with `modelName`, `paletteId`, `isPortrait`, and `isDarkMode`
|
||||
|
||||
#### `screenClasses` (getter)
|
||||
|
||||
Get CSS classes for the current configuration.
|
||||
|
||||
```javascript
|
||||
const classes = picker.screenClasses
|
||||
// ['screen', 'screen--1bit', 'screen--v2', 'screen--md', 'screen--1x']
|
||||
```
|
||||
|
||||
**Returns:** `Array<string>` - Array of CSS class names in priority order
|
||||
|
||||
#### `destroy()`
|
||||
|
||||
Clean up event listeners and references. Call this when removing the picker.
|
||||
|
||||
```javascript
|
||||
picker.destroy()
|
||||
```
|
||||
|
||||
### Events
|
||||
|
||||
#### `trmnl:change` Event
|
||||
|
||||
Emitted when:
|
||||
- User changes any selection (model, palette, orientation, or dark mode)
|
||||
- `setParams()` is called programmatically
|
||||
- **On initialization** - allows you to get the initial state immediately
|
||||
|
||||
```javascript
|
||||
document.getElementById('picker-form').addEventListener('trmnl:change', (event) => {
|
||||
const { origin, screenClasses, model, palette, isPortrait, isDarkMode } = event.detail
|
||||
// Handle the change
|
||||
})
|
||||
```
|
||||
|
||||
**Important:** The event is emitted immediately after initialization, so you can apply initial screen classes without waiting for user interaction.
|
||||
|
||||
**Event Detail Structure:**
|
||||
```javascript
|
||||
{
|
||||
origin: 'constructor' | 'form' | 'setParams', // What triggered the change
|
||||
screenClasses: ['screen', 'screen--1bit', 'screen--v2', 'screen--md', 'screen--1x'],
|
||||
model: { name, label, width, height, kind, css: {...} },
|
||||
palette: { id, name, framework_class },
|
||||
isPortrait: false,
|
||||
isDarkMode: false
|
||||
}
|
||||
```
|
||||
|
||||
**Origin Values:**
|
||||
- `'constructor'` - Initial state or loaded from localStorage
|
||||
- `'form'` - User interaction with form controls
|
||||
- `'setParams'` - Programmatic update via `setParams()` method
|
||||
|
||||
## Form Elements
|
||||
|
||||
The library expects the following elements within the form using data-* attributes:
|
||||
|
||||
### Required Elements
|
||||
- `[data-model-select]` - Model dropdown (typically a `<select>` element)
|
||||
- `[data-palette-select]` - Palette dropdown (typically a `<select>` element)
|
||||
|
||||
### Optional Elements
|
||||
- `[data-orientation-toggle]` - Button to toggle portrait/landscape
|
||||
- `[data-dark-mode-toggle]` - Button to toggle dark mode
|
||||
- `[data-reset-button]` - Button to reset to defaults (first palette, landscape orientation, light mode)
|
||||
- `[data-orientation-text]` - Text element showing current orientation
|
||||
- `[data-dark-mode-text]` - Text element showing current mode
|
||||
|
||||
## Examples
|
||||
|
||||
See [example/index.html](example/index.html) for a complete working example with styling.
|
||||
|
||||
### Applying Classes to Screen Elements
|
||||
|
||||
```javascript
|
||||
document.getElementById('picker-form').addEventListener('trmnl:change', (event) => {
|
||||
const { screenClasses } = event.detail
|
||||
|
||||
// Apply to all elements with class 'screen'
|
||||
document.querySelectorAll('.screen').forEach(screen => {
|
||||
screen.className = screenClasses.join(' ')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
Generated
+3860
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -12,10 +12,11 @@
|
||||
"LICENSE"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "npm run build:esm && npm run build:browser && npm run build:browser:min",
|
||||
"build": "npm run build:esm && npm run build:browser && npm run build:browser:min && npm run build:docs",
|
||||
"build:esm": "esbuild src/index.js --bundle --format=esm --outfile=dist/trmnl-picker.esm.js --sourcemap",
|
||||
"build:browser": "esbuild src/index.js --bundle --format=iife --global-name=TRMNLPicker --outfile=dist/trmnl-picker.js --sourcemap --footer:js=\"TRMNLPicker=TRMNLPicker.default;\"",
|
||||
"build:browser:min": "esbuild src/index.js --bundle --format=iife --global-name=TRMNLPicker --minify --outfile=dist/trmnl-picker.min.js --sourcemap --footer:js=\"TRMNLPicker=TRMNLPicker.default;\"",
|
||||
"build:docs": "documentation build src/index.js -f html -o doc --project-name '@trmnl/picker' --project-description 'JavaScript library for TRMNL device model and palette selection' --shallow && node scripts/expand-docs-nav.js",
|
||||
"watch": "npm run build:browser -- --watch",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
@@ -30,6 +31,7 @@
|
||||
"author": "TRMNL",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"documentation": "^14.0.3",
|
||||
"esbuild": "^0.20.0"
|
||||
},
|
||||
"repository": {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Automatically expand all accordion elements in documentation navigation
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const docsIndexPath = path.join(__dirname, '../doc/index.html');
|
||||
|
||||
if (!fs.existsSync(docsIndexPath)) {
|
||||
console.error('Error: doc/index.html not found');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let html = fs.readFileSync(docsIndexPath, 'utf8');
|
||||
|
||||
// Remove 'display-none' from all toggle-target elements (handles both single and double quotes, and different class orderings)
|
||||
html = html.replace(/class=['"]([^'"]*\s)?display-none\s+toggle-target([^'"]*)['"]/g, "class='$1toggle-target$2'");
|
||||
html = html.replace(/class=['"]([^'"]*\s)?toggle-target\s+display-none([^'"]*)['"]/g, "class='$1toggle-target$2'");
|
||||
|
||||
// Change the toggle icon from ▸ (collapsed) to ▾ (expanded)
|
||||
html = html.replace(/<span class='icon'>▸<\/span>/g, "<span class='icon'>▾</span>");
|
||||
|
||||
fs.writeFileSync(docsIndexPath, html);
|
||||
|
||||
console.log('✓ Expanded all navigation accordions in documentation');
|
||||
+62
-23
@@ -1,8 +1,29 @@
|
||||
/**
|
||||
* Default model to select when no localStorage state is found
|
||||
* @private
|
||||
* @constant {string}
|
||||
*/
|
||||
const DEFAULT_MODEL_NAME = 'og_plus'
|
||||
const _DEFAULT_MODEL_NAME = 'og_plus'
|
||||
|
||||
/**
|
||||
* Event fired when picker state changes
|
||||
* @event TRMNLPicker#trmnl:change
|
||||
* @type {CustomEvent}
|
||||
* @property {Object} detail - Event details
|
||||
* @property {string} detail.origin - What triggered the change: 'constructor', 'form', or 'setParams'
|
||||
* @property {Array<string>} detail.screenClasses - Array of CSS classes for Framework CSS rendering
|
||||
* @property {Object} detail.model - Current model object with name, label, width, height, kind, css properties
|
||||
* @property {Object} detail.palette - Current palette object with id, name, framework_class properties
|
||||
* @property {boolean} detail.isPortrait - Portrait orientation flag
|
||||
* @property {boolean} detail.isDarkMode - Dark mode flag
|
||||
*
|
||||
* @example
|
||||
* picker.formElement.addEventListener('trmnl:change', (event) => {
|
||||
* const { origin, screenClasses, model, palette } = event.detail
|
||||
* console.log(`Changed via ${origin}`)
|
||||
* console.log('Classes:', screenClasses)
|
||||
* })
|
||||
*/
|
||||
|
||||
/**
|
||||
* TRMNLPicker - Vanilla JS library for TRMNL device and palette selection
|
||||
@@ -10,6 +31,12 @@ const DEFAULT_MODEL_NAME = 'og_plus'
|
||||
* Provides a reactive picker component that manages device models, color palettes,
|
||||
* orientation, and display mode. Emits 'trmnl:change' events with current state
|
||||
* and CSS classes for rendering.
|
||||
*
|
||||
* **[View a live demo →](https://usetrmnl.github.io/trmnl-picker/example/)**
|
||||
*
|
||||
* **Note:** Using the constructor directly is not recommended. Use the static
|
||||
* {@link TRMNLPicker.create} method instead, which automatically fetches
|
||||
* models and palettes from the TRMNL API if not provided.
|
||||
*
|
||||
* @class TRMNLPicker
|
||||
* @param {string|Element} formIdOrElement - Form element ID or DOM element reference
|
||||
@@ -18,21 +45,40 @@ const DEFAULT_MODEL_NAME = 'og_plus'
|
||||
* @param {Array<Object>} options.palettes - Array of palette objects from TRMNL API
|
||||
* @param {string} [options.localStorageKey] - Optional key for persisting state to localStorage
|
||||
*
|
||||
* @fires trmnl:change - Emitted when picker state changes, includes origin, state, and screenClasses
|
||||
* @fires TRMNLPicker#trmnl:change
|
||||
*
|
||||
* @example
|
||||
* // HTML Structure - Required form with data-* attributes
|
||||
* // <form id="picker-form">
|
||||
* // <!-- Required: Model selector -->
|
||||
* // <select data-model-select></select>
|
||||
* //
|
||||
* // <!-- Required: Palette selector -->
|
||||
* // <select data-palette-select></select>
|
||||
* //
|
||||
* // <!-- Optional: Orientation toggle -->
|
||||
* // <button type="button" data-orientation-toggle>
|
||||
* // <span data-orientation-text>Landscape</span>
|
||||
* // </button>
|
||||
* //
|
||||
* // <!-- Optional: Dark mode toggle -->
|
||||
* // <button type="button" data-dark-mode-toggle>
|
||||
* // <span data-dark-mode-text>Light Mode</span>
|
||||
* // </button>
|
||||
* //
|
||||
* // <!-- Optional: Reset button -->
|
||||
* // <button type="button" data-reset-button>Reset</button>
|
||||
* // </form>
|
||||
*
|
||||
* // Create with element ID
|
||||
* const picker = new TRMNLPicker('screen-picker', { models, palettes })
|
||||
* const picker = new TRMNLPicker('picker-form', { models, palettes })
|
||||
*
|
||||
* // Create with DOM element
|
||||
* const element = document.getElementById('picker')
|
||||
* const element = document.getElementById('picker-form')
|
||||
* const picker = new TRMNLPicker(element, { models, palettes })
|
||||
*
|
||||
* // Listen for changes
|
||||
* picker.formElement.addEventListener('trmnl:change', (event) => {
|
||||
* console.log(event.detail.origin) // 'form', 'setParams', 'constructor'
|
||||
* console.log(event.detail.model)
|
||||
* console.log(event.detail.palette)
|
||||
* console.log(event.detail.screenClasses)
|
||||
* })
|
||||
*/
|
||||
@@ -293,7 +339,7 @@ class TRMNLPicker {
|
||||
if (savedParams) {
|
||||
this._setParams('constructor', savedParams)
|
||||
} else {
|
||||
const defaultModel = sortedModels.find(m => m.name === DEFAULT_MODEL_NAME) || sortedModels[0]
|
||||
const defaultModel = sortedModels.find(m => m.name === _DEFAULT_MODEL_NAME) || sortedModels[0]
|
||||
const defaultPaletteId = this._getFirstValidPaletteId(defaultModel)
|
||||
|
||||
this._setParams('constructor', {
|
||||
@@ -334,15 +380,7 @@ class TRMNLPicker {
|
||||
* Emit 'trmnl:change' event with current state and screen classes
|
||||
* @private
|
||||
* @param {string} origin - Source of the change ('constructor', 'form', 'setParams')
|
||||
* @fires trmnl:change
|
||||
*
|
||||
* Event detail includes:
|
||||
* - origin: string - What triggered the change
|
||||
* - model: Object - Current model object with name, label, size, etc.
|
||||
* - palette: Object - Current palette object with id, name, framework_class
|
||||
* - isPortrait: boolean - Portrait orientation flag
|
||||
* - isDarkMode: boolean - Dark mode flag
|
||||
* - screenClasses: Array<string> - CSS classes for rendering
|
||||
* @fires TRMNLPicker#trmnl:change
|
||||
*/
|
||||
_emitChangeEvent(origin) {
|
||||
// Save to localStorage if key is configured
|
||||
@@ -568,7 +606,7 @@ class TRMNLPicker {
|
||||
* @param {string} [params.paletteId] - Palette ID to select
|
||||
* @param {boolean} [params.isPortrait] - Portrait orientation
|
||||
* @param {boolean} [params.isDarkMode] - Dark mode enabled
|
||||
* @fires trmnl:change - If any parameter changed successfully
|
||||
* @fires TRMNLPicker#trmnl:change
|
||||
* @throws {Error} If params is not an object
|
||||
*
|
||||
* @example
|
||||
@@ -660,11 +698,12 @@ class TRMNLPicker {
|
||||
/**
|
||||
* Get complete picker state including full model and palette objects
|
||||
* @public
|
||||
* @returns {Object} Current state
|
||||
* @returns {Object} return.model - Full model object from API
|
||||
* @returns {Object} return.palette - Full palette object from API
|
||||
* @returns {boolean} return.isPortrait - Portrait orientation flag
|
||||
* @returns {boolean} return.isDarkMode - Dark mode flag
|
||||
* @returns {{
|
||||
* model: Object,
|
||||
* palette: Object,
|
||||
* isPortrait: boolean,
|
||||
* isDarkMode: boolean
|
||||
* }} State object containing model (full model object from API), palette (full palette object from API), isPortrait flag, and isDarkMode flag
|
||||
*
|
||||
* @example
|
||||
* const state = picker.state
|
||||
|
||||
Reference in New Issue
Block a user