preline icon indicating copy to clipboard operation
preline copied to clipboard

Add guides for Ruby on Rails

Open RiadhKHEDHIRI opened this issue 10 months ago • 5 comments

Summary

Add guides for Ruby on Rails

Detailed Description

As Ruby on Rails is on fire (again), please add installation and configuration guides for it.

Use Cases

Develop pretty and UX-friendly products with Preline 💖

RiadhKHEDHIRI avatar Mar 24 '25 15:03 RiadhKHEDHIRI

What exactly do you feel like it's missing? I think the process itself is fairly straightforward.

Something along the lines of:

Setup

Add Tailwind to your Rails project as recommended in TailwindCSS guides or just using the rails generators (see rails new --css=tailwind myapp).

Then add Preline to your project

yarn add preline
# yarn add clipboard # or any other plugins

Make sure you include any plugins you might be using, such as: clipboard, vanilla-calendar-pro, etc. See what needs to be included for each component on the docs on plugins page.

Check your package.json and see the "scripts": {} include the correct build command for CSS using tailwindcss:

{
  "dependencies": {
    ...
    "@tailwindcss/cli": "4.0.16",
    "preline": "^3.0.0",
  },
  "scripts": {
    "build": "esbuild app/javascript/*.* --bundle --sourcemap --format=esm --outdir=app/assets/builds --public-path=/assets",
    "build:css": "npx @tailwindcss/cli -i ./app/assets/stylesheets/application.tailwind.css -o ./app/assets/builds/application.css"
  }
}

Import Preline

# app/javascript/application.js
import "preline"

# If you use plugins as ClipboardJS load them here
import ClipboardJS from "clipboard";
window.ClipboardJS = ClipboardJS;

and in the stylesheets

@import 'tailwindcss';

@plugin '@tailwindcss/typography';
@plugin '@tailwindcss/forms';
@plugin '@tailwindcss/aspect-ratio';
@plugin '@tailwindcss/container-queries';

@import "../../../node_modules/preline/variants.css";

Careful when using Turbo

If you're using Turbo then you need to take care of re-initializing Preline after turbo:load and/or turbo:render events. Here's how I've done it.

import { initClipboardJS } from "./preline/clipboard-fix"

initCustomScripts = () => {
  // Custom fix for clipboard.js Preline helper where I removed addEventListener
  // from the Preline helper and load it manually here.
  initClipboardJS();

  // Re-initialize Preline
  HSStaticMethods.autoInit();
}

document.addEventListener("turbo:load", (event) => {
  console.debug("turbo:load event fired", event);
  initCustomScripts();
});

You can find clipboard-fix.js in one of the lower comments in this thread.

lenart avatar Mar 26 '25 07:03 lenart

While the above instructions worked with Preline 2.7 I'm having issues with setting up the imports with 3.0 and make them work. Specifically, the JS part needs to be setup in a certain way that I still need to figure out. I can't get

Maybe some guides (using jsbundling-rails with npm/yarn/bun/...) wouldn't be that bad after all 😅

lenart avatar Apr 15 '25 05:04 lenart

I've updated the code in my comment above to reflect my current working solution. I've had to manually extract the fix for Clipboard.js (from node_modules/preline/src/helpers/clipboard/index.ts)

lenart avatar Apr 17 '25 18:04 lenart

@lenart so maybe put it here? ;)

AlexKovynev avatar Apr 18 '25 20:04 AlexKovynev

Like I've said, I've updated the comment above with my workaround. For clipboard-fix.js I've copied the code from node_modules/preline/src/helpers/clipboard/index.ts and updated it slightly so that it doesn't bind to load event but allows me to call it manually. Here's the file for convenience:

// app/javascript/preline/clipboard-fix.js

// Code copied from: node_modules/preline/src/helpers/clipboard/index.ts
export function initClipboardJS(clipboardSelector = '.js-clipboard') {
  const $clipboards = document.querySelectorAll(clipboardSelector);

  $clipboards.forEach((el) => {
    const clipboard = new ClipboardJS(el, {
      text: (trigger) => {
        const clipboardText = trigger.dataset.clipboardText;

        if (clipboardText) return clipboardText;

        const clipboardTarget = trigger.dataset.clipboardTarget;
        const $element = document.querySelector(clipboardTarget);

        if (
          $element.tagName === 'SELECT' ||
          $element.tagName === 'INPUT' ||
          $element.tagName === 'TEXTAREA'
        )
          return $element.value;
        else return $element.textContent;
      },
    });
    clipboard.on('success', () => {
      const $default = el.querySelector('.js-clipboard-default');
      const $success = el.querySelector('.js-clipboard-success');
      const $successText = el.querySelector('.js-clipboard-success-text');
      const successText = el.dataset.clipboardSuccessText || '';
      const tooltip = el.closest('.hs-tooltip');
      let oldSuccessText;

      if ($successText) {
        oldSuccessText = $successText.textContent;
        $successText.textContent = successText;
      }
      if ($default && $success) {
        $default.style.display = 'none';
        $success.style.display = 'block';
      }
      if (tooltip) window.HSTooltip.show(tooltip);

      setTimeout(function () {
        if ($successText && oldSuccessText)
          $successText.textContent = oldSuccessText;
        if (tooltip) window.HSTooltip.hide(tooltip);
        if ($default && $success) {
          $success.style.display = '';
          $default.style.display = '';
        }
      }, 800);
    });
  })
}

In my main file (eg. application.js) I can then force initialisation whenever needed (eg. turbo:load).

import { initClipboardJS } from "./preline/clipboard-fix"

document.addEventListener("turbo:load", (event) => {
  initClipboardJS();
})

lenart avatar Apr 26 '25 10:04 lenart