shadcn_phlexcomponents 0.1.14 → 0.1.17
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.
- checksums.yaml +4 -4
- data/app/javascript/controllers/accordion_controller.js +107 -0
- data/app/javascript/controllers/alert_dialog_controller.js +7 -0
- data/app/javascript/controllers/avatar_controller.js +14 -0
- data/app/javascript/controllers/checkbox_controller.js +29 -0
- data/app/javascript/controllers/collapsible_controller.js +39 -0
- data/app/javascript/controllers/combobox_controller.js +278 -0
- data/app/javascript/controllers/command_controller.js +207 -0
- data/app/javascript/controllers/date_picker_controller.js +258 -0
- data/app/javascript/controllers/date_range_picker_controller.js +200 -0
- data/app/javascript/controllers/dialog_controller.js +83 -0
- data/app/javascript/controllers/dropdown_menu_controller.js +238 -0
- data/app/javascript/controllers/dropdown_menu_sub_controller.js +118 -0
- data/app/javascript/controllers/form_field_controller.js +20 -0
- data/app/javascript/controllers/hover_card_controller.js +73 -0
- data/app/javascript/controllers/loading_button_controller.js +14 -0
- data/app/javascript/controllers/popover_controller.js +90 -0
- data/app/javascript/controllers/progress_controller.js +14 -0
- data/app/javascript/controllers/radio_group_controller.js +80 -0
- data/app/javascript/controllers/select_controller.js +265 -0
- data/app/javascript/controllers/sidebar_controller.js +29 -0
- data/app/javascript/controllers/sidebar_trigger_controller.js +15 -0
- data/app/javascript/controllers/slider_controller.js +82 -0
- data/app/javascript/controllers/switch_controller.js +26 -0
- data/app/javascript/controllers/tabs_controller.js +66 -0
- data/app/javascript/controllers/theme_switcher_controller.js +32 -0
- data/app/javascript/controllers/toast_container_controller.js +48 -0
- data/app/javascript/controllers/toast_controller.js +22 -0
- data/app/javascript/controllers/toggle_controller.js +20 -0
- data/app/javascript/controllers/toggle_group_controller.js +20 -0
- data/app/javascript/controllers/tooltip_controller.js +79 -0
- data/app/javascript/shadcn_phlexcomponents.js +60 -0
- data/app/javascript/utils/command.js +448 -0
- data/app/javascript/utils/floating_ui.js +160 -0
- data/app/javascript/utils/index.js +288 -0
- data/app/{javascript → typescript}/utils/index.ts +7 -0
- data/lib/install/install_shadcn_phlexcomponents.rb +10 -3
- data/lib/shadcn_phlexcomponents/components/combobox.rb +2 -6
- data/lib/shadcn_phlexcomponents/version.rb +1 -1
- metadata +69 -35
- /data/app/{javascript → typescript}/controllers/accordion_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/alert_dialog_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/avatar_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/checkbox_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/collapsible_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/combobox_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/command_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/date_picker_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/date_range_picker_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/dialog_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/dropdown_menu_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/dropdown_menu_sub_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/form_field_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/hover_card_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/loading_button_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/popover_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/progress_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/radio_group_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/select_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/sidebar_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/sidebar_trigger_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/slider_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/switch_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/tabs_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/theme_switcher_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/toast_container_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/toast_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/toggle_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/toggle_group_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/tooltip_controller.ts +0 -0
- /data/app/{javascript → typescript}/shadcn_phlexcomponents.ts +0 -0
- /data/app/{javascript → typescript}/utils/command.ts +0 -0
- /data/app/{javascript → typescript}/utils/floating_ui.ts +0 -0
@@ -0,0 +1,160 @@
|
|
1
|
+
import {
|
2
|
+
computePosition,
|
3
|
+
flip,
|
4
|
+
shift,
|
5
|
+
offset,
|
6
|
+
autoUpdate,
|
7
|
+
size,
|
8
|
+
arrow,
|
9
|
+
} from "@floating-ui/dom";
|
10
|
+
const OPPOSITE_SIDE = {
|
11
|
+
top: "bottom",
|
12
|
+
right: "left",
|
13
|
+
bottom: "top",
|
14
|
+
left: "right",
|
15
|
+
};
|
16
|
+
const ARROW_TRANSFORM_ORIGIN = {
|
17
|
+
top: "",
|
18
|
+
right: "0 0",
|
19
|
+
bottom: "center 0",
|
20
|
+
left: "100% 0",
|
21
|
+
};
|
22
|
+
const ARROW_TRANSFORM = {
|
23
|
+
top: "translateY(100%)",
|
24
|
+
right: "translateY(50%) rotate(90deg) translateX(-50%)",
|
25
|
+
bottom: `rotate(180deg)`,
|
26
|
+
left: "translateY(50%) rotate(-90deg) translateX(50%)",
|
27
|
+
};
|
28
|
+
const initFloatingUi = ({
|
29
|
+
referenceElement,
|
30
|
+
floatingElement,
|
31
|
+
side = "bottom",
|
32
|
+
align = "center",
|
33
|
+
sideOffset = 0,
|
34
|
+
alignOffset = 0,
|
35
|
+
arrowElement,
|
36
|
+
}) => {
|
37
|
+
let placement = `${side}-${align}`;
|
38
|
+
placement = placement.replace(/-center/g, "");
|
39
|
+
let arrowHeight = 0,
|
40
|
+
arrowWidth = 0;
|
41
|
+
if (arrowElement) {
|
42
|
+
const rect = arrowElement.getBoundingClientRect();
|
43
|
+
arrowWidth = rect.width;
|
44
|
+
arrowHeight = rect.height;
|
45
|
+
}
|
46
|
+
const middleware = [
|
47
|
+
transformOrigin({ arrowHeight, arrowWidth }),
|
48
|
+
offset({ mainAxis: sideOffset, alignmentAxis: alignOffset }),
|
49
|
+
size({
|
50
|
+
apply: ({ elements, rects, availableWidth, availableHeight }) => {
|
51
|
+
const { width: anchorWidth, height: anchorHeight } = rects.reference;
|
52
|
+
const contentStyle = elements.floating.style;
|
53
|
+
contentStyle.setProperty(
|
54
|
+
"--radix-popper-available-width",
|
55
|
+
`${availableWidth}px`,
|
56
|
+
);
|
57
|
+
contentStyle.setProperty(
|
58
|
+
"--radix-popper-available-height",
|
59
|
+
`${availableHeight}px`,
|
60
|
+
);
|
61
|
+
contentStyle.setProperty(
|
62
|
+
"--radix-popper-anchor-width",
|
63
|
+
`${anchorWidth}px`,
|
64
|
+
);
|
65
|
+
contentStyle.setProperty(
|
66
|
+
"--radix-popper-anchor-height",
|
67
|
+
`${anchorHeight}px`,
|
68
|
+
);
|
69
|
+
},
|
70
|
+
}),
|
71
|
+
];
|
72
|
+
const flipMiddleware = flip({
|
73
|
+
// Ensure we flip to the perpendicular axis if it doesn't fit
|
74
|
+
// on narrow viewports.
|
75
|
+
crossAxis: "alignment",
|
76
|
+
fallbackAxisSideDirection: "end", // or 'start'
|
77
|
+
});
|
78
|
+
const shiftMiddleware = shift();
|
79
|
+
// Prioritize flip over shift for edge-aligned placements only.
|
80
|
+
if (placement.includes("-")) {
|
81
|
+
middleware.push(flipMiddleware, shiftMiddleware);
|
82
|
+
} else {
|
83
|
+
middleware.push(shiftMiddleware, flipMiddleware);
|
84
|
+
}
|
85
|
+
if (arrowElement) {
|
86
|
+
middleware.push(arrow({ element: arrowElement, padding: 0 }));
|
87
|
+
}
|
88
|
+
return autoUpdate(referenceElement, floatingElement, () => {
|
89
|
+
computePosition(referenceElement, floatingElement, {
|
90
|
+
placement: placement,
|
91
|
+
strategy: "fixed",
|
92
|
+
middleware,
|
93
|
+
}).then(({ middlewareData, x, y }) => {
|
94
|
+
const arrowX = middlewareData.arrow?.x;
|
95
|
+
const arrowY = middlewareData.arrow?.y;
|
96
|
+
const cannotCenterArrow = middlewareData.arrow?.centerOffset !== 0;
|
97
|
+
floatingElement.style.setProperty(
|
98
|
+
"--radix-popper-transform-origin",
|
99
|
+
`${middlewareData.transformOrigin?.x} ${middlewareData.transformOrigin?.y}`,
|
100
|
+
);
|
101
|
+
if (arrowElement) {
|
102
|
+
const baseSide = OPPOSITE_SIDE[side];
|
103
|
+
const arrowStyle = {
|
104
|
+
position: "absolute",
|
105
|
+
left: arrowX ? `${arrowX}px` : undefined,
|
106
|
+
top: arrowY ? `${arrowY}px` : undefined,
|
107
|
+
[baseSide]: 0,
|
108
|
+
transformOrigin: ARROW_TRANSFORM_ORIGIN[side],
|
109
|
+
transform: ARROW_TRANSFORM[side],
|
110
|
+
visibility: cannotCenterArrow ? "hidden" : undefined,
|
111
|
+
};
|
112
|
+
Object.assign(arrowElement.style, arrowStyle);
|
113
|
+
}
|
114
|
+
Object.assign(floatingElement.style, {
|
115
|
+
left: `${x}px`,
|
116
|
+
top: `${y}px`,
|
117
|
+
});
|
118
|
+
});
|
119
|
+
});
|
120
|
+
};
|
121
|
+
const transformOrigin = (options) => {
|
122
|
+
return {
|
123
|
+
name: "transformOrigin",
|
124
|
+
options,
|
125
|
+
fn(data) {
|
126
|
+
const { placement, rects, middlewareData } = data;
|
127
|
+
const cannotCenterArrow = middlewareData.arrow?.centerOffset !== 0;
|
128
|
+
const isArrowHidden = cannotCenterArrow;
|
129
|
+
const arrowWidth = isArrowHidden ? 0 : options.arrowWidth;
|
130
|
+
const arrowHeight = isArrowHidden ? 0 : options.arrowHeight;
|
131
|
+
const [placedSide, placedAlign] = getSideAndAlignFromPlacement(placement);
|
132
|
+
const noArrowAlign = { start: "0%", center: "50%", end: "100%" }[
|
133
|
+
placedAlign
|
134
|
+
];
|
135
|
+
const arrowXCenter = (middlewareData.arrow?.x ?? 0) + arrowWidth / 2;
|
136
|
+
const arrowYCenter = (middlewareData.arrow?.y ?? 0) + arrowHeight / 2;
|
137
|
+
let x = "";
|
138
|
+
let y = "";
|
139
|
+
if (placedSide === "bottom") {
|
140
|
+
x = isArrowHidden ? noArrowAlign : `${arrowXCenter}px`;
|
141
|
+
y = `${-arrowHeight}px`;
|
142
|
+
} else if (placedSide === "top") {
|
143
|
+
x = isArrowHidden ? noArrowAlign : `${arrowXCenter}px`;
|
144
|
+
y = `${rects.floating.height + arrowHeight}px`;
|
145
|
+
} else if (placedSide === "right") {
|
146
|
+
x = `${-arrowHeight}px`;
|
147
|
+
y = isArrowHidden ? noArrowAlign : `${arrowYCenter}px`;
|
148
|
+
} else if (placedSide === "left") {
|
149
|
+
x = `${rects.floating.width + arrowHeight}px`;
|
150
|
+
y = isArrowHidden ? noArrowAlign : `${arrowYCenter}px`;
|
151
|
+
}
|
152
|
+
return { data: { x, y } };
|
153
|
+
},
|
154
|
+
};
|
155
|
+
};
|
156
|
+
function getSideAndAlignFromPlacement(placement) {
|
157
|
+
const [side, align = "center"] = placement.split("-");
|
158
|
+
return [side, align];
|
159
|
+
}
|
160
|
+
export { initFloatingUi };
|
@@ -0,0 +1,288 @@
|
|
1
|
+
const ANIMATION_OUT_DELAY = 100;
|
2
|
+
const ON_OPEN_FOCUS_DELAY = 100;
|
3
|
+
const ON_CLOSE_FOCUS_DELAY = 50;
|
4
|
+
const getScrollbarWidth = () => {
|
5
|
+
// Create a temporary div container and append it into the body
|
6
|
+
const outer = document.createElement("div");
|
7
|
+
outer.style.visibility = "hidden";
|
8
|
+
outer.style.overflow = "scroll"; // force scrollbars
|
9
|
+
outer.style.width = "100px";
|
10
|
+
outer.style.position = "absolute";
|
11
|
+
outer.style.top = "-9999px";
|
12
|
+
document.body.appendChild(outer);
|
13
|
+
// Create an inner div and place it inside the outer div
|
14
|
+
const inner = document.createElement("div");
|
15
|
+
inner.style.width = "100%";
|
16
|
+
outer.appendChild(inner);
|
17
|
+
// Calculate the scrollbar width
|
18
|
+
const scrollbarWidth = outer.offsetWidth - inner.offsetWidth;
|
19
|
+
// Clean up
|
20
|
+
outer.remove();
|
21
|
+
return scrollbarWidth;
|
22
|
+
};
|
23
|
+
const lockScroll = (contentId) => {
|
24
|
+
if (window.innerHeight < document.documentElement.scrollHeight) {
|
25
|
+
document.body.dataset.scrollLocked = "1";
|
26
|
+
document.body.classList.add(
|
27
|
+
"data-[scroll-locked]:pointer-events-none",
|
28
|
+
"data-[scroll-locked]:!overflow-hidden",
|
29
|
+
"data-[scroll-locked]:!relative",
|
30
|
+
"data-[scroll-locked]:px-0",
|
31
|
+
"data-[scroll-locked]:pt-0",
|
32
|
+
"data-[scroll-locked]:ml-0",
|
33
|
+
"data-[scroll-locked]:mt-0",
|
34
|
+
);
|
35
|
+
document.body.style.marginRight = `${getScrollbarWidth()}px`;
|
36
|
+
const contentIdsString =
|
37
|
+
document.body.dataset.scrollLockedContentIds || "[]";
|
38
|
+
const contentIds = JSON.parse(contentIdsString);
|
39
|
+
contentIds.push(contentId);
|
40
|
+
document.body.dataset.scrollLockedContentIds = JSON.stringify(contentIds);
|
41
|
+
}
|
42
|
+
};
|
43
|
+
const unlockScroll = (contentId) => {
|
44
|
+
const contentIdsString = document.body.dataset.scrollLockedContentIds || "[]";
|
45
|
+
const contentIds = JSON.parse(contentIdsString);
|
46
|
+
const newContentIds = contentIds.filter((id) => id !== contentId);
|
47
|
+
document.body.dataset.scrollLockedContentIds = JSON.stringify(newContentIds);
|
48
|
+
if (newContentIds.length === 0) {
|
49
|
+
delete document.body.dataset.scrollLocked;
|
50
|
+
document.body.classList.remove(
|
51
|
+
"data-[scroll-locked]:pointer-events-none",
|
52
|
+
"data-[scroll-locked]:!overflow-hidden",
|
53
|
+
"data-[scroll-locked]:!relative",
|
54
|
+
"data-[scroll-locked]:px-0",
|
55
|
+
"data-[scroll-locked]:pt-0",
|
56
|
+
"data-[scroll-locked]:ml-0",
|
57
|
+
"data-[scroll-locked]:mt-0",
|
58
|
+
);
|
59
|
+
document.body.style.marginRight = "";
|
60
|
+
}
|
61
|
+
};
|
62
|
+
const focusTrigger = (triggerTarget) => {
|
63
|
+
setTimeout(() => {
|
64
|
+
if (triggerTarget.dataset.asChild === "false") {
|
65
|
+
const childElement = triggerTarget.firstElementChild;
|
66
|
+
if (childElement) {
|
67
|
+
childElement.focus();
|
68
|
+
}
|
69
|
+
} else {
|
70
|
+
triggerTarget.focus();
|
71
|
+
}
|
72
|
+
}, ON_CLOSE_FOCUS_DELAY);
|
73
|
+
};
|
74
|
+
const focusElement = (element) => {
|
75
|
+
setTimeout(() => {
|
76
|
+
if (element) {
|
77
|
+
element.focus();
|
78
|
+
}
|
79
|
+
}, ON_OPEN_FOCUS_DELAY);
|
80
|
+
};
|
81
|
+
const getFocusableElements = (container) => {
|
82
|
+
return Array.from(
|
83
|
+
container.querySelectorAll(
|
84
|
+
'button, [href], input:not([type="hidden"]), select:not([tabindex="-1"]), textarea, [tabindex]:not([tabindex="-1"])',
|
85
|
+
),
|
86
|
+
);
|
87
|
+
};
|
88
|
+
const getSameLevelItems = ({ content, items, closestContentSelector }) => {
|
89
|
+
const sameLevelItems = [];
|
90
|
+
items.forEach((i) => {
|
91
|
+
if (
|
92
|
+
i.closest(closestContentSelector) === content &&
|
93
|
+
i.dataset.disabled === undefined
|
94
|
+
) {
|
95
|
+
sameLevelItems.push(i);
|
96
|
+
}
|
97
|
+
});
|
98
|
+
return sameLevelItems;
|
99
|
+
};
|
100
|
+
const showContent = ({
|
101
|
+
trigger,
|
102
|
+
content,
|
103
|
+
contentContainer,
|
104
|
+
setEqualWidth,
|
105
|
+
overlay,
|
106
|
+
}) => {
|
107
|
+
contentContainer.style.display = "";
|
108
|
+
if (trigger) {
|
109
|
+
if (setEqualWidth) {
|
110
|
+
const triggerWidth = trigger.offsetWidth;
|
111
|
+
const contentContainerWidth = contentContainer.offsetWidth;
|
112
|
+
if (contentContainerWidth < triggerWidth) {
|
113
|
+
contentContainer.style.width = `${triggerWidth}px`;
|
114
|
+
}
|
115
|
+
}
|
116
|
+
trigger.ariaExpanded = "true";
|
117
|
+
trigger.dataset.state = "open";
|
118
|
+
}
|
119
|
+
content.dataset.state = "open";
|
120
|
+
if (overlay) {
|
121
|
+
overlay.style.display = "";
|
122
|
+
overlay.dataset.state = "open";
|
123
|
+
lockScroll(content.id);
|
124
|
+
}
|
125
|
+
};
|
126
|
+
const hideContent = ({ trigger, content, contentContainer, overlay }) => {
|
127
|
+
if (trigger) {
|
128
|
+
trigger.ariaExpanded = "false";
|
129
|
+
trigger.dataset.state = "closed";
|
130
|
+
}
|
131
|
+
content.dataset.state = "closed";
|
132
|
+
setTimeout(() => {
|
133
|
+
contentContainer.style.display = "none";
|
134
|
+
if (overlay) {
|
135
|
+
overlay.style.display = "none";
|
136
|
+
overlay.dataset.state = "closed";
|
137
|
+
unlockScroll(content.id);
|
138
|
+
}
|
139
|
+
}, ANIMATION_OUT_DELAY);
|
140
|
+
};
|
141
|
+
const getStimulusInstance = (controller, element) => {
|
142
|
+
if (!element) return;
|
143
|
+
return window.Stimulus.getControllerForElementAndIdentifier(
|
144
|
+
element,
|
145
|
+
controller,
|
146
|
+
);
|
147
|
+
};
|
148
|
+
const anyNestedComponentsOpen = (element) => {
|
149
|
+
const components = [];
|
150
|
+
const componentNames = [
|
151
|
+
"dialog",
|
152
|
+
"alert-dialog",
|
153
|
+
"dropdown-menu",
|
154
|
+
"popover",
|
155
|
+
"select",
|
156
|
+
"combobox",
|
157
|
+
"command",
|
158
|
+
"hover-card",
|
159
|
+
"tooltip",
|
160
|
+
"date-picker",
|
161
|
+
"date-range-picker",
|
162
|
+
];
|
163
|
+
componentNames.forEach((name) => {
|
164
|
+
const triggers = Array.from(
|
165
|
+
element.querySelectorAll(
|
166
|
+
`[data-shadcn-phlexcomponents="${name}-trigger"]`,
|
167
|
+
),
|
168
|
+
);
|
169
|
+
const controllerElements = Array.from(
|
170
|
+
element.querySelectorAll(`[data-controller="${name}"]`),
|
171
|
+
);
|
172
|
+
controllerElements.forEach((controller) => {
|
173
|
+
const stimulusInstance = getStimulusInstance(name, controller);
|
174
|
+
if (stimulusInstance) {
|
175
|
+
components.push(stimulusInstance);
|
176
|
+
}
|
177
|
+
});
|
178
|
+
triggers.forEach((trigger) => {
|
179
|
+
const stimulusInstance = getStimulusInstance(
|
180
|
+
name,
|
181
|
+
document.querySelector(`#${trigger.getAttribute("aria-controls")}`),
|
182
|
+
);
|
183
|
+
if (stimulusInstance) {
|
184
|
+
components.push(stimulusInstance);
|
185
|
+
}
|
186
|
+
});
|
187
|
+
});
|
188
|
+
return components.some((c) => c.isOpenValue);
|
189
|
+
};
|
190
|
+
const onClickOutside = (controller, event) => {
|
191
|
+
const target = event.target;
|
192
|
+
// Let trigger handle state
|
193
|
+
if (target === controller.triggerTarget) return;
|
194
|
+
if (controller.triggerTarget.contains(target)) return;
|
195
|
+
controller.close();
|
196
|
+
};
|
197
|
+
const setGroupLabelsId = (controller) => {
|
198
|
+
controller.groupTargets.forEach((g) => {
|
199
|
+
const label = g.querySelector(
|
200
|
+
`[data-shadcn-phlexcomponents="${controller.identifier}-label"]`,
|
201
|
+
);
|
202
|
+
if (label) {
|
203
|
+
label.id = g.getAttribute("aria-labelledby");
|
204
|
+
}
|
205
|
+
});
|
206
|
+
};
|
207
|
+
const getNextEnabledIndex = ({ items, currentIndex, wrapAround, filterFn }) => {
|
208
|
+
let newIndex = null;
|
209
|
+
if (filterFn) {
|
210
|
+
newIndex = items.findIndex(
|
211
|
+
(item, index) => index > currentIndex && filterFn(item),
|
212
|
+
);
|
213
|
+
if (newIndex === -1) {
|
214
|
+
newIndex = currentIndex;
|
215
|
+
}
|
216
|
+
} else {
|
217
|
+
newIndex = currentIndex + 1;
|
218
|
+
}
|
219
|
+
if (newIndex > items.length - 1) {
|
220
|
+
if (wrapAround) {
|
221
|
+
newIndex = 0;
|
222
|
+
} else {
|
223
|
+
newIndex = items.length - 1;
|
224
|
+
}
|
225
|
+
}
|
226
|
+
return newIndex;
|
227
|
+
};
|
228
|
+
const getPreviousEnabledIndex = ({
|
229
|
+
items,
|
230
|
+
currentIndex,
|
231
|
+
wrapAround,
|
232
|
+
filterFn,
|
233
|
+
}) => {
|
234
|
+
let newIndex = null;
|
235
|
+
if (filterFn) {
|
236
|
+
newIndex = items.findLastIndex(
|
237
|
+
(item, index) => index < currentIndex && filterFn(item),
|
238
|
+
);
|
239
|
+
if (newIndex === -1) {
|
240
|
+
newIndex = currentIndex;
|
241
|
+
}
|
242
|
+
} else {
|
243
|
+
newIndex = currentIndex - 1;
|
244
|
+
}
|
245
|
+
if (newIndex < 0) {
|
246
|
+
if (wrapAround) {
|
247
|
+
newIndex = items.length - 1;
|
248
|
+
} else {
|
249
|
+
newIndex = 0;
|
250
|
+
}
|
251
|
+
}
|
252
|
+
return newIndex;
|
253
|
+
};
|
254
|
+
const handleTabNavigation = (element, event) => {
|
255
|
+
const focusableElements = getFocusableElements(element);
|
256
|
+
const firstElement = focusableElements[0];
|
257
|
+
const lastElement = focusableElements[focusableElements.length - 1];
|
258
|
+
// If Shift + Tab pressed on first element, go to last element
|
259
|
+
if (event.shiftKey && document.activeElement === firstElement) {
|
260
|
+
event.preventDefault();
|
261
|
+
lastElement.focus();
|
262
|
+
}
|
263
|
+
// If Tab pressed on last element, go to first element
|
264
|
+
else if (!event.shiftKey && document.activeElement === lastElement) {
|
265
|
+
event.preventDefault();
|
266
|
+
firstElement.focus();
|
267
|
+
}
|
268
|
+
};
|
269
|
+
export {
|
270
|
+
ANIMATION_OUT_DELAY,
|
271
|
+
ON_CLOSE_FOCUS_DELAY,
|
272
|
+
ON_OPEN_FOCUS_DELAY,
|
273
|
+
lockScroll,
|
274
|
+
unlockScroll,
|
275
|
+
focusTrigger,
|
276
|
+
focusElement,
|
277
|
+
getFocusableElements,
|
278
|
+
getSameLevelItems,
|
279
|
+
showContent,
|
280
|
+
hideContent,
|
281
|
+
getStimulusInstance,
|
282
|
+
anyNestedComponentsOpen,
|
283
|
+
onClickOutside,
|
284
|
+
setGroupLabelsId,
|
285
|
+
getNextEnabledIndex,
|
286
|
+
getPreviousEnabledIndex,
|
287
|
+
handleTabNavigation,
|
288
|
+
};
|
@@ -9,6 +9,13 @@ import type { HoverCard } from '../controllers/hover_card_controller'
|
|
9
9
|
import type { Tooltip } from '../controllers/tooltip_controller'
|
10
10
|
import type { DatePicker } from '../controllers/date_picker_controller'
|
11
11
|
import type { DateRangePicker } from '../controllers/date_range_picker_controller'
|
12
|
+
import { Application } from '@hotwired/stimulus'
|
13
|
+
|
14
|
+
declare global {
|
15
|
+
interface Window {
|
16
|
+
Stimulus: Application
|
17
|
+
}
|
18
|
+
}
|
12
19
|
|
13
20
|
const ANIMATION_OUT_DELAY = 100
|
14
21
|
const ON_OPEN_FOCUS_DELAY = 100
|
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
components_path = File.expand_path("../shadcn_phlexcomponents/components", __dir__)
|
4
4
|
components_install_path = Rails.root.join("vendor/shadcn_phlexcomponents/components")
|
5
|
-
|
5
|
+
stimulus_js_controllers_path = File.expand_path("../../app/javascript", __dir__)
|
6
|
+
stimulus_ts_controllers_path = File.expand_path("../../app/typescript", __dir__)
|
6
7
|
stimulus_controllers_install_path = Rails.root.join("vendor/shadcn_phlexcomponents/javascript")
|
7
8
|
css_path = File.expand_path("../../app/stylesheets", __dir__)
|
8
9
|
css_install_path = Rails.root.join("vendor/shadcn_phlexcomponents/stylesheets")
|
@@ -14,12 +15,18 @@ say "Please make sure to commit or stash your existing changes in your working d
|
|
14
15
|
|
15
16
|
if ENV["ENVIRONMENT"] == "test"
|
16
17
|
directory(components_path, components_install_path)
|
17
|
-
directory(
|
18
|
+
directory(stimulus_js_controllers_path, stimulus_controllers_install_path)
|
18
19
|
directory(css_path, css_install_path)
|
19
20
|
copy_file(initializer_file_path, initializer_file_install_path)
|
20
21
|
elsif yes?("Do you want to continue? (y/n)")
|
22
|
+
using_typescript = yes?("Are you using Typescript?")
|
23
|
+
|
24
|
+
if using_typescript
|
25
|
+
directory(stimulus_ts_controllers_path, stimulus_controllers_install_path)
|
26
|
+
else
|
27
|
+
directory(stimulus_js_controllers_path, stimulus_controllers_install_path)
|
28
|
+
end
|
21
29
|
directory(components_path, components_install_path)
|
22
|
-
directory(stimulus_controllers_path, stimulus_controllers_install_path)
|
23
30
|
directory(css_path, css_install_path)
|
24
31
|
copy_file(initializer_file_path, initializer_file_install_path)
|
25
32
|
end
|
@@ -66,10 +66,6 @@ module ShadcnPhlexcomponents
|
|
66
66
|
ComboboxGroup(aria_id: @aria_id, **attributes, &)
|
67
67
|
end
|
68
68
|
|
69
|
-
def empty(**attributes, &)
|
70
|
-
ComboboxEmpty(**attributes, &)
|
71
|
-
end
|
72
|
-
|
73
69
|
def items(collection, value_method:, text_method:, disabled_items: nil, &)
|
74
70
|
vanish(&)
|
75
71
|
|
@@ -229,7 +225,7 @@ module ShadcnPhlexcomponents
|
|
229
225
|
class: "sr-only",
|
230
226
|
id: "#{@aria_id}-search-label",
|
231
227
|
for: "#{@aria_id}-search",
|
232
|
-
) { @
|
228
|
+
) { @search_placeholder_text }
|
233
229
|
|
234
230
|
div(class: "flex h-9 items-center gap-2 border-b px-3") do
|
235
231
|
icon("search", class: "size-4 shrink-0 opacity-50")
|
@@ -238,7 +234,7 @@ module ShadcnPhlexcomponents
|
|
238
234
|
class: "placeholder:text-muted-foreground flex w-full rounded-md bg-transparent py-3 text-sm
|
239
235
|
outline-hidden disabled:cursor-not-allowed disabled:opacity-50 h-9",
|
240
236
|
id: "#{@aria_id}-search",
|
241
|
-
placeholder: @
|
237
|
+
placeholder: @search_placeholder_text,
|
242
238
|
type: :text,
|
243
239
|
autocomplete: "off",
|
244
240
|
autocorrect: "off",
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shadcn_phlexcomponents
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.17
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Yeoh
|
@@ -73,43 +73,77 @@ extra_rdoc_files: []
|
|
73
73
|
files:
|
74
74
|
- README.md
|
75
75
|
- Rakefile
|
76
|
-
- app/javascript/controllers/accordion_controller.
|
77
|
-
- app/javascript/controllers/alert_dialog_controller.
|
78
|
-
- app/javascript/controllers/avatar_controller.
|
79
|
-
- app/javascript/controllers/checkbox_controller.
|
80
|
-
- app/javascript/controllers/collapsible_controller.
|
81
|
-
- app/javascript/controllers/combobox_controller.
|
82
|
-
- app/javascript/controllers/command_controller.
|
83
|
-
- app/javascript/controllers/date_picker_controller.
|
84
|
-
- app/javascript/controllers/date_range_picker_controller.
|
85
|
-
- app/javascript/controllers/dialog_controller.
|
86
|
-
- app/javascript/controllers/dropdown_menu_controller.
|
87
|
-
- app/javascript/controllers/dropdown_menu_sub_controller.
|
88
|
-
- app/javascript/controllers/form_field_controller.
|
89
|
-
- app/javascript/controllers/hover_card_controller.
|
90
|
-
- app/javascript/controllers/loading_button_controller.
|
91
|
-
- app/javascript/controllers/popover_controller.
|
92
|
-
- app/javascript/controllers/progress_controller.
|
93
|
-
- app/javascript/controllers/radio_group_controller.
|
94
|
-
- app/javascript/controllers/select_controller.
|
95
|
-
- app/javascript/controllers/sidebar_controller.
|
96
|
-
- app/javascript/controllers/sidebar_trigger_controller.
|
97
|
-
- app/javascript/controllers/slider_controller.
|
98
|
-
- app/javascript/controllers/switch_controller.
|
99
|
-
- app/javascript/controllers/tabs_controller.
|
100
|
-
- app/javascript/controllers/theme_switcher_controller.
|
101
|
-
- app/javascript/controllers/toast_container_controller.
|
102
|
-
- app/javascript/controllers/toast_controller.
|
103
|
-
- app/javascript/controllers/toggle_controller.
|
104
|
-
- app/javascript/controllers/toggle_group_controller.
|
105
|
-
- app/javascript/controllers/tooltip_controller.
|
106
|
-
- app/javascript/shadcn_phlexcomponents.
|
107
|
-
- app/javascript/utils/command.
|
108
|
-
- app/javascript/utils/floating_ui.
|
109
|
-
- app/javascript/utils/index.
|
76
|
+
- app/javascript/controllers/accordion_controller.js
|
77
|
+
- app/javascript/controllers/alert_dialog_controller.js
|
78
|
+
- app/javascript/controllers/avatar_controller.js
|
79
|
+
- app/javascript/controllers/checkbox_controller.js
|
80
|
+
- app/javascript/controllers/collapsible_controller.js
|
81
|
+
- app/javascript/controllers/combobox_controller.js
|
82
|
+
- app/javascript/controllers/command_controller.js
|
83
|
+
- app/javascript/controllers/date_picker_controller.js
|
84
|
+
- app/javascript/controllers/date_range_picker_controller.js
|
85
|
+
- app/javascript/controllers/dialog_controller.js
|
86
|
+
- app/javascript/controllers/dropdown_menu_controller.js
|
87
|
+
- app/javascript/controllers/dropdown_menu_sub_controller.js
|
88
|
+
- app/javascript/controllers/form_field_controller.js
|
89
|
+
- app/javascript/controllers/hover_card_controller.js
|
90
|
+
- app/javascript/controllers/loading_button_controller.js
|
91
|
+
- app/javascript/controllers/popover_controller.js
|
92
|
+
- app/javascript/controllers/progress_controller.js
|
93
|
+
- app/javascript/controllers/radio_group_controller.js
|
94
|
+
- app/javascript/controllers/select_controller.js
|
95
|
+
- app/javascript/controllers/sidebar_controller.js
|
96
|
+
- app/javascript/controllers/sidebar_trigger_controller.js
|
97
|
+
- app/javascript/controllers/slider_controller.js
|
98
|
+
- app/javascript/controllers/switch_controller.js
|
99
|
+
- app/javascript/controllers/tabs_controller.js
|
100
|
+
- app/javascript/controllers/theme_switcher_controller.js
|
101
|
+
- app/javascript/controllers/toast_container_controller.js
|
102
|
+
- app/javascript/controllers/toast_controller.js
|
103
|
+
- app/javascript/controllers/toggle_controller.js
|
104
|
+
- app/javascript/controllers/toggle_group_controller.js
|
105
|
+
- app/javascript/controllers/tooltip_controller.js
|
106
|
+
- app/javascript/shadcn_phlexcomponents.js
|
107
|
+
- app/javascript/utils/command.js
|
108
|
+
- app/javascript/utils/floating_ui.js
|
109
|
+
- app/javascript/utils/index.js
|
110
110
|
- app/stylesheets/date_picker.css
|
111
111
|
- app/stylesheets/nouislider.css
|
112
112
|
- app/stylesheets/tw-animate.css
|
113
|
+
- app/typescript/controllers/accordion_controller.ts
|
114
|
+
- app/typescript/controllers/alert_dialog_controller.ts
|
115
|
+
- app/typescript/controllers/avatar_controller.ts
|
116
|
+
- app/typescript/controllers/checkbox_controller.ts
|
117
|
+
- app/typescript/controllers/collapsible_controller.ts
|
118
|
+
- app/typescript/controllers/combobox_controller.ts
|
119
|
+
- app/typescript/controllers/command_controller.ts
|
120
|
+
- app/typescript/controllers/date_picker_controller.ts
|
121
|
+
- app/typescript/controllers/date_range_picker_controller.ts
|
122
|
+
- app/typescript/controllers/dialog_controller.ts
|
123
|
+
- app/typescript/controllers/dropdown_menu_controller.ts
|
124
|
+
- app/typescript/controllers/dropdown_menu_sub_controller.ts
|
125
|
+
- app/typescript/controllers/form_field_controller.ts
|
126
|
+
- app/typescript/controllers/hover_card_controller.ts
|
127
|
+
- app/typescript/controllers/loading_button_controller.ts
|
128
|
+
- app/typescript/controllers/popover_controller.ts
|
129
|
+
- app/typescript/controllers/progress_controller.ts
|
130
|
+
- app/typescript/controllers/radio_group_controller.ts
|
131
|
+
- app/typescript/controllers/select_controller.ts
|
132
|
+
- app/typescript/controllers/sidebar_controller.ts
|
133
|
+
- app/typescript/controllers/sidebar_trigger_controller.ts
|
134
|
+
- app/typescript/controllers/slider_controller.ts
|
135
|
+
- app/typescript/controllers/switch_controller.ts
|
136
|
+
- app/typescript/controllers/tabs_controller.ts
|
137
|
+
- app/typescript/controllers/theme_switcher_controller.ts
|
138
|
+
- app/typescript/controllers/toast_container_controller.ts
|
139
|
+
- app/typescript/controllers/toast_controller.ts
|
140
|
+
- app/typescript/controllers/toggle_controller.ts
|
141
|
+
- app/typescript/controllers/toggle_group_controller.ts
|
142
|
+
- app/typescript/controllers/tooltip_controller.ts
|
143
|
+
- app/typescript/shadcn_phlexcomponents.ts
|
144
|
+
- app/typescript/utils/command.ts
|
145
|
+
- app/typescript/utils/floating_ui.ts
|
146
|
+
- app/typescript/utils/index.ts
|
113
147
|
- lib/install/install_shadcn_phlexcomponents.rb
|
114
148
|
- lib/shadcn_phlexcomponents.rb
|
115
149
|
- lib/shadcn_phlexcomponents/alias.rb
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|