Skip to content

Keyboard support and more #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions DynamicSelect.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
dynamic-select selection, dynamic-select selected, dynamic-select dropdown, dynamic-select item {
all: unset;
display: block;
}
dynamic-select[disabled], .dynamic-select .dynamic-select-options .dynamic-select-option[disabled] {
opacity: 0.5;
pointer-events: none;
background-color: transparent;
pointer-events: none;
}
.dynamic-select {
display: flex;
box-sizing: border-box;
Expand All @@ -11,7 +21,7 @@
padding: 7px 30px 7px 12px;
}
.dynamic-select .dynamic-select-header::after {
content: "";
content: '';
display: block;
position: absolute;
top: 50%;
Expand Down Expand Up @@ -66,7 +76,7 @@
.dynamic-select .dynamic-select-options .dynamic-select-option {
padding: 7px 12px;
}
.dynamic-select .dynamic-select-options .dynamic-select-option:hover, .dynamic-select .dynamic-select-options .dynamic-select-option:active {
.dynamic-select .dynamic-select-options .dynamic-select-option:hover, .dynamic-select .dynamic-select-options .dynamic-select-option:active, .dynamic-select .dynamic-select-options .dynamic-select-option.focus {
background-color: #f3f4f7;
}
.dynamic-select .dynamic-select-header, .dynamic-select .dynamic-select-option {
Expand All @@ -92,14 +102,14 @@
max-height: none;
max-width: none;
}
.dynamic-select .dynamic-select-header img, .dynamic-select .dynamic-select-header svg, .dynamic-select .dynamic-select-header i, .dynamic-select .dynamic-select-header span, .dynamic-select .dynamic-select-option img, .dynamic-select .dynamic-select-option svg, .dynamic-select .dynamic-select-option i, .dynamic-select .dynamic-select-option span {
.dynamic-select .dynamic-select-header img, .dynamic-select .dynamic-select-option img, .dynamic-select .dynamic-select-header svg, .dynamic-select .dynamic-select-option svg, .dynamic-select .dynamic-select-header i, .dynamic-select .dynamic-select-option i, .dynamic-select .dynamic-select-header span, .dynamic-select .dynamic-select-option span {
box-sizing: border-box;
margin-right: 10px;
}
.dynamic-select .dynamic-select-header.dynamic-select-no-text, .dynamic-select .dynamic-select-option.dynamic-select-no-text {
justify-content: center;
}
.dynamic-select .dynamic-select-header.dynamic-select-no-text img, .dynamic-select .dynamic-select-header.dynamic-select-no-text svg, .dynamic-select .dynamic-select-header.dynamic-select-no-text i, .dynamic-select .dynamic-select-header.dynamic-select-no-text span, .dynamic-select .dynamic-select-option.dynamic-select-no-text img, .dynamic-select .dynamic-select-option.dynamic-select-no-text svg, .dynamic-select .dynamic-select-option.dynamic-select-no-text i, .dynamic-select .dynamic-select-option.dynamic-select-no-text span {
.dynamic-select .dynamic-select-header.dynamic-select-no-text img, .dynamic-select .dynamic-select-option.dynamic-select-no-text img, .dynamic-select .dynamic-select-header.dynamic-select-no-text svg, .dynamic-select .dynamic-select-option.dynamic-select-no-text svg, .dynamic-select .dynamic-select-header.dynamic-select-no-text i, .dynamic-select .dynamic-select-option.dynamic-select-no-text i, .dynamic-select .dynamic-select-header.dynamic-select-no-text span, .dynamic-select .dynamic-select-option.dynamic-select-no-text span {
margin-right: 0;
}
.dynamic-select .dynamic-select-header .dynamic-select-option-text, .dynamic-select .dynamic-select-option .dynamic-select-option-text {
Expand All @@ -110,4 +120,4 @@
white-space: nowrap;
color: inherit;
font-size: inherit;
}
}
295 changes: 258 additions & 37 deletions DynamicSelect.js

Large diffs are not rendered by default.

17 changes: 15 additions & 2 deletions DynamicSelect.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
dynamic-select {
selection, selected, dropdown, item {
all: unset;
display: block;
}
}
dynamic-select[disabled],
.dynamic-select .dynamic-select-options .dynamic-select-option[disabled] {
opacity: 0.5;
pointer-events: none;
background-color: transparent;
pointer-events: none;
}
.dynamic-select {
display: flex;
box-sizing: border-box;
Expand Down Expand Up @@ -63,7 +76,7 @@
}
.dynamic-select-option {
padding: 7px 12px;
&:hover, &:active {
&:hover, &:active, &.focus {
background-color: #f3f4f7;
}
}
Expand Down Expand Up @@ -110,4 +123,4 @@
font-size: inherit;
}
}
}
}
105 changes: 93 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ The complete guide and reference is available here: [https://codeshack.io/multi-
- Customizable placeholder text
- Multiple columns for dropdown options
- Custom HTML content for options
- Semantic naming convension
- Easy integration with existing forms
- Navigability with tabulation and UP/DOWN arrow keys
- Toggle, Open & Close with keyboard buttons: **Enter**, **Spacebar** & **Esc**
- Search by first letter using any keyboard buttons (a-z, 0-9 + supports UNICODE)
- Reverse search by holding **shift** key
- Disable functionality of dynamic select element and any option from the dropdown in realtime
- Lightweight and fast

## Screenshot
Expand All @@ -30,9 +36,9 @@ The complete guide and reference is available here: [https://codeshack.io/multi-
3. Initialize the dynamic select element in your HTML file:
```html
<select id="dynamic-select" name="example-select" data-placeholder="Select an option" data-dynamic-select>
<option value="1" data-img="path/to/image1.jpg">Option 1</option>
<option value="2" data-img="path/to/image2.jpg">Option 2</option>
<option value="3" data-img="path/to/image3.jpg">Option 3</option>
<option value="1" data-img="path/to/image1.jpg">Option 1</option>
<option value="2" data-img="path/to/image2.png" disabled>Option 2</option>
<option value="3" data-img="path/to/image3.svg">Option 3</option>
</select>
```

Expand All @@ -49,6 +55,12 @@ The complete guide and reference is available here: [https://codeshack.io/multi-
});
```

`new DynamicSelect()` object inherits attribute data from the target element. Hence property: **placeholder**, overwrites dataset attribute: **data-placeholder**.

Additionally, **class** and **style** attributes are reserved for the `<select>` element in case **DynamicSelect** object fails to convert the target select element to `<dynamic-select>`. Use dataset attributes: **data-style** and **data-class** to appropriately style the `<dynamic-select>` element.

<br>

## Usage

### Basic Example
Expand All @@ -68,6 +80,7 @@ To use this dynamic select with images in your project, follow these steps:
```javascript
new DynamicSelect('#dynamic-select', {
placeholder: 'Select an option',
tabindex: 0,
columns: 1,
width: '300px',
height: '40px',
Expand All @@ -82,6 +95,8 @@ To use this dynamic select with images in your project, follow these steps:
});
```

<br>

### Advanced Example with Custom HTML Content

You can also use custom HTML content for the options:
Expand All @@ -98,16 +113,35 @@ You can also use custom HTML content for the options:
new DynamicSelect('#custom-select', {
placeholder: 'Select an option',
data: [
{ value: '1', html: '<img src="path/to/image1.jpg" alt="Option 1"><span>Option 1</span>' },
{ value: '2', html: '<img src="path/to/image2.jpg" alt="Option 2"><span>Option 2</span>' },
{ value: '3', html: '<img src="path/to/image3.jpg" alt="Option 3"><span>Option 3</span>' }
{
value: '1',
html: '<img src="path/to/image1.jpg" alt="Option 1"><span class="dynamic-select-option-text">Option 1</span>'
},
{
value: '2',
html: '<img src="path/to/image2.jpg" alt="Option 2"><span class="dynamic-select-option-text">Option 2</span>'
},
{
value: '3',
html: '<img src="path/to/image3.jpg" alt="Option 3"><span class="dynamic-select-option-text">Option 3</span>'
}
],
onChange: function(value, text, option) {
console.log(value, text, option);
}
});
```

```html
<span class="dynamic-select-option-text">Option N</span>
```

Custom defined HTML context data of `<item>` element requires an instance of `.dynamic-select-option-text` for the element that holds text data. This enhances search functionality by allowing clients to filter options based on the first letter, which is ideal for selection with a wide variety of options, such as: selecting location data - country / state / province / district / county / city...

**More Advanced Examples** - https://jsfiddle.net/sat6h1r4/

<br>

### Example with Multiple Columns

For dropdown options to be displayed in multiple columns:
Expand Down Expand Up @@ -139,35 +173,82 @@ new DynamicSelect('#multi-column-select', {

It is useful if you want to populate images in a grid-like view.

<br>

## Configuration

To customize the dynamic select with images, you can modify the HTML and JavaScript as needed. The following options are available:

- `placeholder`: Placeholder text for the select element.
- `tabindex`: Tabindex attribute for the `<dynamic-select>`.
- `columns`: Number of columns in the dropdown.
- `name`: Name attribute for the select element.
- `width`: Width of the select element.
- `height`: Height of the select element.
- `data`: Array of objects representing the select options.
- `name`: Name attribute for the `<selection>`'s `<input>` element.
- `disabled`: Disabled attribute for the `<dynamic-select>` element.
<hr>

**NOTE** - Added `disabled` **attribute** to `<dynamic-select>` also disables `<selection>`'s `<input>`.<br>Additionally, `disabled` **property** requires a boolean value for it to be initialized and updated.
<hr>

- `style`: Style attribute for the `<dynamic-select>` element.
- `class`: Class attribute for the `<dynamic-select>` element.
- `width`: Width attribute for the `<selection>` element.
- `height`: Height attribute for the `<selection>` element.
- `selectionStyle`: Style attribute for the `<selection>` element.
- `selectionClass`: Class attribute for the `<selection>` element.
- `selectedStyle`: Style attribute for the `<selected>` element.
- `selectedClass`: Class attribute for the `<selected>` element.
<hr>

- `dropdownWidth`: Width attribute for the `<dropdown>` element.
- `dropdownHeight`: Height attribute for the `<dropdown>` element.
- `dropdownStyle`: Style attribute for the `<dropdown>` element.
- `dropdownClass`: Class attribute for the `<dropdown>` element.
- `itemsStyle`: Style attribute for all instances of the `<item>` element.
- `itemsClass`: Class attribute for all instances of the `<item>` element.
<hr>

**NOTE** - Any **style** or **class** option, for example: **dropdownClass**, overwrites its defined properties: width, height,
and any styles from the *"DynamicSelect.css"* stylesheet.
<hr>

- `data`: Array of objects representing the select options: `<item>` element.

<hr>

**NOTE** - Present `<option>` children, of target `<select>` element, are not included in `data` by default. Hence, options disclusion, unless they are specified directly within this array object.
<hr>

- `onChange`: Callback function when the selected option changes.

<br>

Example configuration:
```javascript
new DynamicSelect('#dynamic-select', {
placeholder: 'Select an option',
tabindex: 0,
columns: 2,
disabled: false,
// Style however you like
width: '300px',
height: '50px',
class: 'form-control px-0', // for Bootstrap5 library
selectedStyle: 'border: 0', // for Bootstrap5 library
dropdownStyle: 'background-color: #E8E8E8',
// Customize options ( <item> elements of parent <dropdown> )
data: [
{ value: '1', text: 'Option 1', img: 'path/to/image1.jpg' },
{ value: '2', text: 'Option 2', img: 'path/to/image2.jpg' },
{ value: '3', text: 'Option 3', img: 'path/to/image3.jpg' }
{ value: '2', text: 'Option 2', img: 'path/to/image2.jpg', disabled: true },
{ value: '3', text: 'Option 3', img: 'path/to/image3.jpg', selected: true }
],
onChange: function(value, text, option) {
console.log(value, text, option);
}
});
```
**More Advanced Examples** - https://jsfiddle.net/sat6h1r4/

<br>

## License

Expand Down
123 changes: 123 additions & 0 deletions advanced examples/DynamicSelect.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
dynamic-select selection, dynamic-select selected, dynamic-select dropdown, dynamic-select item {
all: unset;
display: block;
}
dynamic-select[disabled], .dynamic-select .dynamic-select-options .dynamic-select-option[disabled] {
opacity: 0.5;
pointer-events: none;
background-color: transparent;
pointer-events: none;
}
.dynamic-select {
display: flex;
box-sizing: border-box;
flex-direction: column;
position: relative;
width: 100%;
user-select: none;
}
.dynamic-select .dynamic-select-header {
border: 1px solid #dee2e6;
padding: 7px 30px 7px 12px;
}
.dynamic-select .dynamic-select-header::after {
content: '';
display: block;
position: absolute;
top: 50%;
right: 15px;
transform: translateY(-50%);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23949ba3' viewBox='0 0 16 16'%3E%3Cpath d='M8 13.1l-8-8 2.1-2.2 5.9 5.9 5.9-5.9 2.1 2.2z'/%3E%3C/svg%3E");
height: 12px;
width: 12px;
}
.dynamic-select .dynamic-select-header.dynamic-select-header-active {
border-color: #c1c9d0;
}
.dynamic-select .dynamic-select-header.dynamic-select-header-active::after {
transform: translateY(-50%) rotate(180deg);
}
.dynamic-select .dynamic-select-header.dynamic-select-header-active + .dynamic-select-options {
display: flex;
}
.dynamic-select .dynamic-select-header .dynamic-select-header-placeholder {
color: #65727e;
}
.dynamic-select .dynamic-select-options {
display: none;
box-sizing: border-box;
flex-flow: wrap;
position: absolute;
top: 100%;
left: 0;
right: 0;
z-index: 999;
margin-top: 5px;
padding: 5px;
background-color: #fff;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
max-height: 200px;
overflow-y: auto;
overflow-x: hidden;
}
.dynamic-select .dynamic-select-options::-webkit-scrollbar {
width: 5px;
}
.dynamic-select .dynamic-select-options::-webkit-scrollbar-track {
background: #f0f1f3;
}
.dynamic-select .dynamic-select-options::-webkit-scrollbar-thumb {
background: #cdcfd1;
}
.dynamic-select .dynamic-select-options::-webkit-scrollbar-thumb:hover {
background: #b2b6b9;
}
.dynamic-select .dynamic-select-options .dynamic-select-option {
padding: 7px 12px;
}
.dynamic-select .dynamic-select-options .dynamic-select-option:hover, .dynamic-select .dynamic-select-options .dynamic-select-option:active, .dynamic-select .dynamic-select-options .dynamic-select-option.focus {
background-color: #f3f4f7;
}
.dynamic-select .dynamic-select-header, .dynamic-select .dynamic-select-option {
display: flex;
box-sizing: border-box;
align-items: center;
border-radius: 5px;
cursor: pointer;
display: flex;
align-items: center;
width: 100%;
height: 45px;
font-size: 16px;
color: #212529;
}
.dynamic-select .dynamic-select-header img, .dynamic-select .dynamic-select-option img {
object-fit: contain;
max-height: 100%;
max-width: 100%;
}
.dynamic-select .dynamic-select-header img.dynamic-size, .dynamic-select .dynamic-select-option img.dynamic-size {
object-fit: fill;
max-height: none;
max-width: none;
}
.dynamic-select .dynamic-select-header img, .dynamic-select .dynamic-select-option img, .dynamic-select .dynamic-select-header svg, .dynamic-select .dynamic-select-option svg, .dynamic-select .dynamic-select-header i, .dynamic-select .dynamic-select-option i, .dynamic-select .dynamic-select-header span, .dynamic-select .dynamic-select-option span {
box-sizing: border-box;
margin-right: 10px;
}
.dynamic-select .dynamic-select-header.dynamic-select-no-text, .dynamic-select .dynamic-select-option.dynamic-select-no-text {
justify-content: center;
}
.dynamic-select .dynamic-select-header.dynamic-select-no-text img, .dynamic-select .dynamic-select-option.dynamic-select-no-text img, .dynamic-select .dynamic-select-header.dynamic-select-no-text svg, .dynamic-select .dynamic-select-option.dynamic-select-no-text svg, .dynamic-select .dynamic-select-header.dynamic-select-no-text i, .dynamic-select .dynamic-select-option.dynamic-select-no-text i, .dynamic-select .dynamic-select-header.dynamic-select-no-text span, .dynamic-select .dynamic-select-option.dynamic-select-no-text span {
margin-right: 0;
}
.dynamic-select .dynamic-select-header .dynamic-select-option-text, .dynamic-select .dynamic-select-option .dynamic-select-option-text {
box-sizing: border-box;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: inherit;
font-size: inherit;
}
Loading