פיצול בלוקים הוא פיצול של תיבה ברמת הבלוק ב-CSS (כמו קטע או פסקה) למספר קטעים, אם היא לא נכנסת בשלמותה לתוך מאגר קטעים אחד שנקרא fragmentainer. רכיב חלוקת פריטים הוא לא אלמנט, אלא מייצג עמודה בפריסה של כמה עמודות, או דף במדיה שמחולקת לדפים.
כדי שהתוכן יתפרק, הוא צריך להיות בהקשר של פיצול. הקשר של פיצול נוצר בדרך כלל על ידי מאגר עם כמה עמודות (התוכן מפוצל לעמודות) או במהלך הדפסה (התוכן מפוצל לדפים). יכול להיות שיהיה צורך לפצל פסקה ארוכה עם הרבה שורות לכמה קטעים, כך שהשורות הראשונות יופיעו בחלק הראשון, והשורות הנותרות יופיעו בחלקים הבאים.

פיצול בלוקים דומה לסוג אחר של פיצול ידוע: פיצול שורות, שנקרא גם 'הפסקת שורה'. כל רכיב בשורה שמכיל יותר ממילה אחת (כל צומת טקסט, כל רכיב <a>
וכו') ומאפשר הפסקות שורה, יכול להיות מפוצל למספר קטעים. כל קטע ממוקם בקו אחר. תיבת שורה היא פיצול בתוך שורה, שזהה למאגר פיצולים של עמודות ודפים.
פרגמנטציה של בלוקים ב-LayoutNG
LayoutNGBlockFragmentation הוא כתיבה מחדש של מנוע הפיצול של LayoutNG, ששוחרר לראשונה ב-Chrome 102. מבחינת מבני נתונים, המערכת החליפה כמה מבני נתונים מדור קודם ב-NG בקטעי NG שמיוצגים ישירות בעץ הקטעים.
לדוגמה, עכשיו אנחנו תומכים בערך 'avoid' למאפייני ה-CSS 'break-before' ו-'break-after', שמאפשרים לכותבים להימנע מקצוות אחרי כותרת. לרוב, זה נראה מוזר כשהדבר האחרון בדף הוא כותרת, בעוד שתוכן הקטע מתחיל בדף הבא. עדיף להוסיף את ההפסקה לפני הכותרת.

ב-Chrome יש גם תמיכה בחריגה ממלאי המידע, כך שתוכן מונוליתי (שנועד שלא ניתן לשבירה) לא ייחתך לכמה עמודות, ואפקטים של צביעה כמו צללים וטרנספורמציות יוחלו בצורה נכונה.
השלמת הפיצול של בלוקים ב-LayoutNG
פיצול הליבה (קונטיינרים של בלוקים, כולל פריסה של שורות, פריטים צפים ומיקום מחוץ לזרימה) שולח ב-Chrome 102. הפיצ'ר של פיצול גיליונות וטבלאות שוחרר ב-Chrome 103, והפיצ'ר של פיצול גיליונות וטבלאות ב-Flex שוחרר ב-Chrome 106. לבסוף, הדפסה נוספה לגרסה 108 של Chrome. פירוט בלוקים הייתה התכונה האחרונה שהסתמכה על המנוע הקודם כדי לבצע פריסה.
החל מגרסה 108 של Chrome, המנוע הקודם כבר לא משמש לביצוע פריסה.
בנוסף, מבני הנתונים של LayoutNG תומכים בציור ובבדיקת היטים, אבל אנחנו מסתמכים על מבני נתונים מסוימים מדור קודם ל-API של JavaScript שקוראים את פרטי הפריסה, כמו offsetLeft
ו-offsetTop
.
פריסה של כל הרכיבים באמצעות NG תאפשר הטמעה ושליחה של תכונות חדשות שיש להן רק הטמעות של LayoutNG (ולא מקבילות במנוע הקודם), כמו שאילתות של קונטיינרים ב-CSS, מיקום של עוגנים, MathML ופריסה מותאמת אישית (Houdini). לשאילתות בקונטיינרים, השקנו את התכונה קצת מראש, עם אזהרה למפתחים שהדפסה עדיין לא נתמכת.
השקנו את החלק הראשון של LayoutNG ב-2019, שכלל פריסה רגילה של מאגר בלוקים, פריסה בתוך שורה, רכיבים צפים ומיקום מחוץ לזרימה, אבל ללא תמיכה ב-flex, ב-grid או בטבלאות, וללא תמיכה בכלל בפיצול בלוקים. במקרה כזה, נשתמש במנוע הפריסה הקודם לרכיבי flex, grid וטבלאות, וגם בכל דבר שקשור לפיצול בלוקים. זה נכון גם לגבי רכיבים מסוג בלוק, בתוך שורה, צפים ורכיבים מחוץ לזרימה בתוכן מקוטע – כפי שאתם רואים, שדרוג של מנוע פריסה מורכב כל כך במקום הוא תהליך עדין מאוד.
בנוסף, עד אמצע שנת 2019 כבר הופעלה רוב הפונקציונליות של הליבה של פרגמנטציית בלוקים ב-LayoutNG (מאחורי דגל). למה זה לקח כל כך הרבה זמן לשלוח את ההזמנה? התשובה הקצרה היא: הפיצול צריך להתקיים בצורה תקינה לצד חלקים שונים מדור קודם של המערכת, שאי אפשר להסיר או לשדרג אותם עד שכל יחסי התלות משודרגים.
אינטראקציה עם מנוע מדור קודם
מבני הנתונים הקודמים עדיין אחראים לממשקי ה-API של JavaScript שקוראים את פרטי הפריסה, לכן אנחנו צריכים לכתוב חזרה נתונים למנוע הקודם באופן שהוא מבין. זה כולל עדכון תקין של מבני הנתונים הקודמים עם כמה עמודות, כמו LayoutMultiColumnFlowThread.
זיהוי וטיפול בבעיות שקשורות למנוע מדור קודם
נאלצנו לחזור למנוע הפריסה הקודם כשהיה תוכן בתוכן שעדיין לא ניתן היה לטפל בו באמצעות פיצול בלוקים של LayoutNG. בזמן השליחה, חלוקת הבלוק של הליבה של LayoutNG כללה גמישות, רשת, טבלאות וכל מה שמודפס. זה היה מאתגר במיוחד כי היינו צריכים לזהות את הצורך באפשרות חלופית מדור קודם לפני יצירת אובייקטים בעץ הפריסה. לדוגמה, היינו צריכים לזהות את הבעיה לפני שידענו אם יש אב-אב של מאגר עם כמה עמודות, ולפני שידענו אילו צמתים של DOM יהפכו להקשר עיצוב או לא. זו בעיה של 'התרנגולת והביצה' שאין לה פתרון מושלם, אבל כל עוד ההתנהגות השגויה היחידה היא תוצאות חיוביות שגויות (חזרה לגרסה הקודמת כשאין בכלל צורך), זה בסדר, כי כל הבאגים בהתנהגות של הפריסה הזו הם באגים שכבר קיימים ב-Chromium, ולא באגים חדשים.
הליכה בין עצים לפני הצביעה
אנחנו מבצעים את ההכנה לקראת הצביעה אחרי התכנון, אבל לפני הצביעה. האתגר העיקרי הוא שאנחנו עדיין צריכים לעבור על עץ אובייקטי הפריסה, אבל עכשיו יש לנו קטעי NG – אז איך אנחנו מטפלים בזה? אנחנו עוברים גם על אובייקט הפריסה וגם על עצי הקטעים של NG בו-זמנית. זהו תהליך מורכב למדי, כי המיפוי בין שני העצים הוא לא טריוויאלי.
מבנה עץ אובייקטי הפריסה דומה מאוד למבנה של עץ ה-DOM, אבל עץ הפסקולים הוא פלט של הפריסה, ולא קלט אליה. בנוסף לשיקוף בפועל של ההשפעה של כל פיצול, כולל פיצול בתוך שורה (קטעי שורה) ופיצול בתוך בלוק (קטעי עמודה או דף), לעץ הקטעים יש גם קשר ישיר של הורה-צאצא בין בלוק מכיל לבין הצאצאים של DOM שהקטע הזה הוא הבלוק המכיל שלהם. לדוגמה, בעץ הקטעים, קטע שנוצר על ידי רכיב שממוקם באופן מוחלט הוא צאצא ישיר של קטע הבלוק שמכיל אותו, גם אם יש צמתים אחרים בשרשרת האב בין הצאצא שממוקם מחוץ לזרימה לבין הבלוק שמכיל אותו.
המצב יכול להיות מסובך עוד יותר כשיש רכיב שממוקם מחוץ לזרימה בתוך הפיצול, כי אז החלקים מחוץ לזרימה הופכים לצאצאים ישירים של ה-fragmentainer (ולא לצאצא של מה ש-CSS חושב שהוא הבלוק המכיל). זו הייתה בעיה שצריך היה לפתור כדי לאפשר קיום משותף עם המנוע הקודם. בעתיד נוכל לפשט את הקוד הזה, כי LayoutNG תוכנן לתמוך בצורה גמישה בכל מצבי הפריסה המודרניים.
הבעיות במנוע הפיצול הקודם
במנוע הקודם, שתוכנן בתקופת קודמת של האינטרנט, אין ממש מושג של פיצול, גם אם פיצול היה קיים מבחינה טכנית גם אז (כדי לתמוך בהדפסה). תמיכה בפיצול הייתה רק משהו שנוסף (הדפסה) או נוסף במהלך השימוש (עמודות מרובות).
כשמגדירים את הפריסה של תוכן שניתן לפצל, המנוע הקודם מפרס את כל התוכן בפס גבוה שרוחב שלו הוא הגודל של עמודת טקסט או דף, והגובה שלו הוא הגובה הנדרש כדי להכיל את התוכן. הפסים הארוכים האלה לא עוברים עיבוד בדף. אפשר לחשוב עליהם כעיבוד בדף וירטואלי שמאוחר יותר עבר סידור מחדש להצגה הסופית. הרעיון דומה להדפסת כתבה שלמה בעיתון מודפס בעמודה אחת, ולאחר מכן חיתוך שלה למספר עמודות בשלב השני. (בעבר, עיתונים מסוימים השתמשו בטכניקות דומות לזו!)
המנוע הקודם עוקב אחרי גבול מדומיין של דף או עמודה ברצועה. כך אפשר לדחוף תוכן שלא נכנס לגבול אל הדף או העמודה הבאים. לדוגמה, אם רק מחצית השורה העליונה תתאים למה שהמנוע חושב שהוא הדף הנוכחי, הוא יכניס 'עמודת חלוקה לדפים' כדי לדחוף אותו למטה למיקום שבו המנוע מניח ששוכן החלק העליון של הדף הבא. לאחר מכן, רוב העבודה בפועל של הפיצול ('חיתוך במספריים ומיקום') מתרחשת אחרי הפריסה, במהלך הצביעה המקדימות והצביעה, על ידי חיתוך רצועת התוכן הגבוהה לדפים או לעמודות (על ידי חיתוך והעברה של קטעים). כתוצאה מכך, אי אפשר היה לבצע כמה פעולות, כמו החלת טרנספורמציות ומיקום יחסי אחרי הפיצול (כפי שנדרש במפרט). בנוסף, יש תמיכה מסוימת בפיצול טבלאות במנוע הקודם, אבל אין תמיכה כלל בפיצול של רשתות או גמישות.
באיור הבא אפשר לראות איך פריסה של שלוש עמודות מיוצגת באופן פנימי במנוע הקודם, לפני שמשתמשים במספריים, במיקום ובדבק (יש לנו גובה מוגדר, כך שרק ארבע שורות נכנסות, אבל יש קצת מקום עודף בתחתית):

מכיוון שמנוע הפריסה הקודם לא מפצל את התוכן בפועל במהלך הפריסה, יש הרבה פריטים מוזרים, כמו מיקום יחסי וטרנספורמציות שחלות בצורה שגויה, וצלליות תיבה שנחתכות בקצוות העמודות.
דוגמה עם text-shadow:
מנוע המורשת לא מטפל בזה בצורה טובה:

רואים איך הצללית של הטקסט מהשורה בעמודה הראשונה נחתכת, ובמקום זאת ממוקמת בחלק העליון של העמודה השנייה? הסיבה לכך היא שמנוע הפריסה הקודם לא מבין את הפיצול.
הוא אמור להיראות כך:
בשלב הבא נוסיף קצת יותר מורכבות באמצעות טרנספורמציות ו-box-shadow. שימו לב שבמנוע הקודם יש קיצוץ שגוי וחפיפה בין עמודות. הסיבה לכך היא שלפי המפרט, טרנספורמציות אמורות להיות חלות כאפקט לאחר הפריסה ולאחר הפיצול. עם פיצול LayoutNG, שניהם פועלים בצורה תקינה. כך אפשר לשפר את יכולת הפעולה ההדדית עם Firefox, שבו יש תמיכה טובה בפיצול כבר זמן מה, ורוב הבדיקות בתחום הזה עוברות גם שם.
במנוע הקודם יש גם בעיות עם תוכן מונוליתי גבוה. תוכן הוא מונוליתי אם אי אפשר לפצל אותו למספר קטעים. רכיבים עם גלילה של עודף תוכן הם מונוליטיים, כי אין טעם למשתמשים לגלול באזור לא מלבני. קופסאות קווים ותמונות הן דוגמאות נוספות לתוכן מונוליתי. לדוגמה:
אם קטע התוכן המונוליתי גבוה מדי ולא נכנס לעמודה, המנוע הקודם יחלק אותו באופן אגרסיבי (מה שמוביל להתנהגות "מעניינת" מאוד כשמנסים לגלול בקונטיינר שניתן לגלילה):
במקום לאפשר לו לחרוג מהעמודה הראשונה (כפי שקורה בחלוקה של בלוקים ב-LayoutNG):
המנוע הקודם תומך בהפסקות מאולצות. לדוגמה, <div style="break-before:page;">
יוסיף מעבר דף לפני ה-DIV. עם זאת, יש לו תמיכה מוגבלת בלבד במציאת הפסקות לא מאולצות אופטימליות. הוא תומך ב-break-inside:avoid
וב-יתומות ויתומים, אבל אין תמיכה במניעת הפסקות בין בלוקים, אם מבקשים זאת באמצעות break-before:avoid
, לדוגמה. עיין בדוגמה זו:
כאן, לאלמנט #multicol
יש מקום ל-5 שורות בכל עמודה (כי הוא בגובה 100px והערך של line-height הוא 20px), כך שכל #firstchild
יכול להיכנס לעמודה הראשונה. עם זאת, בקוד של הדף ה'אח' #secondchild
מופיעה ההוראה break-before:avoid, כלומר המערכת צריכה להימנע מהוספת הפסקה בין שני הדפים. מכיוון שהערך של widows
הוא 2, אנחנו צריכים להעביר 2 שורות של #firstchild
לעמודה השנייה כדי למלא את כל הבקשות להימנעות מהפסקות. Chromium הוא מנוע הדפדפן הראשון שתומך באופן מלא בשילוב הזה של תכונות.
איך פועלת הפיצול של מודעות ה-NG
בדרך כלל, מנוע הפריסה של NG מתכנן את המסמך על ידי סריקה של עץ התיבות של CSS לפי עומק. כשכל הצאצאים של צומת מסוים מופיעים בתצוגה, אפשר להשלים את הפריסה של הצומת הזה על ידי יצירת NGPhysicalFragment וחזרה לאלגוריתם הפריסה של ההורה. האלגוריתם הזה מוסיף את הפלח לרשימת הפלחיו הצאצאים, וכאשר כל הצאצאים הושלמו, הוא יוצר לעצמו פלח עם כל הפלחיו הצאצאים. באמצעות השיטה הזו, המערכת יוצרת עץ קטעים לכל המסמך. עם זאת, זוהי הפשטה מוגזמת: לדוגמה, רכיבים שממוקמים מחוץ לזרימה יצטרכו לעבור 'בלון' מהמקום שבו הם נמצאים בעץ ה-DOM אל הבלוק שמכיל אותם, לפני שאפשר יהיה למקם אותם בתצוגה. אני מתעלם מהפרט המתקדם הזה כאן כדי לשמור על פשטות.
בנוסף לתיבת ה-CSS עצמה, LayoutNG מספק מרחב אילוצים לאלגוריתם של הפריסה. כך האלגוריתם מקבל מידע כמו המרחב הזמין לפריסה, אם הוקם הקשר חדש של עיצוב ותוצאות של צמצום שוליים ביניים מהתוכן הקודם. מרחב האילוצים יודע גם את גודל הבלוק המתוכנן של ה-fragmentainer ואת ההיסט הנוכחי של הבלוק בתוכו. זה מציין את המקום שבו צריך להפסיק.
כשיש פיצול של בלוקים, הפריסה של הצאצאים צריכה להפסיק בהפסקה. הסיבות לחלוקה כוללות מצב שבו נגמר המרחב בדף או בעמודה, או חלוקה מאולצת. לאחר מכן אנחנו יוצרים קטעים (fragments) של הצמתים שבהם ביקרנו, וחוזרים כל הדרך עד לשורש ההקשר של הפיצול (קונטיינר של עמודות מרובות, או, במקרה של הדפסה, שורש המסמך). לאחר מכן, ברמה הבסיסית של הקשר הפיצול, אנחנו מתכוננים לקונטיינר חדש של קטעי קוד, יורדים שוב לעץ וממשיכים מהמקום שבו הפסקנו לפני ההפסקה.
מבנה הנתונים החשוב שמאפשר להמשיך את הפריסה אחרי הפסקה נקרא NGBlockBreakToken. הוא מכיל את כל המידע הדרוש כדי להמשיך את הפריסה בצורה תקינה ב-fragmentainer הבא. ה-NGBlockBreakToken משויך לצומת, והוא יוצר עץ NGBlockBreakToken, כך שכל צומת שצריך להמשיך ממנו מיוצג. ה-NGBlockBreakToken מצורף ל-NGPhysicalBoxFragment שנוצר עבור צמתים שמתנתקים בתוכם. אסימוני ההפסקה מועברים להורים, ויוצרים עץ של אסימוני הפסקה. אם אנחנו צריכים להפסיק לפני צומת (במקום בתוכו), לא נוצר שבר, אבל צומת ההורה עדיין צריך ליצור אסימון הפסקה מסוג 'break-before' לצומת, כדי שנוכל להתחיל בתכנון שלו כשנגיע לאותה מיקום בעץ הצמתים בקונטיינר הבא של השברים.
ההפסקות מתווספות כשנגמר לנו המקום ב-Fragmentainer (הפסקה לא מאולצת) או כשמתבצעת בקשה להפסקה מאולצת.
במפרט יש כללים להוספת הפסקות אופטימליות ללא כפייה, ולא תמיד נכון להוסיף הפסקה בדיוק במקום שבו נגמר לנו המקום. לדוגמה, יש מאפייני CSS שונים כמו break-before
שמשפיעים על בחירת מיקום ההפסקה.
במהלך יצירת הפריסה, כדי ליישם בצורה נכונה את הקטע הפסקות לא מאולצות במפרט, אנחנו צריכים לעקוב אחרי נקודות עצירה אפשריות. הרשומה הזו מאפשרת לנו לחזור אחורה ולהשתמש בנקודת העצירה האחרונה והטובה ביותר שנמצאה, אם נגמר לנו המקום בנקודה שבה נפרוץ בקשות להימנעות מפסקות (לדוגמה, break-before:avoid
או orphans:7
). לכל נקודת עצירה אפשרית ניתנת ניקוד, החל מ'צריך לעשות זאת רק כצאן אחרון' ועד 'המקום המושלם להפסקה', עם ערכים מסוימים באמצע. אם מיקום הפסקה מקבל ציון 'מושלם', סימן שאין הפרה של כללי הפסקה אם נפסיק שם (ואם אנחנו מקבלים את הציון הזה בדיוק בנקודה שבה נגמר לנו המקום, אין צורך לחפש אחורה משהו טוב יותר). אם הדירוג הוא 'האפשרות האחרונה', נקודת העצירה אפילו לא תקפה, אבל יכול להיות שנפסיק שם אם לא נמצא משהו טוב יותר, כדי למנוע עומס יתר ב-fragmentainer.
נקודות עצירה תקינות מופיעות בדרך כלל רק בין אחים (קופסאות שורה או בלוקים), ולא, למשל, בין הורה לבין הצאצא הראשון שלו (נקודות עצירה מסוג C הן יוצאות דופן, אבל אין צורך לדון בהן כאן). לדוגמה, יש נקודת עצירה תקינה לפני רכיב אח של בלוק עם break-before:avoid, אבל היא נמצאת איפשהו בין 'מושלמת' ל'שימוש אחרון'.
במהלך הפריסה אנחנו עוקבים אחרי נקודת העצירה הטובה ביותר שנמצאה עד כה במבנה שנקרא NGEarlyBreak. עצירה מוקדמת היא נקודת עצירה אפשרית לפני צומת בלוק או בתוך צומת בלוק, או לפני שורה (שורה של מאגר בלוק או שורה גמישה). יכול להיות שנוצר שרשרת או נתיב של אובייקטים מסוג NGEarlyBreak, למקרה שנקפיץ על נקודת העצירה הטובה ביותר עמוק בתוך משהו שעברנו על פניו קודם, כשנגמר לנו המקום. לדוגמה:
במקרה הזה, נגמר לנו המקום ממש לפני #second
, אבל יש בו את ההגדרה 'break-before:avoid', שמקבלת את הציון 'הפרה של break avoid' למיקום ההפסקה. בשלב הזה יש לנו שרשרת NGEarlyBreak של "inside #outer
> inside #middle
> inside #inner
> before "line 3"', עם 'perfect', לכן עדיף שנפסיק שם. לכן צריך לחזור ולהריץ מחדש את הפריסה מתחילת #outer (והפעם להעביר את NGEarlyBreak שמצאנו), כדי שנוכל להפסיק לפני 'שורה 3' ב-#inner. (אנחנו מפסיק לפני 'שורה 3', כדי שה-4 שורות הנותרות יגיעו למפרק הבא, וכדי לפעול בהתאם ל-widows:4
).
האלגוריתם מתוכנן תמיד להפסיק בנקודת העצירה הטובה ביותר האפשרית – כפי שמוגדרת במפרט – על ידי ביטול הכללים בסדר הנכון, אם לא ניתן לעמוד בכל הכללים. חשוב לדעת: אנחנו צריכים לבצע סידור מחדש רק פעם אחת לכל תהליך פיצול. עד שמגיעים לסבב השני של יצירת הפריסה, מיקום ההפסקה הטוב ביותר כבר הועבר לאלגוריתמים של הפריסה. זהו מיקום ההפסקה שזוהה בסבב הראשון של יצירת הפריסה, ונמסר כחלק מהפלט של הפריסה בסיבוב הזה. במעבר השני של הפריסה, אנחנו לא פורסים את התוכן עד שנגמר לנו המקום – למעשה, לא צפוי שניגמר לנו המקום (זו תהיה בעצם שגיאה), כי קיבלנו מקום מצוין (טוב, מצוין ככל שהיה זמין) להוספת הפסקה מוקדמת, כדי להימנע מהפרה מיותרת של כללי ההפסקות. אז אנחנו פשוט מתכננים עד לנקודה הזו, ומפסיקים.
לצד זה, לפעמים אנחנו צריכים להפר חלק מהבקשות להימנעות מהפסקות, אם זה עוזר למנוע עומס יתר ב-fragmentainer. לדוגמה:
במקרה הזה, נגמר לנו המקום ממש לפני #second
, אבל יש לו "break-before:avoid". התרגום הוא 'הפרת ההנחיה לגבי הימנעות מפסקות', בדיוק כמו בדוגמה הקודמת. יש לנו גם את NGEarlyBreak עם 'הפרת 'יתומים ואלמנות'' (בתוך #first
> לפני 'שורה 2'), שעדיין לא מושלם אבל טוב יותר מ'הפרת 'הימנעות מפסקה''. לכן נפסיק לפני 'שורה 2', ונפגע בבקשה לגבי 'יתומים' או 'אלמנות'. המפרט עוסק בנושא הזה בקטע 4.4. Unforced Breaks, שבו מוגדר אילו כללי פיצול יתעלמו קודם אם אין לנו מספיק נקודות עצירה כדי למנוע עומס יתר ב-fragmentainer.
סיכום
המטרה הפונקציונלית של פרויקט הפיצול של בלוקים ב-LayoutNG הייתה לספק הטמעה שתומכת בארכיטקטורה של LayoutNG של כל מה שהמנוע הקודם תומך בו, ורק כמה שפחות דברים אחרים, מלבד תיקוני באגים. היוצא מן הכלל העיקרי הוא תמיכה טובה יותר במניעת הפסקות (break-before:avoid
, למשל), כי זוהי חלק מהותי ממנועי הפיצול, ולכן היא הייתה צריכה להיות שם מההתחלה, כי הוספה שלה בשלב מאוחר יותר הייתה מחייבת כתיבת מחדש.
עכשיו, אחרי שסיימנו את הפיצול של בלוקים ב-LayoutNG, נוכל להתחיל לעבוד על הוספת פונקציונליות חדשה, כמו תמיכה בגדלים מעורבים של דפים בזמן הדפסה, תיבות שוליים של @page
בזמן הדפסה, box-decoration-break:clone
ועוד. כמו ב-LayoutNG באופן כללי, אנחנו מצפים ששיעור הבאגים ועומס התחזוקה של המערכת החדשה יהיו נמוכים יותר עם הזמן.
תודות
- Una Kravets על 'צילום המסך הידני' הנפלא.
- כריס הרלסון על הגהה, משוב והצעות.
- Philip Jägenstedt על משוב והצעות.
- Rachel Andrew על העריכה ועל הדוגמה הראשונה של התרשים עם כמה עמודות.