slim-select 2.13.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +184 -43
  2. package/dist/index.d.ts +304 -52
  3. package/dist/react/index.d.ts +304 -0
  4. package/dist/react/index.js +1549 -0
  5. package/dist/react/index.umd.js +22 -0
  6. package/dist/slimselect.cjs.js +1 -2030
  7. package/dist/slimselect.css +1 -1
  8. package/dist/slimselect.css.map +1 -1
  9. package/dist/slimselect.es.js +1224 -2012
  10. package/dist/slimselect.umd.js +1 -2036
  11. package/dist/vue/index.d.ts +304 -0
  12. package/dist/vue/index.js +1341 -0
  13. package/dist/vue/index.umd.js +1 -0
  14. package/index.html +1 -0
  15. package/package.json +58 -36
  16. package/release.js +0 -7
  17. package/src/slim-select/accessibility.test.ts +782 -0
  18. package/src/slim-select/classes.test.ts +7 -6
  19. package/src/slim-select/classes.ts +8 -6
  20. package/src/slim-select/helpers.test.ts +10 -8
  21. package/src/slim-select/index.test.ts +509 -14
  22. package/src/slim-select/index.ts +58 -31
  23. package/src/slim-select/react/index.tsx +9 -0
  24. package/src/slim-select/react/react.tsx +112 -0
  25. package/src/slim-select/render.test.ts +479 -119
  26. package/src/slim-select/render.ts +213 -91
  27. package/src/slim-select/select.test.ts +28 -10
  28. package/src/slim-select/select.ts +35 -12
  29. package/src/slim-select/settings.test.ts +19 -8
  30. package/src/slim-select/settings.ts +2 -4
  31. package/src/slim-select/slimselect.scss +30 -15
  32. package/src/slim-select/store.test.ts +13 -16
  33. package/src/slim-select/store.ts +97 -86
  34. package/src/slim-select/vue/index.ts +9 -0
  35. package/src/slim-select/vue/vue.test.ts +832 -0
  36. package/src/slim-select/{vue.vue → vue/vue.vue} +45 -23
  37. package/tsconfig.json +6 -4
  38. package/{vite.config.mts → vite.config.docs.mts} +10 -0
  39. package/vite.config.lib.mts +42 -0
  40. package/vite.config.react.mts +40 -0
  41. package/vite.config.vue.mts +39 -0
  42. package/vitest.config.ts +29 -0
  43. package/dist/classes.d.ts +0 -43
  44. package/dist/helpers.d.ts +0 -5
  45. package/dist/render.d.ts +0 -81
  46. package/dist/select.d.ts +0 -30
  47. package/dist/settings.d.ts +0 -35
  48. package/dist/slimselect.global.js +0 -2033
  49. package/dist/slimselect.js +0 -2036
  50. package/dist/slimselect.min.js +0 -1
  51. package/dist/slimselect.umd.min.js +0 -1
  52. package/dist/store.d.ts +0 -84
  53. package/jest.config.js +0 -14
  54. package/src/slim-select/rollup.config.mjs +0 -127
  55. package/src/slim-select/tsconfig.json +0 -14
@@ -1,8 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { defineComponent, PropType } from 'vue'
3
- import SlimSelect, { Config, Events } from '../slim-select'
4
- import { SettingsPartial } from '../slim-select/settings'
5
- import { DataArrayPartial, Option } from '../slim-select/store'
3
+ import SlimSelect, { Config, Events, Option, Optgroup, Settings } from '../index'
6
4
 
7
5
  export default defineComponent({
8
6
  name: 'SlimSelect',
@@ -15,10 +13,10 @@ export default defineComponent({
15
13
  default: false
16
14
  },
17
15
  data: {
18
- type: Array as PropType<DataArrayPartial>
16
+ type: Array as PropType<(Partial<Option> | Partial<Optgroup>)[]>
19
17
  },
20
18
  settings: {
21
- type: Object as PropType<SettingsPartial>
19
+ type: Object as PropType<Partial<Settings>>
22
20
  },
23
21
  events: {
24
22
  type: Object as PropType<Events>,
@@ -34,6 +32,14 @@ export default defineComponent({
34
32
  }
35
33
  },
36
34
  mounted() {
35
+ // Warning: If both slot and data are provided, data takes precedence
36
+ const hasSlotContent = this.$slots.default && this.$slots.default().length > 0
37
+ if (hasSlotContent && this.data) {
38
+ console.warn(
39
+ '[SlimSelect Vue] Both slot content and data prop are provided. Data prop will take precedence and slot content will be ignored.'
40
+ )
41
+ }
42
+
37
43
  let config = {
38
44
  select: this.$refs.slim
39
45
  } as Config
@@ -48,22 +54,22 @@ export default defineComponent({
48
54
  config.settings = this.settings
49
55
  }
50
56
 
51
- // If events are passed in, use it
52
- config.events = (this.events || {}) as Events
57
+ // Create a copy of events to avoid mutating the prop
58
+ const ogAfterChange = this.events?.afterChange
59
+ config.events = {
60
+ ...this.events,
61
+ afterChange: (newVal: Option[]) => {
62
+ const value = this.multiple ? newVal.map((option) => option.value) : newVal.length > 0 ? newVal[0].value : ''
53
63
 
54
- // Wrap config.events.afterChange to run value update
55
- const ogAfterChange = config.events.afterChange
56
- config.events.afterChange = (newVal: Option[]) => {
57
- const value = this.multiple ? newVal.map((option) => option.value) : newVal.length > 0 ? newVal[0].value : ''
58
-
59
- // Check if value is different from modelValue
60
- if (this.value !== value) {
61
- this.value = value
62
- }
64
+ // Update v-model if value changed
65
+ if (this.value !== value) {
66
+ this.value = value
67
+ }
63
68
 
64
- // If they had an original afterChange, run it
65
- if (config.events && ogAfterChange) {
66
- ogAfterChange(newVal)
69
+ // Call the original afterChange callback if it exists
70
+ if (ogAfterChange) {
71
+ ogAfterChange(newVal)
72
+ }
67
73
  }
68
74
  }
69
75
 
@@ -76,7 +82,21 @@ export default defineComponent({
76
82
  // Check if modelValue is the same as the value of the select
77
83
  if (this.value !== selected) {
78
84
  // If not, set the value of the select to the modelValue
79
- this.slim.setSelected(this.value)
85
+ // Don't trigger afterChange on initial sync
86
+ this.slim.setSelected(this.value, false)
87
+ }
88
+ },
89
+ updated() {
90
+ // After DOM updates (like slot content changes), sync selection from modelValue
91
+ // This ensures :selected attribute isn't needed on slot options
92
+ if (this.slim && !this.data) {
93
+ const currentSelected = this.slim.getSelected()
94
+ const modelValues = Array.isArray(this.value) ? this.value : [this.value]
95
+ const needsSync = JSON.stringify(currentSelected.sort()) !== JSON.stringify(modelValues.sort())
96
+
97
+ if (needsSync) {
98
+ this.slim.setSelected(this.value, false)
99
+ }
80
100
  }
81
101
  },
82
102
  beforeUnmount() {
@@ -87,10 +107,12 @@ export default defineComponent({
87
107
  watch: {
88
108
  modelValue: {
89
109
  handler: function (newVal: string | string[] | undefined) {
110
+ if (!this.slim) return
111
+
90
112
  // Set the value of the select to the newVal
91
- this.slim?.setSelected(this.getCleanValue(newVal))
92
- },
93
- immediate: true
113
+ // Don't trigger afterChange when programmatically updating from parent
114
+ this.slim.setSelected(this.getCleanValue(newVal), false)
115
+ }
94
116
  },
95
117
  data: {
96
118
  handler: function (newData) {
package/tsconfig.json CHANGED
@@ -1,24 +1,26 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "baseUrl": ".",
4
- "rootDir": ".",
4
+ "rootDir": "src",
5
5
  "outDir": "dist",
6
6
  "declaration": true,
7
7
  "target": "esnext",
8
8
  "useDefineForClassFields": true,
9
9
  "module": "esnext",
10
10
  "moduleResolution": "node",
11
+ "jsx": "react-jsx",
11
12
  "strict": true,
12
- "jsx": "preserve",
13
13
  "allowJs": false,
14
+ "skipLibCheck": true,
14
15
  "sourceMap": true,
15
16
  "resolveJsonModule": true,
16
17
  "esModuleInterop": true,
17
18
  "importHelpers": true,
18
19
  "lib": ["esnext", "dom"],
19
20
  "paths": {
20
- "slim-select": ["src/slim-select"]
21
+ "@/*": ["src/*"]
21
22
  }
22
23
  },
23
- "include": ["dist/", "src/**/*.ts", "src/**/*.d.ts"]
24
+ "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts"],
25
+ "exclude": ["dist", "node_modules"]
24
26
  }
@@ -1,11 +1,21 @@
1
1
  import vue from '@vitejs/plugin-vue'
2
2
  import { defineConfig } from 'vite'
3
+ import path from 'path'
3
4
 
4
5
  // https://vitejs.dev/config/
5
6
  export default defineConfig({
7
+ resolve: {
8
+ alias: {
9
+ '@': path.resolve(__dirname, 'src')
10
+ }
11
+ },
12
+ server: {
13
+ port: 1111
14
+ },
6
15
  build: {
7
16
  outDir: 'docs',
8
17
  emptyOutDir: true,
18
+ chunkSizeWarningLimit: 12000,
9
19
  rollupOptions: {
10
20
  output: {
11
21
  entryFileNames: `assets/[name].js`,
@@ -0,0 +1,42 @@
1
+ import { defineConfig } from 'vite'
2
+ import path from 'path'
3
+ import dts from 'vite-plugin-dts'
4
+
5
+ export default defineConfig({
6
+ publicDir: false,
7
+ resolve: {
8
+ alias: {
9
+ '@': path.resolve(__dirname, 'src')
10
+ }
11
+ },
12
+ build: {
13
+ lib: {
14
+ entry: path.resolve(__dirname, 'src/slim-select/index.ts'),
15
+ name: 'SlimSelect',
16
+ formats: ['es', 'cjs', 'umd'],
17
+ fileName: (format) => {
18
+ if (format === 'es') return 'slimselect.es.js'
19
+ if (format === 'cjs') return 'slimselect.cjs.js'
20
+ return 'slimselect.umd.js'
21
+ }
22
+ },
23
+ outDir: path.resolve(__dirname, 'dist'),
24
+ rollupOptions: {
25
+ output: {
26
+ exports: 'named',
27
+ assetFileNames: (assetInfo) => {
28
+ if (assetInfo.name === 'style.css') return 'slimselect.css'
29
+ return assetInfo.name || 'asset'
30
+ }
31
+ }
32
+ }
33
+ },
34
+ plugins: [
35
+ dts({
36
+ rollupTypes: true,
37
+ outDir: path.resolve(__dirname, 'dist'),
38
+ entryRoot: path.resolve(__dirname, 'src/slim-select'),
39
+ exclude: [path.resolve(__dirname, 'src/slim-select/**/*.test.ts')]
40
+ })
41
+ ]
42
+ })
@@ -0,0 +1,40 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import path from 'path'
4
+ import dts from 'vite-plugin-dts'
5
+
6
+ export default defineConfig({
7
+ publicDir: false,
8
+ resolve: {
9
+ alias: {
10
+ '@': path.resolve(__dirname, 'src')
11
+ }
12
+ },
13
+ build: {
14
+ lib: {
15
+ entry: path.resolve(__dirname, 'src/slim-select/react/index.tsx'),
16
+ name: 'SlimSelectReact',
17
+ formats: ['es', 'umd'],
18
+ fileName: (format) => `index.${format === 'es' ? 'js' : 'umd.js'}`
19
+ },
20
+ outDir: 'dist/react',
21
+ rollupOptions: {
22
+ external: ['react', 'react-dom'],
23
+ output: {
24
+ exports: 'named',
25
+ globals: {
26
+ react: 'React',
27
+ 'react-dom': 'ReactDOM'
28
+ }
29
+ }
30
+ }
31
+ },
32
+ plugins: [
33
+ react(),
34
+ dts({
35
+ rollupTypes: true,
36
+ include: ['src/slim-select/react/**/*'],
37
+ exclude: ['src/slim-select/**/*.test.ts', 'src/docs/**/*']
38
+ })
39
+ ]
40
+ })
@@ -0,0 +1,39 @@
1
+ import vue from '@vitejs/plugin-vue'
2
+ import { defineConfig } from 'vite'
3
+ import dts from 'vite-plugin-dts'
4
+ import path from 'path'
5
+
6
+ export default defineConfig({
7
+ publicDir: false,
8
+ resolve: {
9
+ alias: {
10
+ '@': path.resolve(__dirname, 'src')
11
+ }
12
+ },
13
+ build: {
14
+ lib: {
15
+ entry: path.resolve(__dirname, 'src/slim-select/vue/index.ts'),
16
+ name: 'SlimSelectVue',
17
+ formats: ['es', 'umd'],
18
+ fileName: (format) => `index.${format === 'es' ? 'js' : 'umd.js'}`
19
+ },
20
+ outDir: path.resolve(__dirname, 'dist/vue'),
21
+ rollupOptions: {
22
+ external: ['vue'],
23
+ output: {
24
+ exports: 'named',
25
+ globals: {
26
+ vue: 'Vue'
27
+ }
28
+ }
29
+ }
30
+ },
31
+ plugins: [
32
+ vue(),
33
+ dts({
34
+ rollupTypes: true,
35
+ include: ['src/slim-select/vue/**/*'],
36
+ exclude: ['src/slim-select/**/*.test.ts', 'src/docs/**/*']
37
+ })
38
+ ]
39
+ })
@@ -0,0 +1,29 @@
1
+ import { defineConfig } from 'vitest/config'
2
+ import vue from '@vitejs/plugin-vue'
3
+ import path from 'path'
4
+
5
+ export default defineConfig({
6
+ plugins: [vue()],
7
+ resolve: {
8
+ alias: {
9
+ '@': path.resolve(__dirname, 'src')
10
+ }
11
+ },
12
+ test: {
13
+ globals: true,
14
+ environment: 'jsdom',
15
+ include: ['src/**/*.test.ts'],
16
+ coverage: {
17
+ provider: 'v8',
18
+ reporter: ['text', 'json', 'html', 'lcov'],
19
+ include: ['src/slim-select/**/*.ts'],
20
+ exclude: ['src/slim-select/**/*.test.ts', 'src/slim-select/vue/index.ts'],
21
+ thresholds: {
22
+ lines: 75,
23
+ functions: 75,
24
+ branches: 75,
25
+ statements: 75
26
+ }
27
+ }
28
+ }
29
+ })
package/dist/classes.d.ts DELETED
@@ -1,43 +0,0 @@
1
- export default class CssClasses {
2
- main: string;
3
- placeholder: string;
4
- values: string;
5
- single: string;
6
- max: string;
7
- value: string;
8
- valueText: string;
9
- valueDelete: string;
10
- valueOut: string;
11
- deselect: string;
12
- deselectPath: string;
13
- arrow: string;
14
- arrowClose: string;
15
- arrowOpen: string;
16
- content: string;
17
- openAbove: string;
18
- openBelow: string;
19
- search: string;
20
- searchHighlighter: string;
21
- searching: string;
22
- addable: string;
23
- addablePath: string;
24
- list: string;
25
- optgroup: string;
26
- optgroupLabel: string;
27
- optgroupLabelText: string;
28
- optgroupActions: string;
29
- optgroupSelectAll: string;
30
- optgroupSelectAllBox: string;
31
- optgroupSelectAllCheck: string;
32
- optgroupClosable: string;
33
- option: string;
34
- optionDelete: string;
35
- highlighted: string;
36
- open: string;
37
- close: string;
38
- selected: string;
39
- error: string;
40
- disabled: string;
41
- hide: string;
42
- constructor(classes?: Partial<CssClasses>);
43
- }
package/dist/helpers.d.ts DELETED
@@ -1,5 +0,0 @@
1
- export declare function generateID(): string;
2
- export declare function hasClassInTree(element: HTMLElement, className: string): HTMLElement | null;
3
- export declare function debounce<T extends (...args: any[]) => void>(func: T, wait?: number, immediate?: boolean): () => void;
4
- export declare function isEqual(a: any, b: any): boolean;
5
- export declare function kebabCase(str: string): string;
package/dist/render.d.ts DELETED
@@ -1,81 +0,0 @@
1
- import Settings from './settings';
2
- import Store, { DataArray, Option, OptionOptional } from './store';
3
- import CssClasses from './classes';
4
- export interface Callbacks {
5
- open: () => void;
6
- close: () => void;
7
- addable?: (value: string) => Promise<OptionOptional | string> | OptionOptional | string | false | undefined | null | Error;
8
- setSelected: (value: string | string[], runAfterChange: boolean) => void;
9
- addOption: (option: Option) => void;
10
- search: (search: string) => void;
11
- beforeChange?: (newVal: Option[], oldVal: Option[]) => boolean | void;
12
- afterChange?: (newVal: Option[]) => void;
13
- }
14
- export interface Main {
15
- main: HTMLDivElement;
16
- values: HTMLDivElement;
17
- deselect: {
18
- main: HTMLDivElement;
19
- svg: SVGSVGElement;
20
- path: SVGPathElement;
21
- };
22
- arrow: {
23
- main: SVGSVGElement;
24
- path: SVGPathElement;
25
- };
26
- }
27
- export interface Content {
28
- main: HTMLDivElement;
29
- search: Search;
30
- list: HTMLDivElement;
31
- }
32
- export interface Search {
33
- main: HTMLDivElement;
34
- input: HTMLInputElement;
35
- addable?: {
36
- main: HTMLDivElement;
37
- svg: SVGSVGElement;
38
- path: SVGPathElement;
39
- };
40
- }
41
- export default class Render {
42
- settings: Settings;
43
- store: Store;
44
- callbacks: Callbacks;
45
- private lastSelectedOption;
46
- main: Main;
47
- content: Content;
48
- classes: CssClasses;
49
- constructor(settings: Required<Settings>, classes: Required<CssClasses>, store: Store, callbacks: Callbacks);
50
- enable(): void;
51
- disable(): void;
52
- open(): void;
53
- close(): void;
54
- updateClassStyles(): void;
55
- updateAriaAttributes(): void;
56
- mainDiv(): Main;
57
- mainFocus(eventType: string | null): void;
58
- placeholder(): HTMLDivElement;
59
- renderValues(): void;
60
- private renderSingleValue;
61
- private renderMultipleValues;
62
- multipleValue(option: Option): HTMLDivElement;
63
- contentDiv(): Content;
64
- moveContent(): void;
65
- searchDiv(): Search;
66
- searchFocus(): void;
67
- getOptions(notPlaceholder?: boolean, notDisabled?: boolean, notHidden?: boolean): HTMLDivElement[];
68
- highlight(dir: 'up' | 'down'): void;
69
- listDiv(): HTMLDivElement;
70
- renderError(error: string): void;
71
- renderSearching(): void;
72
- renderOptions(data: DataArray): void;
73
- option(option: Option): HTMLDivElement;
74
- destroy(): void;
75
- private highlightText;
76
- moveContentAbove(): void;
77
- moveContentBelow(): void;
78
- ensureElementInView(container: HTMLElement, element: HTMLElement): void;
79
- putContent(): 'up' | 'down';
80
- updateDeselectAll(): void;
81
- }
package/dist/select.d.ts DELETED
@@ -1,30 +0,0 @@
1
- import { DataArray, DataArrayPartial, Optgroup, OptgroupOptional, Option } from './store';
2
- export default class Select {
3
- select: HTMLSelectElement;
4
- onValueChange?: (value: Option[]) => void;
5
- onClassChange?: (classes: string[]) => void;
6
- onDisabledChange?: (disabled: boolean) => void;
7
- onOptionsChange?: (data: DataArrayPartial) => void;
8
- listen: boolean;
9
- private observer;
10
- constructor(select: HTMLSelectElement);
11
- enable(): void;
12
- disable(): void;
13
- hideUI(): void;
14
- showUI(): void;
15
- changeListen(listen: boolean): void;
16
- valueChange(ev: Event): boolean;
17
- private observeCall;
18
- getData(): DataArrayPartial;
19
- getDataFromOptgroup(optgroup: HTMLOptGroupElement): OptgroupOptional;
20
- getDataFromOption(option: HTMLOptionElement): Option;
21
- getSelectedOptions(): Option[];
22
- getSelectedValues(): string[];
23
- setSelected(ids: string[]): void;
24
- setSelectedByValue(values: string[]): void;
25
- updateSelect(id?: string, style?: string, classes?: string[]): void;
26
- updateOptions(data: DataArray): void;
27
- createOptgroup(optgroup: Optgroup): HTMLOptGroupElement;
28
- createOption(info: Option): HTMLOptionElement;
29
- destroy(): void;
30
- }
@@ -1,35 +0,0 @@
1
- export type SettingsPartial = Partial<Settings>;
2
- export default class Settings {
3
- id: string;
4
- style: string;
5
- class: string[];
6
- isMultiple: boolean;
7
- isOpen: boolean;
8
- isFullOpen: boolean;
9
- intervalMove: NodeJS.Timeout | null;
10
- disabled: boolean;
11
- alwaysOpen: boolean;
12
- showSearch: boolean;
13
- focusSearch: boolean;
14
- ariaLabel: string;
15
- searchPlaceholder: string;
16
- searchText: string;
17
- searchingText: string;
18
- searchHighlight: boolean;
19
- closeOnSelect: boolean;
20
- contentLocation: HTMLElement | null;
21
- contentPosition: 'relative' | 'absolute' | 'fixed';
22
- openPosition: 'auto' | 'up' | 'down';
23
- placeholderText: string;
24
- allowDeselect: boolean;
25
- hideSelected: boolean;
26
- keepOrder: boolean;
27
- showOptionTooltips: boolean;
28
- minSelected: number;
29
- maxSelected: number;
30
- timeoutDelay: number;
31
- maxValuesShown: number;
32
- maxValuesMessage: string;
33
- addableText: string;
34
- constructor(settings?: SettingsPartial);
35
- }