ฉันจะใช้สไตล์การจัดการ Material-UI กับองค์ประกอบที่ไม่ใช่ material-ui และไม่ตอบสนองได้อย่างไร


9

ฉันมีแอปพลิเคชันที่ฉันใช้ Material UI และผู้ให้บริการธีม (โดยใช้ JSS)

ตอนนี้ฉันได้รวมfullcalendar-react เอาไว้ซึ่งไม่ใช่ไลบรารี React ที่สมบูรณ์แบบจริงๆ - มันเป็นเพียง wrapper องค์ประกอบ React บาง ๆ รอบรหัส fullcalendar ดั้งเดิม

กล่าวคือฉันไม่สามารถเข้าถึงสิ่งต่าง ๆ เช่นอุปกรณ์ประกอบฉากแสดงผลเพื่อควบคุมลักษณะขององค์ประกอบ

อย่างไรก็ตามให้สิทธิ์การเข้าถึงองค์ประกอบ DOM โดยตรงผ่านการติดต่อกลับที่ถูกเรียกเมื่อสร้างการแสดงผล (เช่นเมธอด eventRender )

นี่คือตัวอย่างทดลองพื้นฐาน

ป้อนคำอธิบายรูปภาพที่นี่

ตอนนี้สิ่งที่ฉันต้องการทำก็คือทำให้ส่วนประกอบปฏิทินแบบเต็ม (เช่นปุ่ม) แบ่งปันรูปลักษณ์และความรู้สึกเช่นเดียวกับส่วนที่เหลือของแอปพลิเคชันของฉัน

วิธีหนึ่งในการทำเช่นนี้คือฉันสามารถแทนที่สไตล์ทั้งหมดด้วยตนเองโดยดูที่ชื่อคลาสที่ใช้และใช้สไตล์ตามนั้น

หรือ - ฉันสามารถใช้ชุดรูปแบบ Bootstrap - ตามที่แนะนำในเอกสารประกอบ

แต่ปัญหาของการแก้ปัญหาเหล่านี้ก็คือ:

  1. มันจะทำงานมาก
  2. ฉันจะมีปัญหาการซิงโครไนซ์ถ้าฉันทำการเปลี่ยนแปลงชุดรูปแบบ MUI ของฉันและลืมปรับปรุงชุดรูปแบบปฏิทินพวกเขาจะดูแตกต่างกัน

สิ่งที่ฉันต้องการทำคือ:

  • แปลงธีม MUI เป็นธีม Bootstrap อย่างน่าอัศจรรย์
  • หรือสร้างการแมประหว่างชื่อคลาส MUI และชื่อคลาสปฏิทินเช่น:
.fc-button = .MuiButtonBase-root.MuiButton-root.MuiButton-contained
.fc-button-primary= .MuiButton-containedPrimary

ฉันจะไม่รังเกียจที่จะนวดตัวเลือก ฯลฯ เพื่อให้มันทำงานได้ (เช่นตัวอย่างเช่น - ปุ่ม MUI มีสองช่วงภายในขณะที่ปฏิทินแบบเต็มมีเพียงอันเดียว) ส่วนใหญ่เกี่ยวกับเมื่อฉันเปลี่ยนชุดรูปแบบ - ไม่ต้องการเปลี่ยนในสองแห่ง

การใช้บางอย่างเช่น Sass กับ@extendไวยากรณ์ของมันจะเป็นสิ่งที่ฉันมีอยู่ในใจ ฉันสามารถสร้าง CSS ปฏิทินแบบเต็มด้วย Sass ได้ง่ายพอ - แต่ Sass จะเข้าถึง MuiTheme ได้อย่างไร

บางทีฉันอาจใช้แนวทางตรงกันข้าม - บอก MUI ว่า 'เฮ้ชื่อคลาสเหล่านี้ที่นี่ควรมีสไตล์เหมือนกับคลาส MUI เหล่านี้'

ข้อเสนอแนะที่เป็นรูปธรรมเกี่ยวกับวิธีที่ฉันจะแก้ปัญหานี้?

คำตอบ:


4

นี่คือคำแนะนำของฉัน (เห็นได้ชัดว่ามันไม่ได้ตรงไปข้างหน้า) ใช้รูปแบบจากธีม MUI และสร้างแท็กบนพื้นฐานของมันโดยใช้style react-helmetในการทำเหตุการณ์อย่างฉันสร้างองค์ประกอบ "wrapper" ที่ทำแผนที่ ฉันใช้กฎหลักเพียงอย่างเดียว แต่สามารถขยายไปยังกฎอื่นได้ทั้งหมด

ด้วยวิธีนี้การเปลี่ยนแปลงใด ๆ ที่คุณจะทำในชุดรูปแบบจะมีผลกับตัวเลือกที่แมปด้วย

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>
  );
}

และการใช้งานของอะแดปเตอร์

<MuiAdapter theme={theme} />

ตัวอย่างการทำงาน: https://codesandbox.io/s/reverent-mccarthy-3o856

ภาพหน้าจอ


ฉันชอบความคิดนี้ ปัญหาของการแก้ปัญหานี้คือคุณยังคงต้องใช้สไตล์ทั้งหมดด้วยตัวเอง (เช่นสีของโฮเวอร์, ช่องว่างภายใน, รัศมีเส้นขอบ ฯลฯ ) ตัวอย่างเช่นคุณตัดสินใจว่าข้อความของปุ่มควรขีดเส้นใต้คุณต้องจำไว้ว่าให้เพิ่มtext-decorationคุณสมบัติลงในหมวก
dwjohnston

ฉันเข้าใจสิ่งที่คุณขอ ผมพยายามที่จะใช้วิธีการที่แตกต่างกันและจัดการ MUI styleแท็กและเพิ่ม.fc-ตัวเลือกตามแผนที่ แต่พวกเขามีความขัดแย้งในระดับฐาน (เช่นdisplay, paddingฯลฯ ) ดังนั้นผมจึงไม่เห็นว่ามันจะทำงานแม้ว่าคุณจะมี ตัวเลือกในการโยกย้ายใน scss ตัวอย่างเช่น: codesandbox.io/s/charming-sound-3xmj9
Mosh Feu

ฮ่าฮ่า - ตอนนี้ฉันชอบ ฉันจะให้มันดูดีขึ้นในภายหลัง
dwjohnston

3

คุณสามารถสร้างการแมประหว่างชื่อชั้น MUI และชื่อชั้นปฏิทินโดยจะผ่านref's เป็นไปได้ว่านี่ไม่ใช่สิ่งที่บางคนเรียกว่า "แนวปฏิบัติที่ดีที่สุด" ... แต่มันเป็นทางออก :) โปรดทราบว่าฉันได้อัปเดตองค์ประกอบของคุณจากองค์ประกอบที่ใช้งานได้ไปเป็นองค์ประกอบของคลาส แต่คุณสามารถทำได้โดยใช้ hooks ในส่วนประกอบที่ใช้งานได้

เพิ่มการอ้างอิง

เพิ่ม a refให้กับองค์ประกอบ MUI ที่คุณต้องการตั้งเป็นข้อมูลอ้างอิงในกรณีของคุณButtonคือ

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

และการrefห่อdivรอบส่วนประกอบที่คุณต้องการแมป คุณไม่สามารถเพิ่มลงในองค์ประกอบโดยตรงเนื่องจากจะไม่ทำให้เราสามารถเข้าถึงchildrenได้

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

แผนที่ชั้นเรียน

จากการcomponentDidMount()เพิ่มตรรกะใด ๆ ที่คุณต้องการกำหนดเป้าหมายโหนด DOM ที่ถูกต้อง (สำหรับกรณีของคุณฉันเพิ่มตรรกะสำหรับtypeและmatchingClass) จากนั้นรันตรรกะนั้นบนโหนด FullCalendar DOM ทั้งหมดและแทนที่classListบนการแข่งขันใด ๆ นั้น

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);
    }
  });
};

นี่อาจจะเป็นเรื่องที่หนักหน่วงเล็กน้อย แต่มันตรงตามข้อกำหนดการพิสูจน์ในอนาคตของคุณเมื่อคุณอัปเดต UI วัสดุที่ปรับปรุง นอกจากนี้ยังช่วยให้คุณสามารถเปลี่ยนสิ่งclassListที่คุณส่งไปยังองค์ประกอบที่มีประโยชน์ชัดเจน

คำเตือน

หากส่วนประกอบ 'mapped-to' ( FullCalendar) อัปเดตคลาสในองค์ประกอบที่คุณกำหนดเป้าหมาย (เช่นถ้ามันถูกเพิ่ม.is-selectedเข้าไปในปุ่มปัจจุบัน) หรือเพิ่มปุ่มใหม่หลังจากติดตั้งแล้วคุณจะต้องหาวิธีในการติดตามการเปลี่ยนแปลงที่เกี่ยวข้องและรันใหม่ ตรรกะ

ฉันควรจะพูดถึงว่า (ชัด ๆ ) การเปลี่ยนคลาสอาจมีผลที่ไม่ตั้งใจเช่น UI ที่เสียหายและคุณจะต้องหาวิธีแก้ไข

นี่คือ sandbox ที่ใช้งานได้: https://codesandbox.io/s/determined-frog-3loyf


1
วิธีนี้ใช้ได้ผล โปรดทราบว่าคุณไม่ต้องแปลงเป็นองค์ประกอบของคลาส - คุณสามารถใช้ hooks เพื่อทำสิ่งนี้
dwjohnston

ใช่คุณถูกต้องสามารถทำได้ด้วย hooks ในส่วนประกอบที่ใช้งานได้ ฉันได้อัปเดตคำตอบเพื่อแสดงว่า
sallf

นีซค่อนข้างเป็นแนวทางปฏิบัติ แต่ไม่ทำได้จริง ๆ เพราะคุณจะต้องอ้างอิง
Aniket Chowdhury
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.