0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-08-06 06:47:38 +00:00

add oddbird polyfill - not quite working

Not quite working yet.  polyfill is loaded, but positioned elements are spanning whole viewport.  Parking this for now.
This commit is contained in:
Gazook89
2026-04-24 22:06:52 -05:00
parent 87ed5d107d
commit 359d87440b
8 changed files with 117 additions and 9 deletions
+2
View File
@@ -1,6 +1,8 @@
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import Admin from './admin.jsx'; import Admin from './admin.jsx';
import { bootstrapAnchorPositioningPolyfill } from '../components/anchorPositioningPolyfill.js';
const props = window.__INITIAL_PROPS__ || {}; const props = window.__INITIAL_PROPS__ || {};
createRoot(document.getElementById('reactRoot')).render(<Admin {...props} />); createRoot(document.getElementById('reactRoot')).render(<Admin {...props} />);
bootstrapAnchorPositioningPolyfill();
+11 -4
View File
@@ -71,10 +71,14 @@ const Anchored = ({ children })=>{
// forward ref for AnchoredTrigger // forward ref for AnchoredTrigger
const AnchoredTrigger = forwardRef(({ toggleVisibility, visible, children, className, ...props }, ref)=>( const AnchoredTrigger = forwardRef(({ toggleVisibility, visible, children, className, ...props }, ref)=>(
<button <button
ref={ref} ref={(el)=>{
// setAttribute bypasses React's style sanitization so the anchor polyfill can read it
el?.setAttribute('style', `anchor-name: --${props.id}`);
if(typeof ref === 'function') ref(el);
else if(ref) ref.current = el;
}}
className={`anchored-trigger${visible ? ' active' : ''} ${className}`} className={`anchored-trigger${visible ? ' active' : ''} ${className}`}
onClick={toggleVisibility} onClick={toggleVisibility}
style={{ anchorName: `--${props.id}` }} // setting anchor properties here allows greater recyclability.
{...props} {...props}
> >
{children} {children}
@@ -84,9 +88,12 @@ const AnchoredTrigger = forwardRef(({ toggleVisibility, visible, children, class
// forward ref for AnchoredBox // forward ref for AnchoredBox
const AnchoredBox = forwardRef(({ visible, children, className, anchorId, ...props }, ref)=>( const AnchoredBox = forwardRef(({ visible, children, className, anchorId, ...props }, ref)=>(
<div <div
ref={ref} ref={(el)=>{
el?.setAttribute('style', `position-anchor: --${anchorId}`);
if(typeof ref === 'function') ref(el);
else if(ref) ref.current = el;
}}
className={`anchored-box${visible ? ' active' : ''} ${className}`} className={`anchored-box${visible ? ' active' : ''} ${className}`}
style={{ positionAnchor: `--${anchorId}` }} // setting anchor properties here allows greater recyclability.
{...props} {...props}
> >
{children} {children}
@@ -0,0 +1,29 @@
import './anchorPositioningPolyfill.less';
let polyfillPromise;
const supportsAnchorPositioning = ()=>'anchorName' in document.documentElement.style;
const afterInitialRender = ()=>new Promise((resolve)=>{
requestAnimationFrame(()=>{
requestAnimationFrame(resolve);
});
});
export const bootstrapAnchorPositioningPolyfill = ()=>{
if(supportsAnchorPositioning()) return Promise.resolve(false);
if(polyfillPromise) return polyfillPromise;
polyfillPromise = afterInitialRender()
.then(async ()=>{
const { default: polyfill } = await import('@oddbird/css-anchor-positioning/fn');
await polyfill();
return true;
})
.catch((error)=>{
polyfillPromise = undefined;
throw error;
});
return polyfillPromise;
};
@@ -0,0 +1,15 @@
.hb-anchor-source {
anchor-name: var(--hb-anchor-name);
}
.hb-anchor-target {
position-anchor: var(--hb-position-anchor);
}
.hb-position-area {
position-area: var(--hb-position-area);
}
.hb-position-try-fallbacks {
position-try-fallbacks: var(--hb-position-try-fallbacks);
}
+13 -3
View File
@@ -13,7 +13,7 @@
*/ */
import './dropdown.less'; import './dropdown.less';
import React, { useEffect } from 'react'; import React, { useEffect, useRef } from 'react';
import _ from 'lodash'; import _ from 'lodash';
// use react context to keep track of the menu depth (menus in menus) // use react context to keep track of the menu depth (menus in menus)
@@ -27,6 +27,16 @@ const Dropdown = ({ groupName, className = null, icon, children, color = null, c
// A menu is a submenu if depth > 0 // A menu is a submenu if depth > 0
const isSubMenu = depth > 0; const isSubMenu = depth > 0;
const wrapperRef = useRef(null);
const menuRef = useRef(null);
// Use setAttribute instead of the React style prop because React silently strips unknown CSS
// properties (like anchor-name) from inline styles in browsers that don't support them.
// setAttribute writes raw CSS text that the anchor positioning polyfill can read.
useEffect(()=>{
wrapperRef.current?.setAttribute('style', `anchor-name: ${anchorName}`);
menuRef.current?.setAttribute('style', `position-anchor: ${anchorName}`);
}, [anchorName]);
// hide popover with click inside iframe (not supported by light dismiss) // hide popover with click inside iframe (not supported by light dismiss)
useEffect(()=>{ useEffect(()=>{
@@ -59,7 +69,7 @@ const Dropdown = ({ groupName, className = null, icon, children, color = null, c
}; };
return ( return (
<div className={['menu-wrapper', className].join(' ')} style={{ anchorName }} role='none' > <div ref={wrapperRef} className={['menu-wrapper', className].join(' ')} role='none' >
<button <button
id={groupName.replace(' ', '-')} id={groupName.replace(' ', '-')}
className={['menu-item', color].join(' ')} className={['menu-item', color].join(' ')}
@@ -73,10 +83,10 @@ const Dropdown = ({ groupName, className = null, icon, children, color = null, c
</button> </button>
<MenuDepthContext.Provider value={depth + 1}> <MenuDepthContext.Provider value={depth + 1}>
<div <div
ref={menuRef}
id={menuId} id={menuId}
className='menu-list' className='menu-list'
popover='auto' popover='auto'
style={{ positionAnchor: anchorName }}
role='menu' role='menu'
> >
{children} {children}
+2
View File
@@ -1,6 +1,8 @@
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import Homebrew from './homebrew.jsx'; import Homebrew from './homebrew.jsx';
import { bootstrapAnchorPositioningPolyfill } from '../components/anchorPositioningPolyfill.js';
const props = window.__INITIAL_PROPS__ || {}; const props = window.__INITIAL_PROPS__ || {};
createRoot(document.getElementById('reactRoot')).render(<Homebrew {...props} />); createRoot(document.getElementById('reactRoot')).render(<Homebrew {...props} />);
bootstrapAnchorPositioningPolyfill();
+44 -2
View File
@@ -29,6 +29,7 @@
"@dmsnell/diff-match-patch": "^1.1.0", "@dmsnell/diff-match-patch": "^1.1.0",
"@googleapis/drive": "^20.1.0", "@googleapis/drive": "^20.1.0",
"@lezer/highlight": "^1.2.3", "@lezer/highlight": "^1.2.3",
"@oddbird/css-anchor-positioning": "^0.9.0",
"@sanity/diff-match-patch": "^3.2.0", "@sanity/diff-match-patch": "^3.2.0",
"@vitejs/plugin-react": "^5.1.2", "@vitejs/plugin-react": "^5.1.2",
"body-parser": "^2.2.0", "body-parser": "^2.2.0",
@@ -3337,6 +3338,31 @@
} }
} }
}, },
"node_modules/@floating-ui/core": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz",
"integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==",
"license": "MIT",
"dependencies": {
"@floating-ui/utils": "^0.2.11"
}
},
"node_modules/@floating-ui/dom": {
"version": "1.7.6",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz",
"integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==",
"license": "MIT",
"dependencies": {
"@floating-ui/core": "^1.7.5",
"@floating-ui/utils": "^0.2.11"
}
},
"node_modules/@floating-ui/utils": {
"version": "0.2.11",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz",
"integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
"license": "MIT"
},
"node_modules/@googleapis/drive": { "node_modules/@googleapis/drive": {
"version": "20.1.0", "version": "20.1.0",
"resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-20.1.0.tgz", "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-20.1.0.tgz",
@@ -4122,6 +4148,18 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/@oddbird/css-anchor-positioning": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/@oddbird/css-anchor-positioning/-/css-anchor-positioning-0.9.0.tgz",
"integrity": "sha512-G5nfb4sU0auxJH7VHafPwVJjr1GhH5uPSkmytGqhNftCpT3QEh8pFtMd4YHt1dRwb4o9qVZxlGSKUIc4TIrysQ==",
"license": "BSD-3-Clause",
"dependencies": {
"@floating-ui/dom": "^1.7.5",
"@types/css-tree": "^2.3.11",
"css-tree": "^3.1.0",
"nanoid": "^5.1.6"
}
},
"node_modules/@paralleldrive/cuid2": { "node_modules/@paralleldrive/cuid2": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz",
@@ -4609,6 +4647,12 @@
"@babel/types": "^7.28.2" "@babel/types": "^7.28.2"
} }
}, },
"node_modules/@types/css-tree": {
"version": "2.3.11",
"resolved": "https://registry.npmjs.org/@types/css-tree/-/css-tree-2.3.11.tgz",
"integrity": "sha512-aEokibJOI77uIlqoBOkVbaQGC9zII0A+JH1kcTNKW2CwyYWD8KM6qdo+4c77wD3wZOQfJuNWAr9M4hdk+YhDIg==",
"license": "MIT"
},
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
@@ -6344,7 +6388,6 @@
"version": "3.2.1", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz",
"integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"mdn-data": "2.27.1", "mdn-data": "2.27.1",
@@ -10366,7 +10409,6 @@
"version": "2.27.1", "version": "2.27.1",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz",
"integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==",
"dev": true,
"license": "CC0-1.0" "license": "CC0-1.0"
}, },
"node_modules/media-typer": { "node_modules/media-typer": {
+1
View File
@@ -105,6 +105,7 @@
"@dmsnell/diff-match-patch": "^1.1.0", "@dmsnell/diff-match-patch": "^1.1.0",
"@googleapis/drive": "^20.1.0", "@googleapis/drive": "^20.1.0",
"@lezer/highlight": "^1.2.3", "@lezer/highlight": "^1.2.3",
"@oddbird/css-anchor-positioning": "^0.9.0",
"@sanity/diff-match-patch": "^3.2.0", "@sanity/diff-match-patch": "^3.2.0",
"@vitejs/plugin-react": "^5.1.2", "@vitejs/plugin-react": "^5.1.2",
"body-parser": "^2.2.0", "body-parser": "^2.2.0",