Render inputs inline & results in a turbo frame
This commit is contained in:
@@ -1,43 +1,28 @@
|
||||
@layer components {
|
||||
.conversation {
|
||||
font-family: var(--font-sans);
|
||||
padding-block-end: 6em;
|
||||
}
|
||||
.conversation__composer-input {
|
||||
--focus-ring-size: 0;
|
||||
--input-border-radius: 0;
|
||||
--input-border-size: 0;
|
||||
--input-padding: 0 0.5em;
|
||||
|
||||
.conversation__composer {
|
||||
backdrop-filter: blur(24px);
|
||||
display: flex;
|
||||
gap: var(--inline-space);
|
||||
inset: auto 0 0 0;
|
||||
padding: var(--block-space);
|
||||
position: absolute;
|
||||
&::-webkit-search-cancel-button {
|
||||
--clear-icon-size: 0.75rem;
|
||||
|
||||
.conversation__submit {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background-image: url('data:image/svg+xml;utf8,<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m14.3 12.179a.25.25 0 0 1 0-.354l9.263-9.262a1.5 1.5 0 0 0 -2.124-2.121l-9.262 9.258a.25.25 0 0 1 -.354 0l-9.262-9.258a1.5 1.5 0 0 0 -2.122 2.121l9.261 9.262a.25.25 0 0 1 0 .354l-9.261 9.263a1.5 1.5 0 0 0 2.122 2.121l9.262-9.263a.25.25 0 0 1 .354 0l9.262 9.263a1.5 1.5 0 0 0 2.122-2.121z" fill="%23000"/></svg>');
|
||||
background-repeat: no-repeat;
|
||||
background-size: var(--clear-icon-size);
|
||||
block-size: var(--clear-icon-size);
|
||||
cursor: pointer;
|
||||
inline-size: var(--clear-icon-size);
|
||||
inset: 50% 1ch auto auto;
|
||||
position: absolute;
|
||||
translate: 0 -50%;
|
||||
|
||||
.conversation__header {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
inset: 0 0 auto 0;
|
||||
justify-content: flex-end;
|
||||
padding: var(--block-space-half);
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.conversation__input {
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
|
||||
.input--textarea {
|
||||
--input-padding: 0.5em !important;
|
||||
}
|
||||
|
||||
.conversation:has(.conversation__thinking-indicator--thinking) & {
|
||||
animation: pulse 1.5s infinite;
|
||||
pointer-events: none;
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background-image: url('data:image/svg+xml;utf8,<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m14.3 12.179a.25.25 0 0 1 0-.354l9.263-9.262a1.5 1.5 0 0 0 -2.124-2.121l-9.262 9.258a.25.25 0 0 1 -.354 0l-9.262-9.258a1.5 1.5 0 0 0 -2.122 2.121l9.261 9.262a.25.25 0 0 1 0 .354l-9.261 9.263a1.5 1.5 0 0 0 2.122 2.121l9.262-9.263a.25.25 0 0 1 .354 0l9.262 9.263a1.5 1.5 0 0 0 2.122-2.121z" fill="%23fff"/></svg>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +30,6 @@
|
||||
display: flex;
|
||||
|
||||
.action-text-content {
|
||||
|
||||
border-radius: 0.5rem;
|
||||
inline-size: fit-content;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
import { post } from "@rails/request.js"
|
||||
import { isInput } from "helpers/html_helpers"
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = [ "turboFrame" ]
|
||||
static targets = [ "turboFrame", "searchInput", "askInput", "buttonsContainer" ]
|
||||
static outlets = [ "dialog" ]
|
||||
static values = {
|
||||
searchUrl: String,
|
||||
searchTurboFrameName: String,
|
||||
askUrl: String,
|
||||
askTurboFrameName: String
|
||||
}
|
||||
|
||||
dialogOutletConnected(outlet, element) {
|
||||
@@ -17,36 +14,34 @@ export default class extends Controller {
|
||||
this.#clearTurboFrame()
|
||||
}
|
||||
|
||||
closeModal() {
|
||||
reset() {
|
||||
this.dialogOutlet.close()
|
||||
this.#clearTurboFrame()
|
||||
|
||||
this.#showItem(this.buttonsContainerTarget)
|
||||
this.#hideItem(this.askInputTarget)
|
||||
this.#hideItem(this.searchInputTarget)
|
||||
}
|
||||
|
||||
showModal() {
|
||||
this.dialogOutlet.open()
|
||||
}
|
||||
|
||||
search(event) {
|
||||
if (this.#shouldExecuteHotkey) {
|
||||
event.preventDefault()
|
||||
this.#openInTurboFrame(this.searchTurboFrameNameValue, this.searchUrlValue)
|
||||
this.dialogOutlet.open()
|
||||
}
|
||||
this.#openInTurboFrame("search", this.searchUrlValue)
|
||||
|
||||
this.#showItem(this.searchInputTarget)
|
||||
this.#hideItem(this.askInputTarget)
|
||||
this.#hideItem(this.buttonsContainerTarget)
|
||||
}
|
||||
|
||||
ask(event) {
|
||||
if (this.#shouldExecuteHotkey) {
|
||||
event.preventDefault()
|
||||
this.#initializeConversation()
|
||||
this.#openInTurboFrame(this.askTurboFrameNameValue, this.askUrlValue)
|
||||
this.dialogOutlet.open()
|
||||
}
|
||||
}
|
||||
this.#initializeConversation()
|
||||
this.#openInTurboFrame("conversation", this.askUrlValue)
|
||||
|
||||
get #shouldExecuteHotkey() {
|
||||
const activeElement = document.activeElement
|
||||
|
||||
if (activeElement) {
|
||||
return !isInput(activeElement)
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
this.#showItem(this.askInputTarget)
|
||||
this.#hideItem(this.searchInputTarget)
|
||||
this.#hideItem(this.buttonsContainerTarget)
|
||||
}
|
||||
|
||||
#clearTurboFrame() {
|
||||
@@ -64,11 +59,11 @@ export default class extends Controller {
|
||||
post(this.askUrlValue)
|
||||
}
|
||||
|
||||
#closeModalIfAlreadyOpen() {
|
||||
if (this.hasDialogOutlet) {
|
||||
try {
|
||||
this.closeModal()
|
||||
} catch {}
|
||||
}
|
||||
#showItem(element) {
|
||||
element.removeAttribute("hidden")
|
||||
}
|
||||
|
||||
#hideItem(element) {
|
||||
element.setAttribute("hidden", "hidden")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
import { isInput } from "helpers/html_helpers"
|
||||
|
||||
export default class extends Controller {
|
||||
click(event) {
|
||||
@@ -10,7 +9,7 @@ export default class extends Controller {
|
||||
}
|
||||
|
||||
#shouldIgnore(event) {
|
||||
return event.defaultPrevented || isInput(event.target)
|
||||
return event.defaultPrevented || event.target.closest("input, textarea, lexical-editor")
|
||||
}
|
||||
|
||||
get #isClickable() {
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export function isInput(element) {
|
||||
return !!element.closest("input, textarea, lexical-editor")
|
||||
}
|
||||
@@ -4,9 +4,9 @@
|
||||
controller: "bar",
|
||||
bar_dialog_outlet: "#bar-modal",
|
||||
bar_search_url_value: search_path,
|
||||
bar_ask_url_value: conversation_path,
|
||||
bar_ask_url_value: conversation_path
|
||||
} do %>
|
||||
<div class="flex justify-center gap">
|
||||
<div class="flex justify-center gap" data-bar-target="buttonsContainer">
|
||||
<%= tag.button \
|
||||
class: "btn btn--plain",
|
||||
data: {
|
||||
@@ -28,9 +28,11 @@
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div hidden>
|
||||
<div data-bar-target="searchInput" hidden>
|
||||
<%= render "searches/form", query_terms: "" %>
|
||||
</div>
|
||||
|
||||
<div data-bar-target="askInput" hidden>
|
||||
<%= render "conversations/composer" %>
|
||||
</div>
|
||||
|
||||
@@ -41,7 +43,7 @@
|
||||
controller: "dialog",
|
||||
dialog_target: "dialog",
|
||||
dialog_modal_value: "false",
|
||||
action: "keydown.esc->dialog#close" } do %>
|
||||
action: "keydown.esc->bar#reset" } do %>
|
||||
<%= turbo_frame_tag "fizzy-bar", data: { bar_target: "turboFrame" } %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
<%= form_with model: Conversation::Message.new, local: true,
|
||||
class: "conversation__composer",
|
||||
class: "conversation__composer flex align-center",
|
||||
data: {
|
||||
turbo_frame: "conversation",
|
||||
action: "turbo:submit-end->conversation--composer#submitEnd",
|
||||
controller: "conversation--composer",
|
||||
conversation__composer_conversation__messages_outlet: "#conversation-messages" } do |form| %>
|
||||
|
||||
<%= form.hidden_field :client_message_id, data: { conversation__composer_target: "clientMessageIdInput" } %>
|
||||
|
||||
<div class="conversation__input">
|
||||
<%= form.text_field :content,
|
||||
autofocus: "on",
|
||||
placeholder: "Ask something…",
|
||||
class: "input input--textarea",
|
||||
data: {
|
||||
action: "
|
||||
keydown.enter->conversation--composer#submit
|
||||
keydown.ctrl+enter->conversation--composer#submit",
|
||||
conversation__composer_target: "textInput" } %>
|
||||
</div>
|
||||
<%= form.label :content, "Ask Fizzy", class: "font-weight-black txt-nowrap" %>
|
||||
<%= form.text_field :content,
|
||||
autofocus: "on",
|
||||
placeholder: "Ask something…",
|
||||
class: "conversation__composer-input input",
|
||||
data: {
|
||||
action: "
|
||||
keydown.esc->bar#reset
|
||||
keydown.enter->conversation--composer#submit keydown.enter->bar#showModal
|
||||
keydown.ctrl+enter->conversation--composer#submit keydown.ctrl+enter->bar#showModal",
|
||||
conversation__composer_target: "textInput" } %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<%= turbo_frame_tag dom_id(@conversation, :messages_container) do %>
|
||||
<%= tag.div \
|
||||
id: dom_id(@conversation, :messages),
|
||||
id: "conversation-messages",
|
||||
class: "conversation__messages",
|
||||
data: {
|
||||
controller: "conversation--messages",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
<%= form_with url: search_path, method: :get, class: "search__form flex align-center", data: { turbo_frame: "search" } do |form| %>
|
||||
<%= form.label :q, "Search Fizzy", class: "font-weight-black txt-nowrap" %>
|
||||
<%= text_field_tag :q, query_terms, class: "search__form-input input", type: "search",
|
||||
placeholder: "Find something in Cards and Comments…", autocomplete: "off" %>
|
||||
<%= text_field_tag :q, query_terms,
|
||||
class: "search__form-input input",
|
||||
type: "search",
|
||||
placeholder: "Find something in Cards and Comments…",
|
||||
autocomplete: "off",
|
||||
data: {
|
||||
action: "keydown.enter->bar#showModal keydown.esc->bar#reset"
|
||||
} %>
|
||||
<% end %>
|
||||
|
||||
Reference in New Issue
Block a user