mirror of
https://github.com/tabler/tabler.git
synced 2026-10-12 05:06:55 +04:00
Rewrite autosize as an Autosize component with the plugin bundled (#3045)
This commit is contained in:
1 parent
1ffbeeca42
commit
a13ebc3621
7 files changed
+199
-27
No files matched your search
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@tabler/core": minor
|
||||||
|
"@tabler/docs": patch
|
||||||
|
"@tabler/preview": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Added `Autosize` component to `tabler.js` with the `autosize` plugin bundled, plus `update()` and `dispose()` methods.
|
||||||
+79
-7
@@ -1,11 +1,83 @@
|
|||||||
// js-docs-start autosize-init
|
/**
|
||||||
const autosizeElements: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>('[data-bs-toggle="autosize"]')
|
* --------------------------------------------------------------------------
|
||||||
|
* Tabler autosize.ts
|
||||||
|
* Licensed under MIT (https://github.com/tabler/tabler/blob/dev/LICENSE)
|
||||||
|
* --------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
if (autosizeElements.length) {
|
import autosize from 'autosize'
|
||||||
autosizeElements.forEach(function (element: HTMLElement) {
|
|
||||||
if (window.autosize) {
|
import BaseComponent from './bootstrap/base-component'
|
||||||
window.autosize(element)
|
import SelectorEngine from './bootstrap/dom/selector-engine'
|
||||||
|
import type { ElementSelector } from './bootstrap/types'
|
||||||
|
|
||||||
|
type ComponentConfig = Record<string, never>
|
||||||
|
|
||||||
|
type ComponentConfigInput = Record<string, unknown>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants
|
||||||
|
*/
|
||||||
|
|
||||||
|
const NAME = 'autosize'
|
||||||
|
|
||||||
|
const SELECTOR_DATA_TOGGLE = `[data-bs-toggle="${NAME}"], [data-tblr-toggle="${NAME}"]`
|
||||||
|
|
||||||
|
const Default: ComponentConfig = {}
|
||||||
|
|
||||||
|
const DefaultType: Record<keyof ComponentConfig, string> = {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class definition
|
||||||
|
*
|
||||||
|
* Wraps the `autosize` plugin (https://github.com/jackmoore/autosize), bundled
|
||||||
|
* with `tabler.js`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Autosize extends BaseComponent {
|
||||||
|
declare _element: HTMLTextAreaElement
|
||||||
|
declare _config: ComponentConfig
|
||||||
|
|
||||||
|
constructor(element: ElementSelector, config?: ComponentConfigInput) {
|
||||||
|
super(element, config)
|
||||||
|
|
||||||
|
if (this._element) {
|
||||||
|
autosize(this._element)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
static get Default(): ComponentConfig {
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
static get DefaultType(): Record<keyof ComponentConfig, string> {
|
||||||
|
return DefaultType
|
||||||
|
}
|
||||||
|
|
||||||
|
static get NAME(): string {
|
||||||
|
return NAME
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public
|
||||||
|
update(): void {
|
||||||
|
autosize.update(this._element)
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose(): void {
|
||||||
|
autosize.destroy(this._element)
|
||||||
|
super.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data API implementation
|
||||||
|
*/
|
||||||
|
|
||||||
|
// js-docs-start autosize-init
|
||||||
|
for (const element of SelectorEngine.find(SELECTOR_DATA_TOGGLE)) {
|
||||||
|
Autosize.getOrCreateInstance(element)
|
||||||
}
|
}
|
||||||
// js-docs-end autosize-init
|
// js-docs-end autosize-init
|
||||||
|
|
||||||
|
export default Autosize
|
||||||
Vendored
+14
-1
@@ -1,5 +1,18 @@
|
|||||||
|
// The `autosize` package ships no type definitions
|
||||||
|
declare module 'autosize' {
|
||||||
|
type AutosizeTarget = Element | ArrayLike<Element>
|
||||||
|
|
||||||
|
interface Autosize {
|
||||||
|
(target: AutosizeTarget): AutosizeTarget
|
||||||
|
update(target: AutosizeTarget): AutosizeTarget
|
||||||
|
destroy(target: AutosizeTarget): AutosizeTarget
|
||||||
|
}
|
||||||
|
|
||||||
|
const autosize: Autosize
|
||||||
|
export default autosize
|
||||||
|
}
|
||||||
|
|
||||||
interface Window {
|
interface Window {
|
||||||
autosize?: (element: HTMLElement | HTMLTextAreaElement) => void
|
|
||||||
countUp?: {
|
countUp?: {
|
||||||
CountUp: new (
|
CountUp: new (
|
||||||
target: HTMLElement,
|
target: HTMLElement,
|
||||||
|
|||||||
@@ -12,3 +12,6 @@ import './src/sortable'
|
|||||||
|
|
||||||
// Re-export everything from bootstrap.ts (single source of truth)
|
// Re-export everything from bootstrap.ts (single source of truth)
|
||||||
export * from './src/bootstrap'
|
export * from './src/bootstrap'
|
||||||
|
|
||||||
|
// Tabler's own components
|
||||||
|
export { default as Autosize } from './src/autosize'
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import { describe, it, expect, beforeAll, beforeEach, afterEach, vi } from 'vitest'
|
||||||
|
import autosize from 'autosize'
|
||||||
|
import { clearFixture, getFixture } from '../helpers/fixture'
|
||||||
|
import Autosize from '../../src/autosize'
|
||||||
|
|
||||||
|
vi.mock('autosize', () => ({
|
||||||
|
default: Object.assign(vi.fn(), { update: vi.fn(), destroy: vi.fn() }),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const plugin = vi.mocked(autosize)
|
||||||
|
|
||||||
|
describe('Autosize', () => {
|
||||||
|
let fixtureEl: HTMLElement
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
fixtureEl = getFixture()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks()
|
||||||
|
fixtureEl.innerHTML = '<textarea data-bs-toggle="autosize"></textarea>'
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
clearFixture()
|
||||||
|
})
|
||||||
|
|
||||||
|
const textarea = (): HTMLTextAreaElement => fixtureEl.querySelector('textarea')!
|
||||||
|
|
||||||
|
describe('NAME', () => {
|
||||||
|
it('should return plugin name', () => {
|
||||||
|
expect(Autosize.NAME).toBe('autosize')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('constructor', () => {
|
||||||
|
it('should call the autosize plugin with the element', () => {
|
||||||
|
const instance = new Autosize(textarea())
|
||||||
|
|
||||||
|
expect(plugin).toHaveBeenCalledWith(textarea())
|
||||||
|
expect(Autosize.getInstance(textarea())).toBe(instance)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('update', () => {
|
||||||
|
it('should call autosize.update with the element', () => {
|
||||||
|
const instance = new Autosize(textarea())
|
||||||
|
instance.update()
|
||||||
|
|
||||||
|
expect(plugin.update).toHaveBeenCalledWith(textarea())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('dispose', () => {
|
||||||
|
it('should call autosize.destroy and remove the instance', () => {
|
||||||
|
const instance = new Autosize(textarea())
|
||||||
|
const element = textarea()
|
||||||
|
instance.dispose()
|
||||||
|
|
||||||
|
expect(plugin.destroy).toHaveBeenCalledWith(element)
|
||||||
|
expect(Autosize.getInstance(element)).toBeNull()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getOrCreateInstance', () => {
|
||||||
|
it('should reuse the instance and not initialise the plugin twice', () => {
|
||||||
|
const first = Autosize.getOrCreateInstance(textarea())
|
||||||
|
const second = Autosize.getOrCreateInstance(textarea())
|
||||||
|
|
||||||
|
expect(second).toBe(first)
|
||||||
|
expect(plugin).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,15 +1,11 @@
|
|||||||
---
|
---
|
||||||
title: Autosize
|
title: Autosize
|
||||||
summary: The autosize element will automatically adjust the textarea height and make it easier for users to follow as they type.
|
summary: The autosize element will automatically adjust the textarea height and make it easier for users to follow as they type.
|
||||||
docs-libs: [autosize]
|
|
||||||
description: Make textareas grow automatically as users type with the autosize plugin, so longer text stays visible without manual resizing.
|
description: Make textareas grow automatically as users type with the autosize plugin, so longer text stays visible without manual resizing.
|
||||||
related: [/ui/forms/elements]
|
related: [/ui/forms/elements]
|
||||||
---
|
---
|
||||||
import Example from '@components/Example.astro';
|
import Example from '@components/Example.astro';
|
||||||
import CodeDocs from '@components/CodeDocs.astro';
|
import CodeDocs from '@components/CodeDocs.astro';
|
||||||
import TabsPackage from '@components/TabsPackage.astro';
|
|
||||||
import { Code } from 'astro:components';
|
|
||||||
import { site } from '@shared/lib/site.ts';
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -21,15 +17,7 @@ A textarea keeps its height no matter how much text it holds, so long answers en
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Install autosize with npm:
|
The plugin is bundled with `tabler.js`, so there is nothing extra to install or load. It only changes the height of the field, so it needs no CSS of its own.
|
||||||
|
|
||||||
<TabsPackage name="autosize" />
|
|
||||||
|
|
||||||
Or include it from a CDN:
|
|
||||||
|
|
||||||
<Code lang="html" code={`<script src="${site.cdnUrl}/dist/libs/autosize/dist/autosize.min.js"></script>`} />
|
|
||||||
|
|
||||||
The plugin only changes the height of the field, so it needs no CSS of its own.
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -43,22 +31,37 @@ Set `rows` to choose the starting height. With `rows="1"` the field starts as a
|
|||||||
|
|
||||||
## JavaScript
|
## JavaScript
|
||||||
|
|
||||||
Tabler initializes every element with `data-bs-toggle="autosize"` on page load. This is the code that runs:
|
Tabler wraps the plugin in an `Autosize` component that works like the Bootstrap components: it is created once per element and stored on it. On page load Tabler creates one for every element with `data-bs-toggle="autosize"`. This is the code that runs:
|
||||||
|
|
||||||
<CodeDocs name="autosize-init" file="core/js/src/autosize.ts" />
|
<CodeDocs name="autosize-init" file="core/js/src/autosize.ts" />
|
||||||
|
|
||||||
Call the plugin yourself when you add a textarea later, for example inside a modal or a row added by JavaScript:
|
Create the component yourself when you add a textarea later, for example inside a modal or a row added by JavaScript:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
autosize(document.getElementById('message'));
|
const autosize = new tabler.Autosize(document.getElementById('message'));
|
||||||
```
|
```
|
||||||
|
|
||||||
Autosize measures the field, so a hidden textarea gets the wrong height. Run `autosize.update()` once it becomes visible:
|
`tabler.Autosize.getOrCreateInstance(element)` returns the existing component or creates a new one, so it is safe to call more than once for the same field.
|
||||||
|
|
||||||
|
Autosize measures the field, so a hidden textarea gets the wrong height. Call `update()` once it becomes visible:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
autosize.update(document.getElementById('message'));
|
tabler.Autosize.getOrCreateInstance(document.getElementById('message')).update();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Call `dispose()` to stop the plugin and remove the component from the element:
|
||||||
|
|
||||||
|
```js
|
||||||
|
tabler.Autosize.getInstance(document.getElementById('message'))?.dispose();
|
||||||
|
```
|
||||||
|
|
||||||
|
| Method | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `update()` | Recalculates the height, for example after the textarea becomes visible or its value changes from code. |
|
||||||
|
| `dispose()` | Stops the plugin and removes the component from the element. |
|
||||||
|
| `getInstance(element)` | Static. Returns the component for the element, or `null`. |
|
||||||
|
| `getOrCreateInstance(element)` | Static. Returns the component for the element and creates it when needed. |
|
||||||
|
|
||||||
## Accessibility
|
## Accessibility
|
||||||
|
|
||||||
- Keep the `label` for the field. A growing textarea is still a normal form control and needs its name.
|
- Keep the `label` for the field. A growing textarea is still a normal form control and needs its name.
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ const rows = [
|
|||||||
]
|
]
|
||||||
---
|
---
|
||||||
|
|
||||||
<DefaultLayout title="Form elements" pageMenu="form.elements" pageLibs={['nouislider', 'autosize', 'tabler-flags', 'tabler-payments', 'litepicker', 'tom-select', 'imask']}>
|
<DefaultLayout title="Form elements" pageMenu="form.elements" pageLibs={['nouislider', 'tabler-flags', 'tabler-payments', 'litepicker', 'tom-select', 'imask']}>
|
||||||
<DocsLink slot="page-header-actions" path="/ui/forms/elements" />
|
<DocsLink slot="page-header-actions" path="/ui/forms/elements" />
|
||||||
<div class="row row-cards">
|
<div class="row row-cards">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
|
|||||||
Reference in new issue
Block a user