# Strong Password

Use strong passwords based on required characters.

[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/strong-password) [![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/strong-password.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 Strong Password component provides real-time password strength validation with visual feedback. It checks password requirements such as minimum length, uppercase/lowercase letters, numbers, and special characters, displaying strength indicators and helpful hints.

**Key Features:**
- Real-time password strength checking
- Visual strength indicators (strips)
- Password requirement hints
- Customizable strength rules
- Popover mode for hints
- Programmatic control via JavaScript API
- Event system for strength tracking

## Installation

To get started, install Strong Password plugin via npm, else you can skip this step if you are already using Preline UI as a package.

```bash
npm i @preline/strong-password
```

### CSS

Use [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.

```css
@import "tailwindcss";

/* @preline/strong-password */
@source "../node_modules/@preline/strong-password/*.js";
@import "./node_modules/@preline/strong-password/variants.css";
@import "./node_modules/@preline/strong-password/theme.css";
```

### JavaScript

Include the JavaScript that powers the interactive elements near the end of your `</body>` tag:

```html
<script src="./node_modules/@preline/strong-password/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 HSStrongPassword from "@preline/strong-password/non-auto.mjs";

  new HSStrongPassword(document.querySelector("#strong-password"));
</script>
```

### Via Bundler

When using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.

`@preline/strong-password` is the auto-init entry: it scans the DOM and initializes matching elements automatically.

```js
import "@preline/strong-password";
```

`@preline/strong-password/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 HSStrongPassword from "@preline/strong-password/non-auto";

HSStrongPassword.autoInit();

// Or initialize a specific element manually
const el = document.querySelector("#strong-password");
if (el) new HSStrongPassword(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 strong password component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component shows password strength indicators as the user types.

```html
<div class="flex-1">
  <input type="password" id="hs-strong-password-input-first" class="py-3 px-4 block w-full border-gray-200 rounded-lg text-sm  focus:border-blue-500 focus:ring-blue-500 dark:bg-neutral-900 dark:border-neutral-700 dark:text-neutral-400">

  <div data-hs-strong-password='{
    "target": "#hs-strong-password-input-first",
    "stripClasses": "hs-strong-password:opacity-100 hs-strong-password-accepted:bg-green-500 h-2 flex-auto rounded-full bg-blue-500 opacity-50 mx-1"
  }' id="hs-strong-password-first" class="flex mt-2 -mx-1"></div>
</div>

<div class="mb-3">
  <span id="hs-strong-password-weakness-first"></span>
</div>
```

**Structure Requirements:**
- `data-hs-strong-password`: Required on the container element, contains configuration options as JSON
- `:target`: Required, must be a valid CSS selector pointing to the password input field
- Password input field must exist in the DOM
- Optional hints container for displaying password requirements

**Initial State:**
- Password input is empty
- Strength strips are hidden or inactive
- Hints are not displayed (unless configured)

## Configuration Options

### Data Options

Data options are specified in the `data-hs-strong-password` attribute as a JSON object.

| Option | Target Element | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `data-hs-strong-password` | Container element | - | - | Activate a Strong Password by specifying on an element. The value should contain at least one lowercase number, one uppercase letter, one number, one special character (!%&@#$^*?_~), and minimum 6 characters. Should be added to the content block that contains the checks. |
| `:target` | Inside `data-hs-strong-password` | string (CSS selector) | - | Determines which element should be observed. This must be a valid CSS selector. Required. |
| `:hints` | Inside `data-hs-strong-password` | string (CSS selector) | - | Determines which element should be observed as hints container. This must be a valid CSS selector. There are some selectors that available for using inside the hints: `[data-hs-strong-password-hints-weakness-text]` determines which element should be observed as weakness text container. It should contain an array of strings, e.g. - ["Empty", "Weak", "Medium", "Strong", "Very Strong", "Super Strong"]. Each text is for the concrete strength value, e.g. "Medium" have index 2, and it will be set up if strength is also 2. "Empty" have index 0, and it will be shown if field is empty and have no any strength. `[data-hs-strong-password-hints-rule-text]` determines which element should be observed as rule text container. It could have one of the follow value: "min-length" \| "lowercase" \| "uppercase" \| "numbers" \| "special-characters". `[data-check]` determines which element should be observed inside the `[data-hs-strong-password-hints-rule-text]` as rule check icon container. `[data-uncheck]` determines which element should be observed inside the `[data-hs-strong-password-hints-rule-text]` as rule uncheck icon container. |
| `:stripClasses` | Inside `data-hs-strong-password` | string | - | Defines CSS classes for each strip. These classes are applied to each strength indicator strip. |
| `:minLength` | Inside `data-hs-strong-password` | number | `6` | Determines minimum password length. |
| `:mode` | Inside `data-hs-strong-password` | `"default"` \| `"popover"` | `"default"` | Determines which mode should be used. If `popover` is selected, the hints will be displayed as a popover. |
| `:popoverSpace` | Inside `data-hs-strong-password` | number | `10` | Available if `mode` is set to `popover`. Determines the space between the popover and the target element. |
| `:checksExclude` | Inside `data-hs-strong-password` | array | `[]` | Determines which checks should be excluded. It could contain an array of strings, e.g. - ["min-length", "lowercase", "uppercase", "numbers", "special-characters"]. |
| `:specialCharactersSet` | Inside `data-hs-strong-password` | string | `!"#$%&\'()*+,-./:;<=>?@[\\]^_\`{|}~` | Determines which special characters should be used for checking. It could contain a string of available characters, e.g. - "!%&". |

**Example:**
```html
<div data-hs-strong-password='{
  "target": "#hs-password-input",
  "minLength": 8,
  "mode": "popover",
  "stripClasses": "h-2 flex-auto rounded-full bg-blue-500 opacity-50 mx-1"
}'>
</div>
```

### Tailwind Modifiers

| Name | Description |
| --- | --- |
| `hs-strong-password:*` | A modifier that allows you to set Tailwind classes for the passed stripes. |
| `hs-strong-password-accepted:*` | A modifier that allows you to set Tailwind classes when password strength is accepted. |

## JavaScript API

The `HSStrongPassword` object is available in the global `window` object after the plugin is loaded.

### Instance Methods

These methods are called on a strong password instance.

| Method | Parameters | Return Type | Description |
| --- | --- | --- | --- |
| `recalculateDirection()` | None | `void` | Force recalculation for dropdown hint position. Useful when scrolling or layout changes occur. |
| `destroy()` | None | `void` | Destroys the strong password instance, removes all generated markup, classes, and event listeners. Use when removing strong password from DOM. |

### Static Methods

These methods are called directly on the `HSStrongPassword` class.

| Method | Parameters | Return Type | Description |
| --- | --- | --- | --- |
| `HSStrongPassword.getInstance(target, isInstance)` | `target`: `HTMLElement \| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \| { id: string \| number, element: HSStrongPassword } \| null` | Returns the strong password instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSStrongPassword` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if strong password instance is not found. |

### Usage Examples

**Example 1: Recalculating popover position**
```javascript
// Get the strong password instance
const instance = HSStrongPassword.getInstance('#hs-strong-password', true);

if (instance) {
  const { element } = element;

  // Recalculate popover position on scroll
  document.addEventListener('scroll', () => {
    element.recalculateDirection();
  });
}
```

**Example 2: Getting instance and accessing properties**
```javascript
// Get the strong password instance
const instance = HSStrongPassword.getInstance('#hs-strong-password', true);

if (instance) {
  const { element } = instance;

  // Access instance properties
  console.log('Strength:', element.strength);
  console.log('Passed rules:', element.passedRules);
  console.log('Is opened:', element.isOpened);

  // Clean up when removing from DOM
  function removeStrongPassword() {
    element.destroy();
  }
}
```

**Example 3: Destroying strong password instance**
```javascript
const instance = HSStrongPassword.getInstance('#hs-strong-password', 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-strong-password').remove();
  });
}
```

## Events

Strong password instances emit events that can be listened to for strength tracking and custom behavior.

| Event Name | When Fired | Callback Parameter | Description |
| --- | --- | --- | --- |
| `on:change` | When target element was changed | `{ strength: number, rules: string[] }` | Fires when the password input value changes. Returns an object with `strength` (0-4, current level of strength, default is 0) and `rules` (array of passed checks: "lowercase", "uppercase", "numbers", "special-characters"). |

### Event Usage Example

```javascript
// Get strong password instance
const instance = HSStrongPassword.getInstance('#hs-strong-password', true);

if (instance) {
  const { element } = instance;

  // Listen to change event
  element.on('change', ({ strength, rules }) => {
    console.log('Password strength:', strength);
    console.log('Passed rules:', rules);
    // Perform actions based on strength
    // e.g., enable/disable submit button, show messages
  });
}
```

## Common Patterns

### Pattern 1: With Hints Container

Display password requirements in a hints container.

```html
<input type="password" id="hs-password-input">

<div data-hs-strong-password='{
  "target": "#hs-password-input",
  "hints": "#hs-hints-container"
}'></div>

<div id="hs-hints-container">
  <div data-hs-strong-password-hints-weakness-text></div>
  <div data-hs-strong-password-hints-rule-text data-check="min-length"></div>
</div>
```

### Pattern 2: Popover Mode

Display hints as a popover.

```html
<input type="password" id="hs-password-input">

<div data-hs-strong-password='{
  "target": "#hs-password-input",
  "mode": "popover",
  "popoverSpace": 10
}'></div>
```

### Pattern 3: Custom Requirements

Exclude certain checks or customize special characters.

```html
<div data-hs-strong-password='{
  "target": "#hs-password-input",
  "checksExclude": ["special-characters"],
  "specialCharactersSet": "!@#$%"
}'></div>
```

## License

Copyright (c) 2024 Htmlstream.

Licensed under the [MIT License](https://opensource.org/licenses/MIT).
