FL Readonly Manager
A lightweight JavaScript/TypeScript utility to manage readonly/disabled states for form controls based on CSS classes. Supports nested containers with granular control, Shadow DOM traversal, and automatic DOM observation.

Table of Contents
Features
Core Features
- šÆ CSS Class-Based Control - Toggle readonly/disabled states using simple CSS classes
- š¦ Nested Container Support - Handle complex nested structures with proper inheritance
- š« Granular Exemptions - Exempt specific elements or sections from readonly behavior
- šļø Automatic Observation - Uses MutationObserver to detect class changes automatically
- š Shadow DOM Support - Traverse into web components and shadow roots
- š TypeScript Ready - Full type definitions included
- š§ Configurable - Customize selectors, debounce timing, and behavior
- šŖ¶ Modular & dependency-free - No runtime dependencies; ~42KB minified with all managers enabled (feature managers are opt-in)
- āæ Accessibility - Properly sets readonly/disabled attributes for screen readers
New in v3.0.0
- šØ Theme Manager - Visual themes for readonly elements (subtle, greyed, striped, disabled, bordered)
- š RBAC Manager - Role-based access control with data attributes
- š§ CRUD Page Guardrail - Reusable CRUD page mode integration for shared views
- ā” Bulk Operations - Set readonly on multiple elements via selector or array
- ā° Scheduler Manager - Time-based readonly scheduling
- š¬ Tooltip Manager - Show reasons why elements are readonly
- ⨠Animation Manager - Animated state transitions
- šÆ Event Manager - Lifecycle hooks (beforeReadonly, afterReadonly)
- š¾ Persistence Manager - Save/restore states to localStorage
- ā
Validation Manager - Form validation that skips readonly fields
New in v3.1.0
- š¾ Auto-Save Detection - Track form changes and prevent data loss with beforeunload warnings
- ā®ļø Undo/Redo Stack - Full history management with keyboard shortcuts (Ctrl+Z/Ctrl+Y)
- š Form Diff Viewer - Visual diff tracking to highlight changes with color-coded indicators
- āØļø Keyboard Shortcuts - Power-user shortcuts for toggling readonly states and navigation
- š Smart Field Copying - One-click copy buttons for readonly fields
- šÆ Conditional Readonly - Dynamic readonly rules based on expressions or functions
- šØ Readonly Presets - Pre-configured profiles for common scenarios (view/edit modes)
- š Smart Dependencies - Automatic field dependencies with cascading readonly states
- ⨠Auto-Format Values - 10+ formatters (date, currency, phone, SSN, credit card, etc.)
- š¦ Field Groups - Manage related fields as cohesive units
Installation
NPM / Yarn
npm install fl-readonly-manager
yarn add fl-readonly-manager
pnpm add fl-readonly-manager
Package: www.npmjs.com/package/fl-readonly-manager
CDN (unpkg)
<script src="https://unpkg.com/fl-readonly-manager@3.1.0/dist/readonly.min.js"></script>
<script src="https://unpkg.com/fl-readonly-manager@3.1.0/dist/readonly.js"></script>
<script src="https://unpkg.com/fl-readonly-manager/dist/readonly.min.js"></script>
CDN (jsDelivr)
<script src="https://cdn.jsdelivr.net/npm/fl-readonly-manager@3.1.0/dist/readonly.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/fl-readonly-manager@3.1.0/dist/readonly.js"></script>
<script src="https://cdn.jsdelivr.net/npm/fl-readonly-manager/dist/readonly.min.js"></script>
ES Module
import { ReadonlyManager } from 'fl-readonly-manager';
CommonJS
const { ReadonlyManager } = require('fl-readonly-manager');
ES Module via CDN
<script type="module">
import { ReadonlyManager } from 'https://unpkg.com/fl-readonly-manager@3.1.0/dist/index.mjs';
ReadonlyManager.init();
</script>
Browser (IIFE)
For direct browser usage without a bundler:
<script src="https://unpkg.com/fl-readonly-manager/dist/readonly.min.js"></script>
<script>
const { ReadonlyManager, FL_Logger } = ReadonlyManagerLib;
document.addEventListener('DOMContentLoaded', () => {
ReadonlyManager.init();
const controller = ReadonlyManager.createCrudController(document, {
pageSelector: '.crud-page',
modeAttribute: 'data-crud-mode',
autoRefresh: true
});
controller.setMode('edit');
});
</script>
Quick Start
1. Add CSS Classes to Your HTML
<div class="canBeReadOnly readonly">
<input type="text" name="field1">
<select name="field2">
<option>Option 1</option>
</select>
<button type="button">Click</button>
</div>
2. Include the Script
<script src="https://unpkg.com/fl-readonly-manager/dist/readonly.min.js"></script>
<script>
const { ReadonlyManager } = ReadonlyManagerLib;
document.addEventListener('DOMContentLoaded', () => {
ReadonlyManager.init();
});
</script>
3. Toggle Readonly Dynamically
document.getElementById('myForm').classList.toggle('readonly');
ReadonlyManager.setReadonly(document.getElementById('myForm'), true);
CSS Classes Reference
.canBeReadOnly | Any element | Marks element as controllable by this system |
.readonly | Any element | Activates readonly/disabled state |
.input-readonly | Any element | Alternative to .readonly (same behavior) |
.ommitReadOnly | Any element | Exempts element and children from readonly |
.fl-readonly-applied | Auto-added | Visual indicator (added automatically when readonly) |
.crud-page | Page container | Guardrail class for shared CRUD views |
.crud-view | Page container | Page is in readonly/view mode |
.crud-edit | Page container | Page is in editable/edit mode |
.crud-create | Page container | Page is in create mode |
.crud-delete | Page container | Page is in delete mode |
CRUD Page Guardrails
Use createCrudPageReadonlyController, useCrudReadonly, and applyCrudReadonlyGuardrails to keep shared CRUD page views consistent across view/edit/create/delete states.
data-crud-mode controls the current page state.
crud-page marks the page container for guardrail policy.
readonly is applied automatically in view/delete/readonly modes.
canBeReadOnly ensures the readonly manager manages the page container.
This makes the readonly manager reusable across all CRUD operation pages that share the same page view.
Usage Examples
Example 1: Basic Container
All inputs inside become readonly/disabled:
<div class="canBeReadOnly readonly">
<input type="text" name="name">
<input type="email" name="email">
<select name="country">
<option>USA</option>
</select>
<button type="submit">Submit</button>
</div>
Example 2: Container with Exempted Section
Use .ommitReadOnly to keep sections editable:
<div class="canBeReadOnly readonly">
<input type="text" name="locked">
<div class="ommitReadOnly">
<input type="text" name="editable">
<button type="button">Action</button>
</div>
<input type="text" name="also-locked">
</div>
Example 3: Individual Control
Apply to a single element:
<input type="text" class="canBeReadOnly readonly" name="single">
Example 4: Exempted Individual Control
Exempt specific controls within a readonly container:
<div class="canBeReadOnly readonly">
<input type="text" name="locked">
<input type="text" name="exempt" class="ommitReadOnly">
<input type="text" name="also-locked">
</div>
Example 5: Nested Containers
Handle complex nested structures:
<div class="canBeReadOnly readonly">
<input type="text" name="outer">
<div class="canBeReadOnly ommitReadOnly">
<input type="text" name="inner1">
<div class="canBeReadOnly readonly">
<input type="text" name="inner2">
</div>
</div>
</div>
Example 6: Dynamic Toggle with JavaScript
<div id="myForm" class="canBeReadOnly">
<input type="text" name="field1">
<input type="text" name="field2">
</div>
<button onclick="toggleReadonly()">Toggle Readonly</button>
<script>
function toggleReadonly() {
const form = document.getElementById('myForm');
form.classList.toggle('readonly');
}
function toggleReadonlyAPI() {
const form = document.getElementById('myForm');
const isReadonly = ReadonlyManager.isReadonly(form);
ReadonlyManager.setReadonly(form, !isReadonly);
}
</script>
Example 7: Complete Form with Mixed Controls
<form class="canBeReadOnly readonly">
<input type="text" name="name">
<input type="email" name="email">
<input type="tel" name="phone">
<input type="url" name="website">
<input type="password" name="password">
<input type="number" name="age">
<input type="date" name="birthdate">
<textarea name="bio"></textarea>
<input type="checkbox" name="agree">
<input type="radio" name="choice" value="1">
<input type="radio" name="choice" value="2">
<input type="file" name="avatar">
<input type="color" name="theme">
<input type="range" name="volume">
<select name="country">
<option>USA</option>
<option>Canada</option>
</select>
<button type="submit">Submit</button>
<button type="reset">Reset</button>
<div contenteditable="true" name="richtext">Edit me</div>
</form>
Example 8: Theme Manager (v3.0.0)
Apply visual themes to readonly elements:
ReadonlyManager.configure({ THEME: { enabled: true } });
ReadonlyManager.setTheme('greyed');
Example 9: RBAC - Role-Based Access Control (v3.0.0)
Control field editability based on user roles:
<input data-editable-roles="admin,editor" value="Admin/Editor only">
<input data-readonly-for-roles="viewer" value="Readonly for viewers">
ReadonlyManager.configure({ RBAC: { enabled: true } });
ReadonlyManager.setRole('admin');
Example 10: CRUD Page Guardrail + Shared View Hook
Use the same page view for shared CRUD operations and let the manager enforce page mode state.
<div class="crud-page canBeReadOnly" data-crud-mode="view">
<form>
<input type="text" name="name">
<button type="submit">Save</button>
</form>
</div>
import { ReadonlyManager, createCrudPageReadonlyController } from 'fl-readonly-manager';
const controller = createCrudPageReadonlyController(document, {
pageSelector: '.crud-page',
modeAttribute: 'data-crud-mode',
autoRefresh: true
});
controller.setMode('edit');
controller.setMode('view');
const hook = ReadonlyManager.useCrudReadonly(document, {
pageSelector: '.crud-page',
autoRefresh: true
});
hook.setMode('create');
Example 12: Blazor Page Integration (v3.0.0)
Integrate fl-readonly-manager in a Blazor page using JS interop.
<script src="https://unpkg.com/fl-readonly-manager/dist/readonly.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
ReadonlyManager.init();
const controller = ReadonlyManager.createCrudController(document, {
pageSelector: '.crud-page',
modeAttribute: 'data-crud-mode',
autoRefresh: true
});
window.FlReadonlyCrud = {
setMode: (mode) => controller.setMode(mode),
applyGuardrails: () => ReadonlyManager.applyCrudReadonlyGuardrails(document)
};
});
</script>
@page "/crud"
@inject IJSRuntime JS
<div class="crud-page canBeReadOnly" data-crud-mode="view">
<form>
<input type="text" name="name" placeholder="Name" />
<input type="email" name="email" placeholder="Email" />
<button type="submit">Save</button>
</form>
<button @onclick="() => SetModeAsync('view')">View</button>
<button @onclick="() => SetModeAsync('edit')">Edit</button>
<button @onclick="() => SetModeAsync('create')">Create</button>
<button @onclick="() => SetModeAsync('delete')">Delete</button>
</div>
@code {
private async Task SetModeAsync(string mode)
{
await JS.InvokeVoidAsync("FlReadonlyCrud.setMode", mode);
}
}
Example 12: Bulk Operations (v3.0.0)
Set readonly on multiple elements at once:
await ReadonlyManager.setReadonlyBulk('.form-fields', true);
await ReadonlyManager.setReadonlyBulk(document.querySelectorAll('input'), false);
await ReadonlyManager.setReadonlyBulk('.fields', true, {
onProgress: (current, total) => console.log(`${current}/${total}`)
});
Example 13: Scheduler Manager (v3.0.0)
Time-based readonly scheduling:
const element = document.getElementById('scheduledField');
const taskId = ReadonlyManager.scheduleReadonly(element, {
startTime: new Date('2026-01-15T09:00:00'),
endTime: new Date('2026-01-15T17:00:00')
});
ReadonlyManager.cancelSchedule(taskId);
Example 14: Event Manager (v3.0.0)
Lifecycle hooks for readonly changes:
ReadonlyManager.events.on('afterReadonly', (data) => {
console.log(`${data.element.id} is now ${data.isReadonly ? 'readonly' : 'editable'}`);
});
ReadonlyManager.events.on('beforeReadonly', (data) => {
if (data.element.id === 'protectedField') {
return false;
}
});
Example 15: Tooltip Manager (v3.0.0)
Show reasons why elements are readonly:
ReadonlyManager.configure({ TOOLTIP: { enabled: true } });
const field = document.getElementById('lockedField');
field.setAttribute('data-readonly-reason', 'Locked by admin');
ReadonlyManager.tooltip.attach(field);
Example 16: Animation Manager (v3.0.0)
Animate state transitions:
ReadonlyManager.configure({
ANIMATION: {
enabled: true,
duration: 300,
easing: 'ease-in-out'
}
});
ReadonlyManager.setReadonly(element, true);
Example 17: Persistence Manager (v3.0.0)
Save and restore readonly states:
ReadonlyManager.persistence.save(element);
ReadonlyManager.persistence.load(element);
ReadonlyManager.persistence.clear(element);
Example 18: Validation Manager (v3.0.0)
Form validation that respects readonly fields:
const form = document.getElementById('myForm');
const editableFields = ReadonlyManager.validation.getEditableFields(form);
editableFields.forEach(field => {
if (ReadonlyManager.validation.shouldValidate(field)) {
}
});
Example 17: Auto-Save Detection (v3.1.0)
Track form changes and prevent data loss:
const autoSave = ReadonlyManager.autoSave;
autoSave.configure({
enabled: true,
detectChanges: true,
warnBeforeUnload: true,
onDirtyChange: (form, isDirty) => {
console.log(`Form is ${isDirty ? 'dirty' : 'clean'}`);
}
});
autoSave.initializeForm(form);
if (autoSave.isDirty(form)) {
console.log('Form has unsaved changes');
}
const changedFields = autoSave.getChangedFields(form);
const originalValues = autoSave.getOriginalValues(form);
changedFields.forEach(field => {
const name = field.name || field.id;
console.log(`${name}: "${originalValues[name]}" ā "${field.value}"`);
});
autoSave.resetForm(form);
autoSave.markClean(form);
Example 18: Undo/Redo Stack (v3.1.0)
History management for readonly state changes (not field values):
const history = ReadonlyManager.history;
history.configure({
enabled: true,
maxSize: 50,
shortcuts: { undo: 'Ctrl+Z', redo: 'Ctrl+Y' },
onUndo: (entry) => console.log('Undid:', entry.action),
onRedo: (entry) => console.log('Redid:', entry.action)
});
history.record({
action: 'makeReadonly',
elements: [{ selector: '#myField', id: 'myField' }],
previousState: [false],
newState: [true],
source: 'user'
});
history.undo();
history.redo();
if (history.canUndo()) {
console.log(`Can undo ${history.getHistory().length} actions`);
}
if (history.canRedo()) {
console.log(`Can redo ${history.getRedoStack().length} actions`);
}
history.clear();
Example 19: Form Diff Viewer (v3.1.0)
Visual diff tracking to highlight changes:
const diffTracker = ReadonlyManager.diffTracker;
diffTracker.configure({
enabled: true,
highlightChanges: true,
showDiffIndicator: true
});
diffTracker.takeSnapshot(form);
const diff = diffTracker.getDiff(form);
let totalChanges = 0;
Object.entries(diff.fields).forEach(([name, fieldDiff]) => {
if (fieldDiff.changed) {
totalChanges++;
console.log(`${name}: "${fieldDiff.before}" ā "${fieldDiff.after}"`);
}
});
console.log(`${totalChanges} fields changed`);
diffTracker.showDiff(form);
const diffJson = diffTracker.exportDiff(form);
diffTracker.resetDiff(form);
Example 20: Keyboard Shortcuts (v3.1.0)
Power-user shortcuts for productivity:
ReadonlyManager.configure({ SHORTCUTS: { enabled: true } });
ReadonlyManager.shortcuts.register('Ctrl+E', () => {
document.querySelector('.form-container').classList.toggle('readonly');
}, {
description: 'Toggle edit mode'
});
Example 21: Smart Forms - Conditional Readonly (v3.1.0)
Dynamic readonly rules based on conditions:
ReadonlyManager.configure({ SMART_FORMS: { enabled: true } });
ReadonlyManager.smartForms.addConditionalRule({
selector: '#billingAddress',
condition: (context) => context.sameAsShipping === true,
readonly: true
});
ReadonlyManager.smartForms.updateContext({ sameAsShipping: true });
Example 22: Auto-Format Values (v3.1.0)
Formatters for common data types (requires value storage for round-trip):
const smartForms = ReadonlyManager.smartForms;
smartForms.enable({ autoFormat: true });
const originalValues = new Map();
const phoneField = document.getElementById('phone');
originalValues.set(phoneField, phoneField.value);
smartForms.setFormat(phoneField, { type: 'phone' });
const priceField = document.getElementById('price');
originalValues.set(priceField, priceField.value);
smartForms.setFormat(priceField, {
type: 'currency',
locale: 'en-US'
});
const dateField = document.getElementById('date');
originalValues.set(dateField, dateField.value);
smartForms.setFormat(dateField, {
type: 'date',
locale: 'en-US'
});
function onReadonlyChange(field) {
if (field.readOnly) {
if (!originalValues.has(field)) {
originalValues.set(field, field.value);
}
smartForms.applyFormatToField(field);
} else {
if (originalValues.has(field)) {
field.value = originalValues.get(field);
}
}
}
Example 23: Field Groups (v3.1.0)
Manage related fields as cohesive units:
const smartForms = ReadonlyManager.smartForms;
smartForms.enable({ groups: true });
smartForms.addGroup({
name: 'personal-info',
selector: '[data-group="personal"]'
});
smartForms.setGroupReadonly('personal-info', true);
const groups = smartForms.getGroups();
console.log(`Registered groups: ${groups.map(g => g.name).join(', ')}`);
smartForms.removeGroup('personal-info');
JavaScript API
ReadonlyManager.init()
Initialize the readonly manager. Called automatically on page load.
ReadonlyManager.init();
ReadonlyManager.refresh(rootElement?)
Manually re-process all readonly controls. Useful after dynamic DOM changes.
ReadonlyManager.refresh();
ReadonlyManager.refresh(document.getElementById('myForm'));
ReadonlyManager.setReadonly(element, readonly)
Programmatically set readonly state on an element.
const form = document.getElementById('myForm');
ReadonlyManager.setReadonly(form, true);
ReadonlyManager.setReadonly(form, false);
ReadonlyManager.isReadonly(element)
Check if an element is currently readonly.
const form = document.getElementById('myForm');
if (ReadonlyManager.isReadonly(form)) {
console.log('Form is readonly');
}
ReadonlyManager.destroy()
Disconnect the observer and stop watching for changes.
ReadonlyManager.destroy();
ReadonlyManager.configure(options)
Update configuration options at runtime.
ReadonlyManager.configure({
DEBOUNCE_MS: 50,
DEBUG: true,
TRAVERSE_SHADOW_DOM: true
});
ReadonlyManager.getConfig()
Get current configuration.
const config = ReadonlyManager.getConfig();
console.log(config.DEBOUNCE_MS);
ReadonlyManager.logger
Access the logger instance for debugging.
ReadonlyManager.logger.enable();
ReadonlyManager.logger.setLevel(0);
ReadonlyManager.logger.disable();
ReadonlyManager.features
Check browser feature support.
const features = ReadonlyManager.features;
if (features.shadowDOM) {
console.log('Shadow DOM is supported');
}
if (features.mutationObserver) {
console.log('Auto-updates are available');
}
Configuration Options
EDITABLE_SELECTOR | string | 'input, select, textarea, button, [contenteditable]' | CSS selector for editable controls |
CONTAINER_SELECTOR | string | 'div, section, span, ...' | CSS selector for container elements |
CLASSES.CAN_BE_READONLY | string | 'canBeReadOnly' | Class marking controllable elements |
CLASSES.READONLY | string | 'readonly' | Class activating readonly state |
CLASSES.INPUT_READONLY | string | 'input-readonly' | Alternative readonly class |
CLASSES.OMMIT_READONLY | string | 'ommitReadOnly' | Class for exempted elements |
CLASSES.READONLY_APPLIED | string | 'fl-readonly-applied' | Visual indicator class |
DEBOUNCE_MS | number | 50 | Debounce delay for observer (ms) |
DEBUG | boolean | false | Enable debug logging |
TRAVERSE_SHADOW_DOM | boolean | true | Enable Shadow DOM traversal |
Custom Configuration Example
ReadonlyManager.configure({
DEBOUNCE_MS: 50,
DEBUG: true,
CLASSES: {
CAN_BE_READONLY: 'my-readonly-container',
READONLY: 'is-locked',
OMMIT_READONLY: 'keep-editable'
}
});
Control Behavior Reference
input[type="text"] | readonly="readonly" |
input[type="password"] | readonly="readonly" |
input[type="email"] | readonly="readonly" |
input[type="number"] | readonly="readonly" |
input[type="tel"] | readonly="readonly" |
input[type="url"] | readonly="readonly" |
input[type="search"] | readonly="readonly" |
input[type="date"] | readonly="readonly" |
input[type="time"] | readonly="readonly" |
input[type="datetime-local"] | readonly="readonly" |
input[type="month"] | readonly="readonly" |
input[type="week"] | readonly="readonly" |
textarea | readonly="readonly" |
input[type="checkbox"] | disabled |
input[type="radio"] | disabled |
input[type="file"] | disabled |
input[type="color"] | disabled |
input[type="range"] | disabled |
input[type="submit"] | disabled |
input[type="reset"] | disabled |
input[type="button"] | disabled |
select | disabled |
button | disabled |
[contenteditable] | contenteditable="false" |
Shadow DOM Support
The manager can traverse into Shadow DOM to process elements inside web components.
Enable Shadow DOM (Default)
ReadonlyManager.configure({ TRAVERSE_SHADOW_DOM: true });
Web Component Example
<my-custom-form class="canBeReadOnly readonly">
<input type="text" name="light-input">
</my-custom-form>
<script>
class MyCustomForm extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<div class="canBeReadOnly readonly">
<input type="text" name="shadow-input">
</div>
`;
}
}
customElements.define('my-custom-form', MyCustomForm);
</script>
Both light-input and shadow-input will be processed.
Logger Utility
Built-in logger for debugging with enable/disable control.
const logger = ReadonlyManager.logger;
logger.enable();
logger.setLevel(0);
logger.debug('Detailed info');
logger.info('General info');
logger.warn('Warning');
logger.error('Error');
logger.disable();
Log Level Constants
import { LogLevel } from 'fl-readonly-manager';
ReadonlyManager.logger.setLevel(LogLevel.DEBUG);
ReadonlyManager.logger.setLevel(LogLevel.INFO);
ReadonlyManager.logger.setLevel(LogLevel.WARN);
ReadonlyManager.logger.setLevel(LogLevel.ERROR);
Browser Compatibility
| Chrome | 54+ | Full support |
| Firefox | 63+ | Full support |
| Safari | 10.1+ | Full support |
| Edge | 79+ | Full support (Chromium) |
| Edge Legacy | 15+ | No Shadow DOM |
| IE 11 | ā | Not supported |
Feature Detection
const features = ReadonlyManager.features;
if (!features.mutationObserver) {
console.warn('Auto-updates not available. Call refresh() manually.');
}
if (!features.shadowDOM) {
console.warn('Shadow DOM not supported. Web components will not be traversed.');
}
TypeScript Usage
Full TypeScript support with type definitions included.
Import Types
import {
ReadonlyManager,
ReadonlyManagerConfig,
ILogger,
LogLevel,
FeatureSupport
} from 'fl-readonly-manager';
Type-Safe Configuration
import { ReadonlyManagerConfig } from 'fl-readonly-manager';
const config: Partial<ReadonlyManagerConfig> = {
DEBOUNCE_MS: 50,
DEBUG: true
};
ReadonlyManager.configure(config);
Using the Core Class Directly
import { ReadonlyManagerCore } from 'fl-readonly-manager';
const manager = new ReadonlyManagerCore({
DEBOUNCE_MS: 50,
TRAVERSE_SHADOW_DOM: true
});
manager.init();
manager.handleReadonlyControls(document.body);
Migration from v1
Breaking Changes
- Function renamed:
FL_HandleReadonlyControls() is now ReadonlyManager.refresh()
- Configuration: Settings now use
ReadonlyManager.configure() instead of global variables
- Logger: Console logging now requires enabling
ReadonlyManager.logger.enable()
Migration Steps
FL_HandleReadonlyControls();
ReadonlyManager.refresh();
ReadonlyManager.configure({ DEBOUNCE_MS: 50 });
Backward Compatibility
The legacy function is still available for backward compatibility:
FL_HandleReadonlyControls();
Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature
- Make your changes
- Run tests:
npm test
- Submit a pull request
Development
npm install
npm run dev
npm test
npm run test:coverage
npm run build
npm run lint
Publishing
Publish from the fl-readonly-manager package folder.
1. Prepare the release
npm install
npm version patch
npm version minor
npm version major
npm run lint
npm run typecheck
npm test
npm run build
2. Preview the package contents
npm pack --dry-run
The package publishes the files declared in package.json:
dist
examples
README.md
USE_CASES.md
LICENSE
3. Publish to npm
npm login
npm publish
prepublishOnly runs automatically during publish and executes:
npm run build && npm test
If this is the first public publish for the package, use:
npm publish --access public
License
MIT Ā© FL Team
Support