From a6c9337e7d5f872a8d64da3f265c2e3e1e9e25f1 Mon Sep 17 00:00:00 2001 From: David Dawkins Date: Wed, 3 Jul 2024 12:13:38 +0100 Subject: [PATCH] - Slightly more helpful error message to console when Bind throws - Added `includeRule` for CSS style sheets. For example, `includeRule "wh100"` will look for and include CSS definitions for `rule ".wh100"` from the same style sheet --- .config/dotnet-tools.json | 12 ++++---- RELEASE_NOTES.md | 5 +++- global.json | 4 +-- package-lock.json | 14 +++++----- src/Sutil/Bindings.fs | 2 +- src/Sutil/Styling.fs | 58 +++++++++++++++++++++++++++++++-------- 6 files changed, 66 insertions(+), 29 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 9fda245..3fcef12 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -2,17 +2,17 @@ "version": 1, "isRoot": true, "tools": { - "fable": { - "version": "3.7.20", - "commands": [ - "fable" - ] - }, "paket": { "version": "5.257.0", "commands": [ "paket" ] + }, + "fable": { + "version": "4.19.3", + "commands": [ + "fable" + ] } } } \ No newline at end of file diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b59ecbe..ed27b5c 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,7 @@ +### 2.0.12 +- Slightly more helpful error message to console when Bind throws +- Added `includeRule` for CSS style sheets. For example, `includeRule "wh100"` will look for and include CSS definitions for `rule ".wh100"` from the same style sheet + ### 2.0.11 - New interface IReadOnlyStore, like an IObservable where you can query the current value - Unprotect a few core functions and modules to enable development of new SutilElements (ContextHelpers, build, SutilEffect.MakeGroup, SutilGroup.RegisterUnsubscribe, SutilCore.ContextHelpers, SutilCore.build) @@ -7,7 +11,6 @@ - Added more `c` helpers (ac, hrc, tbodyc, olc) - Fix for Store.iter - ### 2.0.9 - Remove cleanup diagnostics diff --git a/global.json b/global.json index 24b2f5b..088e8bd 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "6.0.300", + "version": "8.0.100", "rollForward": "latestFeature" } -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index 5105d73..c1e2ca8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ }, "devDependencies": { "esm": "^3.2.25", - "fable-publish-utils": "2.0.0" + "fable-publish-utils": "^2.2.0" } }, "node_modules/@discoveryjs/json-ext": { @@ -1917,9 +1917,9 @@ } }, "node_modules/fable-publish-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fable-publish-utils/-/fable-publish-utils-2.0.0.tgz", - "integrity": "sha512-VLLGbgUVTnN/oScXcVb2LkH20Y5Pa24S4TXV83LYHjyvJS9CTaYTo/+kHiGE7BGPeS6FShsDHsCCGY+fhQ5BWA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/fable-publish-utils/-/fable-publish-utils-2.2.0.tgz", + "integrity": "sha512-/P3iaJj2WLQiMdBEB6wVWTUdeg3RLk9i7ltvQ0HvM7k6+5spwBLwdL2gYfFV8E4n44wlWuMXy6SxyP0OMrGBQA==", "dev": true }, "node_modules/fast-deep-equal": { @@ -8138,9 +8138,9 @@ } }, "fable-publish-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fable-publish-utils/-/fable-publish-utils-2.0.0.tgz", - "integrity": "sha512-VLLGbgUVTnN/oScXcVb2LkH20Y5Pa24S4TXV83LYHjyvJS9CTaYTo/+kHiGE7BGPeS6FShsDHsCCGY+fhQ5BWA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/fable-publish-utils/-/fable-publish-utils-2.2.0.tgz", + "integrity": "sha512-/P3iaJj2WLQiMdBEB6wVWTUdeg3RLk9i7ltvQ0HvM7k6+5spwBLwdL2gYfFV8E4n44wlWuMXy6SxyP0OMrGBQA==", "dev": true }, "fast-deep-equal": { diff --git a/src/Sutil/Bindings.fs b/src/Sutil/Bindings.fs index f5bf85f..f5264a3 100644 --- a/src/Sutil/Bindings.fs +++ b/src/Sutil/Bindings.fs @@ -78,7 +78,7 @@ let bindElementCO<'T> (store : IObservable<'T>) (element: IObservable<'T> -> Su node <- build (element(store)) (bindCtx |> ContextHelpers.withReplace (node,group.NextDomNode)) with | x -> - JS.console.error(x) + JS.console.error("sutil.bindElementCO:parentNode: ", ctx.ParentNode, "exception:", x) node <- build (elementFromException x) (bindCtx |> ContextHelpers.withReplace (node,group.NextDomNode)) ) group.RegisterUnsubscribe ( fun () -> diff --git a/src/Sutil/Styling.fs b/src/Sutil/Styling.fs index b366717..78dc7a3 100644 --- a/src/Sutil/Styling.fs +++ b/src/Sutil/Styling.fs @@ -86,34 +86,69 @@ let private framesToText (frames : KeyFrames) = let private isSutilRule (nm:string,v) = nm.StartsWith("sutil") -let private ruleToText (styleName : string) (rule:StyleRule) = +let private ruleToText (classMap : Map) (styleName : string) (rule:StyleRule) = //rule.SelectorSpec + (styleListToText rule.Style) - let styleText = String.Join ("\n", rule.Style |> Seq.filter (not << isSutilRule) |> Seq.map (fun (nm,v) -> $" {nm}: {v};")) + + let rec styleText (r : StyleRule) = + r.Style + |> Seq.filter (not << isSutilRule) + |> Seq.map (fun (nm,v) -> + if (nm.EndsWith("()")) then + match classMap.TryFind nm[0..-3] with + | Some subrule -> + styleText subrule + | _ -> + Fable.Core.JS.console.warn("No class found for substitution: ", nm[0..-3]) + "" + else + $" {nm}: {v};") + |> String.concat "\n" + [ specifySelector styleName rule.SelectorSpec " {\n" - styleText + styleText rule "}\n" ] |> String.concat "" -let rec mediaRuleToText styleName rule = - sprintf "@media %s {\n%s\n}\n" (rule.Condition) (rule.Rules |> List.map (entryToText styleName) |> String.concat "\n") +let rec mediaRuleToText classMap styleName rule = + sprintf "@media %s {\n%s\n}\n" (rule.Condition) (rule.Rules |> List.map (entryToText classMap styleName) |> String.concat "\n") -and entryToText (styleName : string) = function +and entryToText classMap (styleName : string) = function | Rule rule -> - ruleToText styleName rule + ruleToText classMap styleName rule | KeyFrames frames -> framesToText frames | MediaRule rule -> - mediaRuleToText styleName rule + mediaRuleToText classMap styleName rule + +let private isClassChar c = Char.IsLetterOrDigit(c) || c = '-' || c = '_' + +let private isClassName (s : string) = + s.ToCharArray() |> Array.forall isClassChar + +let private isClassOnly (s : string) = + s.Length >= 2 && s[0] = '.' && isClassName (s.Substring(1)) + + +let getClassMap (styleSheet) = + styleSheet + |> List.choose (fun d -> match d with Rule r when isClassOnly r.SelectorSpec -> Some (r.SelectorSpec.Substring(1),r) | _ -> None) + |> (fun items -> Fable.Core.JS.console.log("Class map: ", items |> List.map fst |> String.concat ","); items) + |> Map + + +let includeRule (name : string) = (name + "()"), ("" :> obj) let private styleSheetAsText (styleSheet : StyleSheetDefinitions) = - System.String.Join("\n", styleSheet |> List.map (entryToText "")) + let classMap : Map = getClassMap styleSheet + + System.String.Join("\n", styleSheet |> List.map (entryToText classMap "")) let private addStyleSheet (doc:Document) styleName (styleSheet : StyleSheetDefinitions) = let style = newStyleElement doc for entry in styleSheet do - entryToText styleName entry |> doc.createTextNode |> style.appendChild |> ignore + entryToText (getClassMap styleSheet) styleName entry |> doc.createTextNode |> style.appendChild |> ignore (fun () -> style.parentElement.removeChild(style) |> ignore) let addGlobalStyleSheet (doc:Document) (styleSheet : StyleSheetDefinitions) = @@ -125,10 +160,9 @@ let addGlobalStyleSheet (doc:Document) (styleSheet : StyleSheetDefinitions) = let rule selector style = let result = Rule { SelectorSpec = selector - //Selector = parseSelector selector Style = style } - //log($"%s{selector} -> %A{result.Selector}") + result ///