Bagaimana saya menerapkan gaya terkelola Material-UI ke elemen non-material-ui, non-reaksi?

9

Saya memiliki aplikasi tempat saya menggunakan Material UI dan penyedia temanya (menggunakan JSS).

Saya sekarang menggabungkan fullcalendar-react , yang bukan benar-benar perpustakaan React sepenuhnya - itu hanya pembungkus komponen React tipis di sekitar kode fullcalendar asli.

Maksudnya, bahwa saya tidak memiliki akses ke hal-hal seperti render props untuk mengendalikan bagaimana ia mengatur elemen-elemennya.

Namun demikian, memberi Anda akses ke elemen DOM secara langsung, melalui panggilan balik yang dipanggil saat ia merendernya (misalnya metode eventRender ).

Inilah kotak pasir demo dasar.

masukkan deskripsi gambar di sini

Sekarang yang ingin saya lakukan adalah membuat komponen Kalender Lengkap (misalnya, tombol) berbagi tampilan dan rasa yang sama dengan aplikasi saya yang lain.

Salah satu cara untuk melakukan ini, adalah bahwa saya bisa secara manual mengesampingkan semua gaya dengan melihat nama-nama kelas yang digunakannya dan menerapkan gaya yang sesuai.

Atau - Saya bisa menerapkan tema Bootstrap - seperti yang disarankan dalam dokumentasi mereka.

Tetapi masalah dengan salah satu dari solusi ini, adalah bahwa:

  1. Itu akan banyak pekerjaan
  2. Saya akan mengalami masalah sinkronisasi, jika saya membuat perubahan pada tema MUI saya dan lupa untuk memperbarui tema kalender mereka akan terlihat berbeda.

Yang ingin saya lakukan adalah:

  • Konversikan secara ajaib tema MUI menjadi tema Bootstrap.
  • Atau buat pemetaan antara nama kelas MUI dan nama kelas kalender, seperti:
.fc-button = .MuiButtonBase-root.MuiButton-root.MuiButton-contained
.fc-button-primary= .MuiButton-containedPrimary

Saya tidak keberatan harus memijat selektor dll untuk membuatnya bekerja (mis. Misalnya - Tombol MUI memiliki dua rentang internal, sedangkan Kalender Lengkap hanya memiliki satu). Ini kebanyakan tentang ketika saya mengubah tema - tidak ingin harus mengubahnya di dua tempat.

Menggunakan sesuatu seperti Sass dengan @extendsintaksnya adalah apa yang ada dalam pikiran saya. Saya bisa membuat CSS kalender penuh dengan Sass cukup mudah - tetapi bagaimana Sass mendapatkan akses ke MuiTheme?

Mungkin saya bisa mengambil pendekatan yang berlawanan - beri tahu MUI 'Hai nama kelas ini di sini harus ditata seperti kelas MUI ini'.

Adakah saran nyata tentang bagaimana saya akan menyelesaikan ini?

djohnston
sumber

Jawaban:

4

Ini saran saya (jelas, itu tidak lurus ke depan). Ambil gaya dari tema MUI dan buat styletag berdasarkan itu gunakan react-helmet. Untuk melakukannya dengan baik, saya membuat komponen "pembungkus" yang melakukan peta. Saya hanya menerapkan aturan utama tetapi bisa diperluas ke yang lain.

Dengan cara ini, setiap perubahan yang Anda lakukan dalam tema akan memengaruhi pemilih yang dipetakan juga.

import React from "react";
import { Helmet } from "react-helmet";

export function MuiAdapter({ theme }) {
  if (!theme.palette) {
    return <></>;
  }
  return (
    <Helmet>
      <style type="text/css">{`
          .fc-button-primary {
            background: ${theme.palette.primary.main}
          }
          /* more styles go here */
      `}</style>
    </Helmet>
  );
}

Dan penggunaan adaptor

<MuiAdapter theme={theme} />

Demo yang berfungsi: https://codesandbox.io/s/reverent-mccarthy-3o856

tangkapan layar

Mosh Feu
sumber
Saya suka pemikiran ini. Masalah dengan solusi ini adalah bahwa Anda pada dasarnya masih akan menerapkan semua gaya sendiri (mis. Warna hover, padding, radius batas dll). Jika misalnya, Anda memutuskan teks tombol harus digarisbawahi, Anda harus ingat untuk menambahkan text-decorationproperti ke helm.
dwjohnston
Saya mengerti apa yang Anda minta. Saya mencoba untuk mengambil pendekatan yang berbeda dan memanipulasi styletag MUI dan menambahkan mereka .fc-selektor sesuai peta tetapi mereka memiliki konflik di tingkat dasar (seperti display, paddingdll.) Jadi saya tidak melihat bagaimana itu akan bekerja bahkan jika Anda ingin memiliki opsi untuk memigrasikannya di scss. Misalnya: codesandbox.io/s/charming-sound-3xmj9
Mosh Feu
Haha - sekarang ini yang saya suka. Saya akan memberikan tampilan yang lebih baik nanti.
dwjohnston
3

Anda dapat membuat pemetaan antara nama kelas MUI dan nama kelas kalender dengan menelusuri ref. Mungkin saja ini bukan yang oleh sebagian orang disebut "praktik terbaik" ... tetapi ini adalah solusi :). Perhatikan bahwa saya memperbarui komponen Anda dari komponen fungsional ke komponen kelas, tetapi Anda bisa melakukannya dengan kait di komponen fungsional.

Tambahkan referensi

Tambahkan refke elemen MUI yang ingin Anda tetapkan sebagai referensi, dalam kasus Anda the Button.

<Button
  color="primary"
  variant="contained"
  ref={x => {
    this.primaryBtn = x;
  }}
>

Dan refuntuk membungkus divkomponen yang ingin Anda petakan. Anda tidak dapat menambahkannya langsung ke komponen karena itu tidak akan memberi kami akses ke children.

<div
  ref={x => {
    this.fullCal = x;
  }}
>
  <FullCalendar
    ...
  />
</div>

Kelas peta

Dari componentDidMount()menambahkan logika apa pun yang Anda butuhkan untuk menargetkan simpul DOM yang benar (untuk kasus Anda, saya menambahkan logika untuk typedan matchingClass). Kemudian jalankan logika itu di semua node DOM FullCalendar dan ganti classListsemua yang cocok.

componentDidMount() {
  this.updatePrimaryBtns();
}

updatePrimaryBtns = () => {
  const children = Array.from(this.fullCal.children);
  // Options
  const type = "BUTTON";
  const matchingClass = "fc-button-primary";

  this.mapClassToElem(children, type, matchingClass);
};

mapClassToElem = (arr, type, matchingClass) => {
  arr.forEach(elem => {
    const { tagName, classList } = elem;
    // Check for match
    if (tagName === type && Array.from(classList).includes(matchingClass)) {
      elem.classList = this.primaryBtn.classList.value;
    }

    // Run on any children
    const next = elem.children;
    if (next.length > 0) {
      this.mapClassToElem(Array.from(next), type, matchingClass);
    }
  });
};

Ini mungkin sedikit sulit, tetapi memenuhi persyaratan bukti masa depan Anda ketika Anda memperbarui pembaruan UI Bahan. Ini juga akan memungkinkan Anda untuk mengubah classListsaat Anda meneruskannya ke elemen, yang memiliki manfaat yang jelas.

Peringatan

Jika komponen 'mapped-to' ( FullCalendar) memperbarui kelas pada elemen yang Anda targetkan (seperti jika itu ditambahkan .is-selectedke tombol saat ini) atau menambahkan tombol baru setelah pemasangan maka Anda harus mencari cara untuk melacak perubahan yang relevan dan jalankan kembali logika.

Saya juga harus menyebutkan bahwa (jelas) mengubah kelas mungkin memiliki konsekuensi yang tidak diinginkan seperti UI yang rusak dan Anda harus mencari cara untuk memperbaikinya.

Inilah kotak pasir yang berfungsi: https://codesandbox.io/s/determined-frog-3loyf

sallf
sumber
1
Ini bekerja. Perhatikan bahwa Anda tidak perlu mengonversi ke komponen kelas - Anda dapat menggunakan kait untuk melakukan ini.
dwjohnston
Ya Anda benar, ini bisa dilakukan dengan kait di komponen fungsional. Saya telah memperbarui jawaban untuk mencerminkan hal itu.
sallf
Bagus, pendekatan yang cukup pragmatis. Tetapi tidak benar-benar layak karena Anda akan selalu membutuhkan referensi.
Aniket Chowdhury