jb-select 5.3.2 → 6.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 (69) hide show
  1. package/README.md +67 -29
  2. package/dist/index.cjs.js +2 -0
  3. package/dist/index.cjs.js.br +0 -0
  4. package/dist/index.cjs.js.gz +0 -0
  5. package/dist/index.cjs.js.map +1 -0
  6. package/dist/index.js +2 -0
  7. package/dist/index.js.br +0 -0
  8. package/dist/index.js.gz +0 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/index.umd.js +2 -0
  11. package/dist/index.umd.js.br +0 -0
  12. package/dist/index.umd.js.gz +0 -0
  13. package/dist/index.umd.js.map +1 -0
  14. package/dist/web-component/jb-select/lib/index.d.ts +7 -0
  15. package/dist/web-component/jb-select/lib/jb-option/jb-option.d.ts +21 -0
  16. package/dist/web-component/jb-select/lib/jb-option/render.d.ts +1 -0
  17. package/dist/web-component/jb-select/lib/jb-option/types.d.ts +4 -0
  18. package/dist/web-component/jb-select/lib/jb-option-list/jb-option-list.d.ts +10 -0
  19. package/dist/web-component/jb-select/lib/jb-option-list/types.d.ts +5 -0
  20. package/dist/web-component/jb-select/lib/jb-select.d.ts +3 -7
  21. package/dist/web-component/jb-select/lib/types.d.ts +6 -10
  22. package/index.js +2 -2
  23. package/lib/index.ts +8 -0
  24. package/lib/jb-option/jb-option.scss +23 -0
  25. package/lib/jb-option/jb-option.ts +136 -0
  26. package/lib/jb-option/render.ts +9 -0
  27. package/lib/jb-option/types.ts +4 -0
  28. package/lib/jb-option-list/jb-option-list.ts +125 -0
  29. package/lib/jb-option-list/types.ts +5 -0
  30. package/lib/jb-select.html +1 -1
  31. package/lib/jb-select.scss +36 -45
  32. package/lib/jb-select.ts +165 -238
  33. package/lib/types.ts +8 -9
  34. package/package.json +5 -3
  35. package/react/README.md +188 -0
  36. package/react/dist/common/hooks/use-event.d.ts +3 -0
  37. package/react/dist/common/hooks/useLazyRef.d.ts +4 -0
  38. package/react/dist/common/hooks/useMobx.d.ts +4 -0
  39. package/react/dist/common/scripts/device-detection.d.ts +1 -0
  40. package/react/dist/common/scripts/persian-helper.d.ts +3 -0
  41. package/react/dist/index.cjs.js +122 -0
  42. package/react/dist/index.cjs.js.map +1 -0
  43. package/react/dist/index.js +118 -0
  44. package/react/dist/index.js.map +1 -0
  45. package/react/dist/index.umd.js +125 -0
  46. package/react/dist/index.umd.js.map +1 -0
  47. package/react/dist/web-component/jb-select/react/lib/JBOption.d.ts +19 -0
  48. package/react/dist/web-component/jb-select/react/lib/JBOptionList.d.ts +22 -0
  49. package/react/dist/web-component/jb-select/react/lib/JBSelect.d.ts +38 -0
  50. package/react/dist/web-component/jb-select/react/lib/index.d.ts +3 -0
  51. package/react/index.js +1 -0
  52. package/react/lib/JBOption.tsx +45 -0
  53. package/react/lib/JBOptionList.tsx +59 -0
  54. package/react/lib/JBSelect.tsx +105 -0
  55. package/react/lib/index.tsx +3 -0
  56. package/react/package.json +33 -0
  57. package/react/tsconfig.json +19 -0
  58. package/dist/jb-select.cjs.js +0 -2
  59. package/dist/jb-select.cjs.js.br +0 -0
  60. package/dist/jb-select.cjs.js.gz +0 -0
  61. package/dist/jb-select.cjs.js.map +0 -1
  62. package/dist/jb-select.js +0 -2
  63. package/dist/jb-select.js.br +0 -0
  64. package/dist/jb-select.js.gz +0 -0
  65. package/dist/jb-select.js.map +0 -1
  66. package/dist/jb-select.umd.js +0 -2
  67. package/dist/jb-select.umd.js.br +0 -0
  68. package/dist/jb-select.umd.js.gz +0 -0
  69. package/dist/jb-select.umd.js.map +0 -1
@@ -0,0 +1,136 @@
1
+ import { type JBSelectWebComponent } from '../jb-select';
2
+ import CSS from './jb-option.scss';
3
+ import { renderHTML } from "./render";
4
+ import { JBOptionElements } from "./types";
5
+
6
+ //TODO: check for filter text to set visibility on mount
7
+ export class JBOptionWebComponent<TValue> extends HTMLElement {
8
+
9
+ #elements: JBOptionElements;
10
+ // it may be empty
11
+ #SelectElement?: JBSelectWebComponent
12
+ #value: TValue;
13
+ get value(): TValue {
14
+ return this.#value;
15
+ }
16
+ set value(value: TValue) {
17
+ this.#value = value;
18
+ }
19
+ #selected = false;
20
+ set selected(value: boolean) {
21
+ this.#selected = value;
22
+ this.#elements.componentWrapper.classList.add("--selected");
23
+ }
24
+ get selected() {
25
+ return this.#selected;
26
+ }
27
+ get optionContent():Node[]{
28
+ const optionNodes = this.#elements.contentWrapper.querySelector("slot").assignedNodes();
29
+ return optionNodes;
30
+ }
31
+ //TODO: add search hidden property for more accurate hidden and more personalized logic
32
+ #hidden = false;
33
+ get hidden(){
34
+ return this.#hidden;
35
+ }
36
+ set hidden(value:boolean){
37
+ this.#hidden = value;
38
+ if(value){
39
+ this.#elements.componentWrapper.classList.add('--hidden');
40
+ }else{
41
+ this.#elements.componentWrapper.classList.remove('--hidden');
42
+ }
43
+ }
44
+ /**
45
+ * return text content of option (it used in search by default to filter option)
46
+ */
47
+ get optionContentText(){
48
+ const optionTextContent = this.optionContent.reduce((acc,item)=>{
49
+ acc += item.textContent;
50
+ return acc;
51
+ },"");
52
+ return optionTextContent;
53
+ }
54
+ constructor() {
55
+ super();
56
+ this.#initWebComponent();
57
+ this.#initProp();
58
+ }
59
+ connectedCallback() {
60
+ // standard web component event that called when all of dom is bounded
61
+ this.#dispatchPlaceEvent();
62
+
63
+ }
64
+ setSelectElement(element: JBSelectWebComponent) {
65
+ if (element) {
66
+ this.#SelectElement = element;
67
+ this.#SelectElement.addEventListener("filter-change", this.#onFilterChange.bind(this));
68
+ }
69
+ }
70
+ #onFilterChange(e: CustomEvent){
71
+ const {filterText} = e.detail;
72
+ const optionTextContent = this.optionContentText;
73
+ if(optionTextContent.includes(filterText)){
74
+ this.hidden = false;
75
+ }else{
76
+ this.hidden = true;
77
+ }
78
+ }
79
+ disconnectedCallback() {
80
+ this.#SelectElement?.removeEventListener("filter-change", this.#onFilterChange.bind(this));
81
+ const event = new CustomEvent("jb-option-disconnected",{bubbles:true,composed:true,cancelable:false});
82
+ this.dispatchEvent(event);
83
+ }
84
+ #initWebComponent() {
85
+ const shadowRoot = this.attachShadow({
86
+ mode: "open",
87
+ });
88
+ const html = `<style>${CSS}</style>` + "\n" + renderHTML();
89
+ const element = document.createElement("template");
90
+ element.innerHTML = html;
91
+ shadowRoot.appendChild(element.content.cloneNode(true));
92
+ this.#elements = {
93
+ componentWrapper: shadowRoot.querySelector(".jb-options-web-component")!,
94
+ contentWrapper: shadowRoot.querySelector(".option-content-wrapper")!,
95
+ };
96
+ this.#registerEventListener();
97
+ }
98
+ #registerEventListener() {
99
+ this.#elements.componentWrapper.addEventListener("click", this.#onOptionClick.bind(this));
100
+ }
101
+ //this event called on each connectedCallback so select could find it's option
102
+ #dispatchPlaceEvent() {
103
+ const event = new CustomEvent("jb-option-connected", { bubbles: true, composed: true });
104
+ this.dispatchEvent(event);
105
+ }
106
+ #initProp() {
107
+ this.value = this.getAttribute("value") as TValue || null;
108
+ }
109
+ static get observedAttributes() {
110
+ return ["value"];
111
+ }
112
+ attributeChangedCallback(name: string, oldValue: string, newValue: string) {
113
+ // do something when an attribute has changed
114
+ this.#onAttributeChange(name, newValue);
115
+ }
116
+ #onAttributeChange(name: string, value: string) {
117
+ switch (name) {
118
+ case 'value':
119
+ this.#value = value as TValue;
120
+ }
121
+ }
122
+ #onOptionClick() {
123
+ if (!this.#selected) {
124
+ this.#dispatchSelectEvent();
125
+ }
126
+ }
127
+ #dispatchSelectEvent() {
128
+ const event = new CustomEvent("select", { bubbles: true, cancelable: false, composed: true });
129
+ this.dispatchEvent(event);
130
+ }
131
+ }
132
+ const myElementNotExists = !customElements.get("jb-option");
133
+ if (myElementNotExists) {
134
+ //prevent duplicate registering
135
+ window.customElements.define("jb-option", JBOptionWebComponent);
136
+ }
@@ -0,0 +1,9 @@
1
+ export function renderHTML(): string {
2
+ return /* html */ `
3
+ <div class="jb-options-web-component">
4
+ <div class="option-content-wrapper">
5
+ <slot></slot>
6
+ </div>
7
+ </div>
8
+ `;
9
+ }
@@ -0,0 +1,4 @@
1
+ export type JBOptionElements = {
2
+ componentWrapper:HTMLDivElement,
3
+ contentWrapper:HTMLDivElement,
4
+ }
@@ -0,0 +1,125 @@
1
+ import { JBOptionWebComponent } from "../jb-option/jb-option";
2
+ import { type OptionListCallbacks } from "./types";
3
+
4
+ //TOption is the type of option, TValue is the type of value we extract from option
5
+ export class JBOptionListWebComponent<TOption, TValue> extends HTMLElement {
6
+ #callbacks: OptionListCallbacks<TOption, TValue> = {};
7
+ get callbacks() {
8
+ return this.#callbacks;
9
+ }
10
+ #optionList: TOption[] = [];
11
+ #optionPairMap: Map<TOption, JBOptionWebComponent<TValue>> = new Map();
12
+ get optionList() {
13
+ return this.#optionList || [];
14
+ }
15
+ set optionList(value) {
16
+ if (!Array.isArray(value)) {
17
+ console.error(
18
+ "your provided option list to jb-option-list is not a array. you must provide array value",
19
+ { value }
20
+ );
21
+ return;
22
+ }
23
+ this.#optionList = value;
24
+ this.#initOptionList(value);
25
+ }
26
+ constructor() {
27
+ super();
28
+ this.#initWebComponent();
29
+ }
30
+ connectedCallback() {
31
+ // standard web component event that called when all of dom is bounded
32
+ }
33
+ setCallback<T extends keyof OptionListCallbacks<TOption, TValue>>(key: T, callbackFn: OptionListCallbacks<TOption, TValue>[T]) {
34
+ this.#callbacks[key] = callbackFn;
35
+ switch (key) {
36
+ case 'getContentDOM':
37
+ case 'getTitle':
38
+ this.#updateOptionsContent();
39
+ break;
40
+ case 'getValue':
41
+ this.#updateOptionsValue();
42
+ }
43
+ }
44
+ #updateOptionsContent() {
45
+ this.#optionPairMap.forEach((dom, option) => {
46
+ this.#fillOptionContent(option, dom);
47
+ });
48
+ }
49
+ #updateOptionsValue() {
50
+ this.#optionPairMap.forEach((dom, option) => {
51
+ dom.value = this.#getOptionValue(option);
52
+ });
53
+ }
54
+ #initWebComponent() {
55
+ const shadowRoot = this.attachShadow({
56
+ mode: "open",
57
+ });
58
+ const element = document.createElement("template");
59
+
60
+ shadowRoot.appendChild(element.content.cloneNode(true));
61
+ }
62
+ //
63
+ #initOptionList(optionList: TOption[]) {
64
+ this.shadowRoot.innerHTML = "";
65
+ optionList.forEach((option) => {
66
+ const dom = this.#createOptionDOM(option);
67
+ this.shadowRoot.appendChild(dom);
68
+ this.#optionPairMap.set(option, dom);
69
+ });
70
+ }
71
+ #getOptionValue(option: TOption): TValue {
72
+ if (this.callbacks.getValue && typeof this.callbacks.getValue !== "function") {
73
+ console.error("getOptionValue callback is not a function");
74
+ }
75
+ try {
76
+ if (typeof this.#callbacks.getValue == "function") {
77
+ return this.#callbacks.getValue(option);
78
+ } else {
79
+ return option as unknown as TValue;
80
+ }
81
+ } catch (e) {
82
+ console.error(
83
+ `Invalid getOptionValue callback Result, must be a function that returns the value of an option`,
84
+ option
85
+ );
86
+ }
87
+ }
88
+ #getOptionTitle(option: TOption): string {
89
+ if (typeof this.#callbacks.getTitle == "function") {
90
+ try {
91
+ return this.#callbacks.getTitle(option);
92
+ } catch (e) {
93
+ console.error(
94
+ `Invalid getOptionTitle callback Result, must be a function that returns the value of an option`,
95
+ option
96
+ );
97
+ }
98
+ } else {
99
+ return String(option);
100
+ }
101
+ return "";
102
+ }
103
+ #fillOptionContent(option: TOption, element: JBOptionWebComponent<TValue>) {
104
+ element.innerHTML = "";
105
+ if (typeof this.#callbacks.getContentDOM == "function") {
106
+ element.appendChild(this.#callbacks.getContentDOM(option));
107
+ } else {
108
+ element.innerHTML = this.#getOptionTitle(option);
109
+ }
110
+ }
111
+ #createOptionDOM(item: TOption): JBOptionWebComponent<TValue> {
112
+ // const optionElement = document.createElement("jb-option") as JBOptionWebComponent<TValue>;
113
+ const optionElement = new JBOptionWebComponent<TValue>();
114
+ //it has default function who return exact same input
115
+ this.#fillOptionContent(item, optionElement);
116
+ optionElement.value = this.#getOptionValue(item);
117
+ return optionElement;
118
+ }
119
+
120
+ }
121
+ const myElementNotExists = !customElements.get("jb-option-list");
122
+ if (myElementNotExists) {
123
+ //prevent duplicate registering
124
+ window.customElements.define("jb-option-list", JBOptionListWebComponent);
125
+ }
@@ -0,0 +1,5 @@
1
+ export type OptionListCallbacks<TOption,TValue> = {
2
+ getTitle?:(option:TOption)=>string,
3
+ getValue?:(option:TOption)=>TValue
4
+ getContentDOM?:((option:TOption) => HTMLElement);
5
+ }
@@ -40,7 +40,7 @@
40
40
  </div>
41
41
  <div class="select-list-wrapper">
42
42
  <div class="select-list" tabindex="-1">
43
-
43
+ <slot></slot>
44
44
  </div>
45
45
  <div class="empty-list-placeholder">
46
46
  <slot name="empty-list-message">no item available</slot>
@@ -7,7 +7,7 @@
7
7
  --p-border-bottom-width: var(--jb-select-border-bottom-width, var(--jb-select-border-width, 3px));
8
8
  --p-base-z-index: 1;
9
9
  --p-mobile-modal-z-index: 900;
10
- --p-mobile-modal-height:var(--jb-select-mobile-modal-height, 100vh);
10
+ --p-mobile-modal-height: var(--jb-select-mobile-modal-height, 100vh);
11
11
  }
12
12
 
13
13
  .jb-select-web-component {
@@ -20,7 +20,7 @@
20
20
  @include mobile-tablet {
21
21
  position: fixed;
22
22
  bottom: 0;
23
- top:initial;
23
+ top: initial;
24
24
  left: 0;
25
25
  background-color: var(--jb-select-overlay-bgcolor, #0008);
26
26
  width: 100vw;
@@ -38,10 +38,8 @@
38
38
  border-width: var(--jb-select-mobile-search-border-width, var(--jb-select-border-width, 1px));
39
39
  border-color: var(--jb-select-mobile-search-border-color, var(--jb-select-border-color, #f7f6f6));
40
40
  border-bottom-width: var(--jb-select-mobile-search-border-bottom-width, var(--p-border-bottom-width));
41
- border-bottom-color: var(
42
- --jb-select-mobile-search-border-bottom-color,
43
- var(--jb-select-border-bottom-color, var(--jb-select-border-color, #f7f6f6))
44
- );
41
+ border-bottom-color: var(--jb-select-mobile-search-border-bottom-color,
42
+ var(--jb-select-border-bottom-color, var(--jb-select-border-color, #f7f6f6)));
45
43
  border-radius: var(--jb-select-mobile-search-border-radius, var(--jb-select-border-radius, 16px));
46
44
  }
47
45
 
@@ -59,8 +57,7 @@
59
57
  transition: none;
60
58
  }
61
59
 
62
- .selected-value {
63
- }
60
+ .selected-value {}
64
61
  }
65
62
  }
66
63
 
@@ -119,6 +116,7 @@
119
116
  }
120
117
 
121
118
  &.--has-value {
119
+
122
120
  //if user select a option and value is setted and not null
123
121
  .select-box {
124
122
  border-color: var(--jb-select-border-color-selected, #c3ff14);
@@ -159,46 +157,51 @@
159
157
  border-bottom: solid var(--p-border-bottom-width) var(--jb-select-border-color, #f7f6f6);
160
158
  border-radius: var(--jb-select-border-radius, 1rem);
161
159
  background-color: var(--jb-select-bgcolor, #f7f6f6);
162
- margin:var(--jb-select-select-box-margin, 4px 0px 0px 0px);
160
+ margin: var(--jb-select-select-box-margin, 4px 0px 0px 0px);
163
161
  overflow: hidden;
164
162
  display: flex;
165
163
  padding-inline-end: var(--jb-select-box-padding-end, 1rem);
166
164
  gap: 0.5rem;
167
165
  align-items: center;
166
+
168
167
  &:focus-within {
169
168
  border-color: var(--jb-select-border-color, var(--p-p-color));
170
169
  border-bottom-color: var(--jb-select-border-color, var(--p-p-color));
171
170
  border-radius: var(--jb-select-border-radius, 1rem) var(--jb-select-border-radius, 1rem) 0 0;
171
+
172
172
  @include mobile-tablet {
173
173
  border-radius: var(--jb-select-mobile-search-border-radius, var(--jb-select-border-radius, 1rem));
174
174
  }
175
175
  }
176
- .start-section{
176
+
177
+ .start-section {
177
178
  height: 100%;
178
179
  width: auto;
179
180
  display: flex;
180
181
  justify-content: center;
181
182
  align-items: center;
182
183
  }
183
- .middle-section{
184
+
185
+ .middle-section {
184
186
  position: relative;
185
187
  width: 100%;
186
188
  height: 100%;
187
- flex:1;
189
+ flex: 1;
190
+
188
191
  .selected-value-wrapper {
189
192
  position: absolute;
190
- top:0;
193
+ top: 0;
191
194
  left: 0;
192
195
  width: 100%;
193
196
  height: 100%;
194
197
  border-radius: inherit;
195
198
  overflow: hidden;
196
199
  z-index: 1;
197
-
200
+
198
201
  &.--search-typed {
199
202
  opacity: 0;
200
203
  }
201
-
204
+
202
205
  .selected-value {
203
206
  width: 100%;
204
207
  box-sizing: border-box;
@@ -215,15 +218,17 @@
215
218
  align-items: center;
216
219
  }
217
220
  }
221
+
218
222
  .front-box {
219
223
  position: absolute;
220
- top:0;
224
+ top: 0;
221
225
  left: 0;
222
226
  width: 100%;
223
227
  height: 100%;
224
228
  border-radius: inherit;
225
229
  overflow: hidden;
226
230
  z-index: 2;
231
+
227
232
  input {
228
233
  border: none;
229
234
  width: 100%;
@@ -233,15 +238,15 @@
233
238
  padding: 2px 12px 0 12px;
234
239
  display: block;
235
240
  font-family: inherit;
236
- font-size: var(--jb-select-value-font-size,1.1rem);
241
+ font-size: var(--jb-select-value-font-size, 1.1rem);
237
242
  color: var(--jb-select-input-color, #1f1735);
238
243
  margin: 0;
239
244
  border-radius: 0;
240
-
245
+
241
246
  &:focus {
242
247
  outline: none;
243
248
  }
244
-
249
+
245
250
  &::placeholder {
246
251
  color: var(--jb-select-placeholder-color, initial);
247
252
  font-size: var(--jb-select-placeholder-font-size, 1.1em);
@@ -249,12 +254,13 @@
249
254
  }
250
255
  }
251
256
  }
252
- .end-section{
257
+
258
+ .end-section {
253
259
  .arrow-icon {
254
260
  margin: var(--jb-select-arrow-icon-margin, 0 0 0 0);
255
261
  }
256
262
  }
257
-
263
+
258
264
 
259
265
  &:focus-within .selected-value {
260
266
  opacity: 0.7;
@@ -314,40 +320,25 @@
314
320
  width: 100%;
315
321
  max-height: var(--jb-select-list-max-height, 400px);
316
322
  overflow-y: auto;
317
- padding: var(--jb-select-list-padding, 16px 0);
318
-
319
- &:empty {
320
- padding: 0;
323
+ slot {
324
+ padding: var(--jb-select-list-padding, 16px 0);
325
+ &:empty {
326
+ padding: 0;
327
+ }
321
328
  }
322
329
 
330
+
323
331
  @include mobile-tablet {
324
332
  max-height: calc(var(--p-mobile-modal-height) - 240px);
325
333
  }
326
334
 
327
- .select-option {
328
- min-height: 36px;
329
- padding: 4px 16px;
330
- display: flex;
331
- align-items: center;
332
- font-size: 0.9em;
333
- color: var(--jb-select-option-color, inherit);
334
- background-color: var(--jb-select-option-background-color, transparent);
335
-
336
- &:hover {
337
- background-color: var(--jb-select-option-background-color-hover, #1073db);
338
- color: var(--jb-select-option-color-hover, #fff);
339
- cursor: pointer;
340
- }
335
+ /* option style places */
341
336
 
342
- &.--selected-option {
343
- font-weight: 900;
344
- }
345
- }
346
337
  &::-webkit-scrollbar {
347
338
  width: 9px;
348
339
  background-color: transparent;
349
340
  }
350
-
341
+
351
342
  &::-webkit-scrollbar-thumb {
352
343
  background-color: var(--jb-select-list-scroll-color, #c3c3c3);
353
344
  border-radius: var(--jb-select-list-scroll-border-radius, 4px);
@@ -367,4 +358,4 @@
367
358
  }
368
359
 
369
360
  }
370
- }
361
+ }