diff --git a/README.md b/README.md index e445cd4..6bffdd5 100644 --- a/README.md +++ b/README.md @@ -251,6 +251,7 @@ Join our [Discord channel](https://discord.gg/WhX2nG6GTQ) or [open an issue](htt | **disabled** | `boolean` | `false` | Whether the input should be disabled for the user (API can still be used programmatically). | | **inputType** | `string` | `text` | The `type` attribute of the search input. | | **autocomplete** | `string` | `undefined` | The `autocomplete` attribute of the search input. | +| **rtl** | `boolean` | `false` | Whether the multiselect should be right-to-left. It also respects `dir="rtl"` on any parent element. | | **max** | `number` | `-1` | The maximum number of options that **can be selected** when using `multiple` or `tags` mode. If `-1` the number of options won't be limited. | | **limit** | `number` | `-1` | The maximum number of options that **should be displayed**. If `-1` the number of options won't be limited. | | **loading** | `boolean` | `false` | Whether a loading spinner should be shown. | @@ -555,24 +556,24 @@ Alternatively you can define class names directly by passing them to the `Multis containerOpen: 'rounded-b-none', containerOpenTop: 'rounded-t-none', containerActive: 'ring ring-green-500 ring-opacity-30', - singleLabel: 'flex items-center h-full max-w-full absolute left-0 top-0 pointer-events-none bg-transparent leading-snug pl-3.5 pr-16 box-border', + singleLabel: 'flex items-center h-full max-w-full absolute left-0 top-0 pointer-events-none bg-transparent leading-snug pl-3.5 pr-16 box-border rtl:left-auto rtl:right-0 rtl:pl-0 rtl:pr-3.5', singleLabelText: 'overflow-ellipsis overflow-hidden block whitespace-nowrap max-w-full', - multipleLabel: 'flex items-center h-full absolute left-0 top-0 pointer-events-none bg-transparent leading-snug pl-3.5', - search: 'w-full absolute inset-0 outline-none focus:ring-0 appearance-none box-border border-0 text-base font-sans bg-white rounded pl-3.5', - tags: 'flex-grow flex-shrink flex flex-wrap items-center mt-1 pl-2', - tag: 'bg-green-500 text-white text-sm font-semibold py-0.5 pl-2 rounded mr-1 mb-1 flex items-center whitespace-nowrap', - tagDisabled: 'pr-2 opacity-50', + multipleLabel: 'flex items-center h-full absolute left-0 top-0 pointer-events-none bg-transparent leading-snug pl-3.5 rtl:left-auto rtl:right-0 rtl:pl-0 rtl:pr-3.5', + search: 'w-full absolute inset-0 outline-none focus:ring-0 appearance-none box-border border-0 text-base font-sans bg-white rounded pl-3.5 rtl:pl-0 rtl:pr-3.5', + tags: 'flex-grow flex-shrink flex flex-wrap items-center mt-1 pl-2 rtl:pl-0 rtl:pr-2', + tag: 'bg-green-500 text-white text-sm font-semibold py-0.5 pl-2 rounded mr-1 mb-1 flex items-center whitespace-nowrap rtl:pl-0 rtl:pr-2 rtl:mr-0 rtl:ml-1', + tagDisabled: 'pr-2 opacity-50 rtl:pl-2', tagRemove: 'flex items-center justify-center p-1 mx-0.5 rounded-sm hover:bg-black hover:bg-opacity-10 group', tagRemoveIcon: 'bg-multiselect-remove bg-center bg-no-repeat opacity-30 inline-block w-3 h-3 group-hover:opacity-60', tagsSearchWrapper: 'inline-block relative mx-1 mb-1 flex-grow flex-shrink h-full', tagsSearch: 'absolute inset-0 border-0 outline-none focus:ring-0 appearance-none p-0 text-base font-sans box-border w-full', tagsSearchCopy: 'invisible whitespace-pre-wrap inline-block h-px', - placeholder: 'flex items-center h-full absolute left-0 top-0 pointer-events-none bg-transparent leading-snug pl-3.5 text-gray-400', - caret: 'bg-multiselect-caret bg-center bg-no-repeat w-2.5 h-4 py-px box-content mr-3.5 relative z-10 opacity-40 flex-shrink-0 flex-grow-0 transition-transform transform pointer-events-none', + placeholder: 'flex items-center h-full absolute left-0 top-0 pointer-events-none bg-transparent leading-snug pl-3.5 text-gray-400 rtl:left-auto rtl:right-0 rtl:pl-0 rtl:pr-3.5', + caret: 'bg-multiselect-caret bg-center bg-no-repeat w-2.5 h-4 py-px box-content mr-3.5 relative z-10 opacity-40 flex-shrink-0 flex-grow-0 transition-transform transform pointer-events-none rtl:mr-0 rtl:ml-3.5', caretOpen: 'rotate-180 pointer-events-auto', - clear: 'pr-3.5 relative z-10 opacity-40 transition duration-300 flex-shrink-0 flex-grow-0 flex hover:opacity-80', + clear: 'pr-3.5 relative z-10 opacity-40 transition duration-300 flex-shrink-0 flex-grow-0 flex hover:opacity-80 rtl:pr-0 rtl:pl-3.5', clearIcon: 'bg-multiselect-remove bg-center bg-no-repeat w-2.5 h-4 py-px box-content inline-block', - spinner: 'bg-multiselect-spinner bg-center bg-no-repeat w-4 h-4 z-10 mr-3.5 animate-spin flex-shrink-0 flex-grow-0', + spinner: 'bg-multiselect-spinner bg-center bg-no-repeat w-4 h-4 z-10 mr-3.5 animate-spin flex-shrink-0 flex-grow-0 rtl:mr-0 rtl:ml-3.5', dropdown: 'max-h-60 absolute -left-px -right-px bottom-0 transform translate-y-full border border-gray-300 -mt-px overflow-y-scroll z-50 bg-white flex flex-col rounded-b', dropdownTop: '-translate-y-full top-px bottom-auto flex-col-reverse rounded-b-none rounded-t', dropdownHidden: 'hidden', diff --git a/src/Multiselect.d.ts b/src/Multiselect.d.ts index eb3cca9..93f2ebd 100644 --- a/src/Multiselect.d.ts +++ b/src/Multiselect.d.ts @@ -44,17 +44,18 @@ declare class Multiselect extends Vue { strict?: boolean; closeOnSelect?: boolean; autocomplete?: string; - groups: boolean; - groupLabel: string; - groupOptions: string; - groupHideEmpty: boolean; - groupSelect: boolean; - inputType: string; + groups?: boolean; + groupLabel?: string; + groupOptions?: string; + groupHideEmpty?: boolean; + groupSelect?: boolean; + inputType?: string; attrs?: object; onCreate?: Function; - searchStart: boolean; - reverse: boolean; - regex: string|object; + searchStart?: boolean; + reverse?: boolean; + regex?: string|object; + rtl?: boolean; $emit(eventName: 'change', e: {originalEvent: Event, value: any}): this; $emit(eventName: 'select', e: {originalEvent: Event, value: any, option: any}): this; diff --git a/src/Multiselect.vue b/src/Multiselect.vue index 950ce41..a7d46af 100644 --- a/src/Multiselect.vue +++ b/src/Multiselect.vue @@ -4,6 +4,7 @@ :tabindex="tabindex" :class="classList.container" :id="id" + :dir="rtl ? 'rtl' : undefined" @focusin="activate" @focusout="deactivate" @keydown="handleKeydown" @@ -459,12 +460,12 @@ }, attrs: { required: false, - type: [Object], + type: Object, default: () => ({}), }, onCreate: { required: false, - type: [Function], + type: Function, }, disabledProp: { type: String, @@ -486,6 +487,11 @@ required: false, default: undefined, }, + rtl: { + type: Boolean, + required: false, + default: false, + }, }, setup(props, context) { diff --git a/themes/default.scss b/themes/default.scss index e6e8866..9f6fe4a 100644 --- a/themes/default.scss +++ b/themes/default.scss @@ -428,6 +428,46 @@ display: none; } +[dir="rtl"] { + .multiselect-multiple-label, + .multiselect-single-label, + .multiselect-placeholder { + padding-right: var(--ms-px, 0.875rem); + padding-left: calc(1.25rem + var(--ms-px, 0.875rem) * 3); + left: auto; + right: 0; + } + + .multiselect-search { + padding-left: 0; + padding-right: var(--ms-px, 0.875rem); + } + + .multiselect-tags { + padding-left: 0; + padding-right: var(--ms-py, 0.5rem); + } + + .multiselect-tag { + padding: var(--ms-tag-py, 0.125rem) var(--ms-tag-px, 0.5rem) var(--ms-tag-py, 0.125rem) 0; + margin-right: 0; + margin-left: var(--ms-tag-mx, 0.25rem); + + &.is-disabled { + padding-left: var(--ms-tag-px, 0.5rem); + } + } + + .multiselect-spinner, + .multiselect-caret { + margin: 0 0 0 var(--ms-px, 0.875rem); + } + + .multiselect-clear { + padding: 0 0 0 var(--ms-px, 0.875rem); + } +} + @keyframes multiselect-spin { from { transform: rotate(0); diff --git a/themes/tailwind.scss b/themes/tailwind.scss index 00b1f1e..2a5e007 100644 --- a/themes/tailwind.scss +++ b/themes/tailwind.scss @@ -19,7 +19,7 @@ } .multiselect-single-label { - @apply flex items-center h-full max-w-full absolute left-0 top-0 pointer-events-none bg-transparent leading-snug pl-3.5 pr-16 box-border; + @apply flex items-center h-full max-w-full absolute left-0 top-0 pointer-events-none bg-transparent leading-snug pl-3.5 pr-16 box-border rtl:left-auto rtl:right-0 rtl:pl-0 rtl:pr-3.5; } .multiselect-single-label-text { @@ -27,22 +27,22 @@ } .multiselect-multiple-label { - @apply flex items-center h-full absolute left-0 top-0 pointer-events-none bg-transparent leading-snug pl-3.5; + @apply flex items-center h-full absolute left-0 top-0 pointer-events-none bg-transparent leading-snug pl-3.5 rtl:left-auto rtl:right-0 rtl:pl-0 rtl:pr-3.5; } .multiselect-search { - @apply w-full absolute inset-0 outline-none focus:ring-0 appearance-none box-border border-0 text-base font-sans bg-white rounded pl-3.5; + @apply w-full absolute inset-0 outline-none focus:ring-0 appearance-none box-border border-0 text-base font-sans bg-white rounded pl-3.5 rtl:pl-0 rtl:pr-3.5; } .multiselect-tags { - @apply flex-grow flex-shrink flex flex-wrap items-center mt-1 pl-2; + @apply flex-grow flex-shrink flex flex-wrap items-center mt-1 pl-2 rtl:pl-0 rtl:pr-2; } .multiselect-tag { - @apply bg-green-500 text-white text-sm font-semibold py-0.5 pl-2 rounded mr-1 mb-1 flex items-center whitespace-nowrap; + @apply bg-green-500 text-white text-sm font-semibold py-0.5 pl-2 rounded mr-1 mb-1 flex items-center whitespace-nowrap rtl:pl-0 rtl:pr-2 rtl:mr-0 rtl:ml-1; &.is-disabled { - @apply pr-2 opacity-50; + @apply pr-2 opacity-50 rtl:pl-2; } } @@ -71,11 +71,11 @@ } .multiselect-placeholder { - @apply flex items-center h-full absolute left-0 top-0 pointer-events-none bg-transparent leading-snug pl-3.5 text-gray-400; + @apply flex items-center h-full absolute left-0 top-0 pointer-events-none bg-transparent leading-snug pl-3.5 text-gray-400 rtl:left-auto rtl:right-0 rtl:pl-0 rtl:pr-3.5; } .multiselect-caret { - @apply bg-multiselect-caret bg-center bg-no-repeat w-2.5 h-4 py-px box-content mr-3.5 relative z-10 opacity-40 flex-shrink-0 flex-grow-0 transition-transform transform pointer-events-none; + @apply bg-multiselect-caret bg-center bg-no-repeat w-2.5 h-4 py-px box-content mr-3.5 relative z-10 opacity-40 flex-shrink-0 flex-grow-0 transition-transform transform pointer-events-none rtl:mr-0 rtl:ml-3.5; &.is-open { @apply rotate-180 pointer-events-auto; @@ -83,7 +83,7 @@ } .multiselect-clear { - @apply pr-3.5 relative z-10 opacity-40 transition duration-300 flex-shrink-0 flex-grow-0 flex hover:opacity-80; + @apply pr-3.5 relative z-10 opacity-40 transition duration-300 flex-shrink-0 flex-grow-0 flex hover:opacity-80 rtl:pr-0 rtl:pl-3.5; } .multiselect-clear-icon { @@ -91,7 +91,7 @@ } .multiselect-spinner { - @apply bg-multiselect-spinner bg-center bg-no-repeat w-4 h-4 z-10 mr-3.5 animate-spin flex-shrink-0 flex-grow-0; + @apply bg-multiselect-spinner bg-center bg-no-repeat w-4 h-4 z-10 mr-3.5 animate-spin flex-shrink-0 flex-grow-0 rtl:mr-0 rtl:ml-3.5; } .multiselect-dropdown { @@ -171,11 +171,11 @@ } .multiselect-no-options { - @apply py-2 px-3 text-gray-600 bg-white text-left; + @apply py-2 px-3 text-gray-600 bg-white text-left rtl:text-right; } .multiselect-no-results { - @apply py-2 px-3 text-gray-600 bg-white text-left; + @apply py-2 px-3 text-gray-600 bg-white text-left rtl:text-right; } .multiselect-fake-input {