UNPKG

preline

Version:

Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.

302 lines (219 loc) 10.1 kB
# Copy Markup Copy input, select or other markups. [![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/copy-markup) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/copy-markup.html) ## Contents - [Overview](#overview) - [Installation](#installation) - [Basic usage](#basic-usage) - [Configuration Options](#configuration-options) - [JavaScript API](#javascript-api) - [Events](#events) - [Common Patterns](#common-patterns) - [License](#license) ## Overview The Copy Markup component allows you to dynamically duplicate HTML elements (inputs, selects, or other markup) with a single click. It's useful for creating dynamic forms where users can add multiple instances of the same field. **Key Features:** - One-click element duplication - Configurable copy limits - Automatic element management - Programmatic control via JavaScript API - Event system for copy/delete tracking - Support for any HTML markup ## Installation To get started, install Copy Markup plugin via npm, else you can skip this step if you are already using Preline UI as a package. ```bash npm i @preline/copy-markup ``` ### CSS [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS file into your Tailwind CSS file. ```css @import "tailwindcss"; /* @preline/copy-markup */ @import "./node_modules/@preline/copy-markup/theme.css"; ``` ### JavaScript Include the JavaScript that powers the interactive elements near the end of your `</body>` tag: ```html <script src="./node_modules/@preline/copy-markup/index.js"></script> ``` ### Manual Initialization Use the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly. ```html <script type="module"> import HSCopyMarkup from "@preline/copy-markup/non-auto.mjs"; new HSCopyMarkup(document.querySelector("#copy-markup")); </script> ``` ### Via Bundler When using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `@preline/copy-markup` is the auto-init entry: it scans the DOM and initializes matching elements automatically. ```js import "@preline/copy-markup"; ``` `@preline/copy-markup/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself. ```js import HSCopyMarkup from "@preline/copy-markup/non-auto"; HSCopyMarkup.autoInit(); // Or initialize a specific element manually const el = document.querySelector("#copy-markup"); if (el) new HSCopyMarkup(el); ``` ### TypeScript This package ships with TypeScript type definitions. No additional `@types/` package is needed. ## Basic usage The following example demonstrates the minimal HTML structure required for a copy markup component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. Clicking the button will duplicate the target element. ```html <div id="hs-wrapper-for-copy-first"> <input id="hs-content-for-copy-first" type="text" class="py-3 px-4 block w-full border-gray-200 rounded-lg text-sm text-gray-800 focus:z-10 dark:bg-neutral-800 dark:border-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-500" placeholder="Enter Name"> </div> <button type="button" data-hs-copy-markup='{ "targetSelector": "#hs-content-for-copy-first", "wrapperSelector": "#hs-wrapper-for-copy-first", "limit": 3 }' id="hs-copy-content-first"> Add Name </button> ``` **Structure Requirements:** - `data-hs-copy-markup`: Required on the trigger button, contains configuration options as JSON - `targetSelector`: Required, must be a valid CSS selector pointing to the element to copy - `wrapperSelector`: Required, must be a valid CSS selector pointing to the container where copies will be added - Target element must exist in the DOM **Initial State:** - One instance of the target element exists - Copy button is enabled (unless limit is reached) ## Configuration Options ### Data Options Data options are specified in the `data-hs-copy-markup` attribute as a JSON object. | Option | Target Element | Type | Default | Description | | --- | --- | --- | --- | --- | | `data-hs-copy-markup` | Trigger button | - | - | Activate a Copy Markup by specifying on an element. Should be added to the button (trigger). | | `:targetSelector` | Inside `data-hs-copy-markup` | string (CSS selector) | - | Specifies the target element to copy. The value must be a valid CSS selector. Required. | | `:wrapperSelector` | Inside `data-hs-copy-markup` | string (CSS selector) | - | Specifies which element should be used for copying. The value must be a valid CSS selector. Required. | | `:limit` | Inside `data-hs-copy-markup` | number | `null` | Specifies how many items you can copy until the button is disabled. `null` means no limit. | **Example:** ```html <button data-hs-copy-markup='{ "targetSelector": "#hs-input-field", "wrapperSelector": "#hs-wrapper", "limit": 5 }'> Add Field </button> ``` ## JavaScript API The `HSCopyMarkup` object is available in the global `window` object after the plugin is loaded. ### Instance Methods These methods are called on a copy markup instance. | Method | Parameters | Return Type | Description | | --- | --- | --- | --- | | `delete(target)` | `target`: `HTMLElement` | `void` | Remove the element associated to the target. The target should be a Node (HTMLElement). Use this to programmatically remove copied elements. | | `destroy()` | None | `void` | Destroys the copy markup instance, removes all generated markup, classes, and event listeners. Use when removing copy markup from DOM. | ### Static Methods These methods are called directly on the `HSCopyMarkup` class. | Method | Parameters | Return Type | Description | | --- | --- | --- | --- | | `HSCopyMarkup.getInstance(target, isInstance)` | `target`: `HTMLElement \| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSCopyMarkup \| { id: string \| number, element: HSCopyMarkup } \| null` | Returns the copy markup instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSCopyMarkup` instance. If `isInstance` is `false` or omitted, returns the `HSCopyMarkup` instance directly. Returns `null` if copy markup instance is not found. | ### Usage Examples **Example 1: Deleting copied elements** ```javascript // Get the copy markup instance const instance = HSCopyMarkup.getInstance('#hs-copy-content', true); if (instance) { const { element } = instance; const deleteBtn = document.querySelector('#hs-delete-btn'); deleteBtn.addEventListener('click', () => { // Delete a specific copied element const copiedElement = document.querySelector('#hs-copy-markup-item-first'); if (copiedElement) { element.delete(copiedElement); } }); } ``` **Example 2: Using instance methods (recommended pattern)** ```javascript // Get the copy markup instance const instance = HSCopyMarkup.getInstance('#hs-copy-content', true); if (instance) { const { element } = instance; // Access instance properties console.log('Items:', element.items); console.log('Limit:', element.limit); // Delete a copied element programmatically function removeCopiedItem(itemElement) { element.delete(itemElement); } // Clean up when removing from DOM function removeCopyMarkup() { element.destroy(); } } ``` **Example 3: Destroying copy markup instance** ```javascript const instance = HSCopyMarkup.getInstance('#hs-copy-content', true); if (instance) { const { element } = instance; const removeBtn = document.querySelector('#hs-remove-btn'); removeBtn.addEventListener('click', () => { // Clean up before removing from DOM element.destroy(); // Now safe to remove the element document.querySelector('#hs-copy-content').remove(); }); } ``` ## Events Copy markup instances emit events that can be listened to for copy/delete tracking and custom behavior. | Event Name | When Fired | Callback Parameter | Description | | --- | --- | --- | --- | | `on:copy` | When target element was copied | `HTMLElement` (copied element) | Fires when a new copy of the target element is created. Returns the newly created element. | | `on:delete` | When target element was deleted | `HTMLElement` (deleted element) | Fires when a copied element is deleted. Returns the deleted element. | ### Event Usage Example ```javascript // Get copy markup instance const instance = HSCopyMarkup.getInstance('#hs-copy-content', true); if (instance) { const { element } = instance; // Listen to copy event element.on('copy', (copiedElement) => { console.log('Element copied:', copiedElement); // Perform actions after element is copied // e.g., initialize other plugins on the new element, update counters }); // Listen to delete event element.on('delete', (deletedElement) => { console.log('Element deleted:', deletedElement); // Perform cleanup or state updates }); } ``` ## Common Patterns ### Pattern 1: Limited Copies Limit the number of copies that can be created. ```html <button data-hs-copy-markup='{ "targetSelector": "#hs-field", "wrapperSelector": "#hs-wrapper", "limit": 5 }'> Add Field (Max 5) </button> ``` ### Pattern 2: Dynamic Form Fields Create dynamic form fields with copy functionality. ```html <div id="hs-fields-wrapper"> <input id="hs-field-template" type="text" placeholder="Field name"> </div> <button data-hs-copy-markup='{ "targetSelector": "#hs-field-template", "wrapperSelector": "#hs-fields-wrapper" }'> Add Another Field </button> ``` ## License Copyright (c) 2026 Preline Labs. Licensed under the [MIT License](https://opensource.org/licenses/MIT).