onlook-dev / onlook

The open source, local-first Figma for React. Design directly in your live React app and publish your changes to code.
https://onlook.dev
Apache License 2.0
3.85k stars 237 forks source link

[feat] Autocomplete Tailwind classes in the input boxes #622

Open drfarrell opened 1 month ago

drfarrell commented 1 month ago

Add in an auto-complete feature for the tailwind inputs, allowing a user to start typing and then be able to arrow up-and-down.

image

Kind of like this, from Sebastiano

image

Kabiirk commented 1 month ago

Hi @drfarrell ,

I can try to work on this , but for my clarity , are you looking to implement something similar to a language server (like IntelliSense) or a create a database of all possible tailwind classes based on tailwind's config files and then filter through it via autocomplete logic ?

An example implementation could be like this :

// Sample dataset of Tailwind CSS classes (should include more classes or be fetched dynamically)
const tailwindClasses = [
  'w-1/2', 'w-1/4', 'h-10', 'h-20', 'text-gray-800', 'bg-blue-500', 
  'hover:bg-blue-700', 'sm:text-lg', 'md:text-xl', 'rounded-full', 
  'flex', 'justify-center', 'items-center', 'space-x-4'
];

// HTML elements
const input = document.getElementById('tailwind-input');
const autocompleteList = document.getElementById('autocomplete-list');

let currentFocus = -1;

// Event listener for input field
input.addEventListener('input', function() {
  const query = this.value;
  closeSuggestions();
  if (!query) return;

  const suggestions = tailwindClasses.filter(item => item.startsWith(query));

  suggestions.forEach((suggestion, index) => {
    const div = document.createElement('div');
    div.classList.add('autocomplete-item');
    div.innerText = suggestion;

    // Add click listener
    div.addEventListener('click', () => {
      input.value = suggestion;
      closeSuggestions();
    });

    autocompleteList.appendChild(div);
  });
});

// More logic for navigating via keyboard Up & Down etc.
// ...
// ...
Kitenite commented 1 month ago

@Kabiirk, thanks for the exploration on this! Can you expand more on the language server implementation? Ideally, we would use the user's project's pre-existing tailwind classes which @hitaspdotnet is partially working on.

For now, I'll leave it up to your discretion on the best implementation also notice you can extend some of the helpers we're using here (for translating CSS values to tailwind classes).

https://github.com/onlook-dev/onlook/blob/1eeceb1dadf9fd6978b86173d9f6fee02718dd76/app/common/helpers/twTranslator/index.ts

One more suggestion is that there are existing tw intellisenseimplementation in vscode plugins but I wonder if that's overkill/too much work to integrate. What are your thoughts from this?

Kabiirk commented 1 month ago

Can you expand more on the language server implementation?

From what I understand, this would entail implementing a Language Server for IntelliSense (& then integrating it with tailwind) & would be like implementing another application (the language server) inside another application (onlook app). Pros are that it can be used to provide features like auto-complete, definitions and usage info on-hover. Some of these features might be outside the scope of this issue.

I feel it adds a lot of complexity & might be a bit overkill to implement/integrate entire host of out-of-scope features & a separate server & server processes (language server & services would need to run in the background with the onlook app) for getting autocomplete in 1 text field.

I'm still figuring out how to implement this so I don't have a concrete approach yet. I'll look into integrating Existing Tailwind intellisense into the project but IntelliSense/Autocompletion functionality itself is tied to Visual Studio Code (VSCode) via their Language Server Protocol & language server implementation, making it difficult to integrate with 3rd party apps, without setting up our own Language server in the background. As per my research this might provide a way forward.

I'll see if I can use the helpers you shared earlier in any way. Am I correct in saying that the code you shared earlier (twTranslator) converts CSS values, inputted in your GUI, to tailwind classes ?

As per my code snippet shared above, One approach is that I generate a list of all possible tailwind classes (static and dynamic), i.e. have a class list or a data store of classes & then filter them based on what's the 'active' text field classname being types. This Example also provides an implementation to autocompletion similar to mine.

Let me know your thoughts on this .

Kitenite commented 1 month ago

I think an actual separate process would be overkill since our need is small (1 input box). The benefit of a language server seems to be to be able to write in the same language and integrate through 1 simple protocol. For us, a simple filtered list would do.

Am I correct in saying that the code you shared earlier (twTranslator) converts CSS values, inputted in your GUI, to tailwind classes ? Yes, the helpers are used to translate from CSS to tailwind it may not be as relevant. Example usage to turn CSS stylesheet to tailwind classes. https://github.com/onlook-dev/onlook/blob/1f4ae57cdfa0dd6072941200c09a4225d92c7b9f/app/src/lib/editor/engine/code/helpers.ts#L28

Instead of a full list, consider that as you type, the scope of the autocomplete narrows semantically. For example: bg- would narrow to a list of colors. w- would go to size for example. This filtering would help with performance instead of filtering against a giant, unsorted list.

It's also worth checking out this implementation. https://github.com/tailwindlabs/tailwindcss-intellisense/blob/901cdf0bc15dab90cfa2c2c09a798316e0b745cf/packages/tailwindcss-language-service/src/completionProvider.ts#L48

Overall, I like the discussion. It's best if we can jump off an existing library instead of fully rolling our own. But for our purposes, we can also maintain the list like you mentioned :)

Excited for this!

Ashutoshdas-dev commented 1 month ago

hey @Kitenite could u please assign it to me

Kitenite commented 1 month ago

@Ashutoshdas-dev,

@Kabiirk is already working on it. It's up to him if he wants to collaborate. Is there anything you'd like to pick up instead?

Kabiirk commented 1 month ago

Hi @Kitenite,

Just FYI/sit-rep, I'm still working on this and will share something soon !

Kitenite commented 1 month ago

@Kabiirk Nice thanks for the update!

Kabiirk commented 1 month ago

Hi @Kitenite,

Apologies for the delay on this, took some time to read up on how to implement autocomplete & had some local issues switching to Bun from Node. I've created a basic implementation of the autocomplete feature. While a lot of styling & refining of autocomplete logic is left, this seems to be in the right direction.

PFB feature demo on a test project. While the TailwindInput (used here) is focused and the tailwind class is being typed, the 1st <ENTER> (keypress) inputs the new class-name into the box, the 2nd <ENTER> applies the class to the selected element.

Autocomplete.webm

Let me know your thoughts in this

Kitenite commented 1 month ago

@Kabiirk Woah this is cool. I like the proposed interaction. Feels very intuitive and the UI looks nice. Feel free to open a PR even if in draft state if you'd like early feedback on the code :)