From bfd48514fc4631bce64dd2cfaeac44350ce482e2 Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Mon, 14 Jul 2025 17:15:46 -0700 Subject: [PATCH 1/2] Add naming conventions table and anchors to guideline blocks --- .../Views/Home/Guidelines.cshtml | 40 +- EssentialCSharp.Web/wwwroot/css/styles.css | 22 ++ EssentialCSharp.Web/wwwroot/js/site.js | 358 +++++++++--------- 3 files changed, 246 insertions(+), 174 deletions(-) diff --git a/EssentialCSharp.Web/Views/Home/Guidelines.cshtml b/EssentialCSharp.Web/Views/Home/Guidelines.cshtml index 87a5387a..a9de4307 100644 --- a/EssentialCSharp.Web/Views/Home/Guidelines.cshtml +++ b/EssentialCSharp.Web/Views/Home/Guidelines.cshtml @@ -8,10 +8,48 @@

@ViewData["Title"]


+
+

+ C# Naming Conventions - Quick Reference Table + +

+
+ + + + + + + + + + + + + + + + + + + + + +
KindNaming ConventionExample
ClassesPascalCaseclass Car
Types and NamespacesPascalCasenamespace VehicleManufacturer;
ParameterscamelCasepublic Car(int odometerMileage, string manufacturer)
MethodsPascalCasepublic void StartEngine()
PropertiesPascalCasepublic double FuelLevel { get; set; }
Local VariablescamelCaseint yearManufactured;
Local FunctionsPascalCasestring CalculateMilesUntilEmpty(double fuelLevel)
Fields_PascalCaseprivate string _Day;
Enum MembersPascalCaseenum Status { Unknown, Operational, Broken, InShop }
Type ParametersTPascalCasepublic TOutput Convert<TInput, TOutput>(TInput from)
InterfacesIPascalCaseinterface ISampleInterface
+
+
+
@foreach (var group in guidelines.GroupBy(g => g.SanitizedSubsection).OrderBy(g => g.Key)) { -

@group.Key

+

+ @group.Key + +

foreach (var guideline in group) {
diff --git a/EssentialCSharp.Web/wwwroot/css/styles.css b/EssentialCSharp.Web/wwwroot/css/styles.css index f0b46c6c..fe346fb6 100644 --- a/EssentialCSharp.Web/wwwroot/css/styles.css +++ b/EssentialCSharp.Web/wwwroot/css/styles.css @@ -788,6 +788,28 @@ details > summary::-webkit-details-marker { border-color: var(--grey-lighten-2) transparent transparent transparent; } +/* Anchor Styling */ +.heading-wrapper:not(:hover) .anchor-link:not(:focus-visible) { + opacity: 0; +} + +.anchor-link { + border: none; + color: var(--link-color); + text-decoration: none; + position: absolute; + font-size: 14px; + margin: 4px 2px; + transition-duration: 0.4s; + cursor: pointer; + background-color: transparent; +} + + .anchor-link:hover { + color: var(--link-color-hover); + } + + /* The snackbar - position it at the bottom and in the middle of the screen */ #snackbar { visibility: hidden; /* Hidden by default. Visible on click */ diff --git a/EssentialCSharp.Web/wwwroot/js/site.js b/EssentialCSharp.Web/wwwroot/js/site.js index 5d57eb98..ee6313c4 100644 --- a/EssentialCSharp.Web/wwwroot/js/site.js +++ b/EssentialCSharp.Web/wwwroot/js/site.js @@ -1,118 +1,118 @@ -import { - createApp, - ref, - reactive, - onMounted, - markRaw, - watch, - computed, -} from "vue"; -import { useWindowSize } from "vue-window-size"; - -/** - * @typedef {Object} TocItem - * @prop {number} [level] - * @prop {string} [key] - * @prop {string} [href] - * @prop {string} [title] - * @prop {TocItem[]} [items] - */ -/** @type {TocItem} */ -const tocData = markRaw(TOC_DATA); - -//Add new content or features here: - -const featuresComingSoonList = [ - { - title: "Client-side Compiler", - text: "Write, compile, and run code snippets right from your browser. Enjoy hands-on experience with the code as you go through the site.", - }, - { - title: "Interactive Code Listings", - text: "Edit, compile, and run the code listings found throughout Essential C#.", - }, - { - title: "Hyperlinking", - text: "Easily navigate to interesting and relevant sites as well as related sections in Essential C#.", - }, - { - title: "Table of Contents Filtering", - text: "The Table of Contents filter will let you narrow down the list of topics to help you quickly and easily find your destination.", - }, -]; - -const contentComingSoonList = [ - { - title: "Experimental attribute", - text: "New feature from C# 12.0.", - }, - { - title: "Source Generators", - text: "A newer .NET feature.", - }, - { - title: "C# 13.0 Features", - text: "Various new features coming in .C# 13.0", - }, -]; - -const completedFeaturesList = [ - { - title: "Copying Header Hyperlinks", - text: "Easily copy a header URL to link to a book section.", - }, - { - title: "Home Page", - text: "Add a home page that features a short description of the book and a high level mindmap.", - }, - { - title: "Keyboard Shortcuts", - text: "Quickly navigate through the book via keyboard shortcuts (right/left arrows, 'n', 'p').", - }, -]; - -/** - * Find the path of TOC entries that lead to the current page. - * @param {TocItem[]} path - * @param {TocItem[]} items - * @returns {TocItem[] | undefined} path of items to the current page - * */ -function findCurrentPage(path, items) { - for (const item of items) { - const itemPath = [item, ...path]; - if ( - window.location.href.endsWith("/" + item.href) || - window.location.href.endsWith("/" + item.key) - ) { - return itemPath; - } - - const recursivePath = findCurrentPage(itemPath, item.items); - if (recursivePath) return recursivePath; - } -} - -function openSearch() { - const el = document - .getElementById("docsearch") - .querySelector(".DocSearch-Button"); - el.click(); -} - -const smallScreenSize = 768; - -const removeHashPath = (path) => { - if (!path) { - return null; - } - let index = path.indexOf("#"); - index = index > 0 ? index : path.length; - return path.substring(0, index); -}; -// v-bind dont like the # in the url -const nextPagePath = removeHashPath(NEXT_PAGE); -const previousPagePath = removeHashPath(PREVIOUS_PAGE); - +import { + createApp, + ref, + reactive, + onMounted, + markRaw, + watch, + computed, +} from "vue"; +import { useWindowSize } from "vue-window-size"; + +/** + * @typedef {Object} TocItem + * @prop {number} [level] + * @prop {string} [key] + * @prop {string} [href] + * @prop {string} [title] + * @prop {TocItem[]} [items] + */ +/** @type {TocItem} */ +const tocData = markRaw(TOC_DATA); + +//Add new content or features here: + +const featuresComingSoonList = [ + { + title: "Client-side Compiler", + text: "Write, compile, and run code snippets right from your browser. Enjoy hands-on experience with the code as you go through the site.", + }, + { + title: "Interactive Code Listings", + text: "Edit, compile, and run the code listings found throughout Essential C#.", + }, + { + title: "Hyperlinking", + text: "Easily navigate to interesting and relevant sites as well as related sections in Essential C#.", + }, + { + title: "Table of Contents Filtering", + text: "The Table of Contents filter will let you narrow down the list of topics to help you quickly and easily find your destination.", + }, +]; + +const contentComingSoonList = [ + { + title: "Experimental attribute", + text: "New feature from C# 12.0.", + }, + { + title: "Source Generators", + text: "A newer .NET feature.", + }, + { + title: "C# 13.0 Features", + text: "Various new features coming in .C# 13.0", + }, +]; + +const completedFeaturesList = [ + { + title: "Copying Header Hyperlinks", + text: "Easily copy a header URL to link to a book section.", + }, + { + title: "Home Page", + text: "Add a home page that features a short description of the book and a high level mindmap.", + }, + { + title: "Keyboard Shortcuts", + text: "Quickly navigate through the book via keyboard shortcuts (right/left arrows, 'n', 'p').", + }, +]; + +/** + * Find the path of TOC entries that lead to the current page. + * @param {TocItem[]} path + * @param {TocItem[]} items + * @returns {TocItem[] | undefined} path of items to the current page + * */ +function findCurrentPage(path, items) { + for (const item of items) { + const itemPath = [item, ...path]; + if ( + window.location.href.endsWith("/" + item.href) || + window.location.href.endsWith("/" + item.key) + ) { + return itemPath; + } + + const recursivePath = findCurrentPage(itemPath, item.items); + if (recursivePath) return recursivePath; + } +} + +function openSearch() { + const el = document + .getElementById("docsearch") + .querySelector(".DocSearch-Button"); + el.click(); +} + +const smallScreenSize = 768; + +const removeHashPath = (path) => { + if (!path) { + return null; + } + let index = path.indexOf("#"); + index = index > 0 ? index : path.length; + return path.substring(0, index); +}; +// v-bind dont like the # in the url +const nextPagePath = removeHashPath(NEXT_PAGE); +const previousPagePath = removeHashPath(PREVIOUS_PAGE); + const app = createApp({ setup() { const { width: windowWidth } = useWindowSize(); @@ -125,10 +125,22 @@ const app = createApp({ const snackbarColor = ref(); function copyToClipboard(copyText) { - let url = window.location.origin + "/" + copyText; + let url; + + // If copyText contains a #, it's a full path with anchor (e.g., 'page#anchor') + // If copyText doesn't contain a #, it's just an anchor for the current page + if (copyText.includes('#')) { + // Full path case: construct URL with origin + path + url = window.location.origin + "/" + copyText; + } else { + // Anchor only case: use current page URL + anchor + const currentUrl = window.location.href.split('#')[0]; // Remove any existing anchor + url = currentUrl + "#" + copyText; + } + let referralId = REFERRAL_ID; - if (referralId && referralId.trim()) { - url = addQueryParam(url, 'rid', referralId); + if (referralId && referralId.trim()) { + url = addQueryParam(url, 'rid', referralId); } navigator.clipboard .writeText(url) @@ -156,10 +168,10 @@ const app = createApp({ ); } - function addQueryParam(url, key, value) { - let urlObj = new URL(url, window.location.origin); - urlObj.searchParams.set(key, value); - return urlObj.toString(); + function addQueryParam(url, key, value) { + let urlObj = new URL(url, window.location.origin); + urlObj.searchParams.set(key, value); + return urlObj.toString(); } function goToPrevious() { @@ -252,9 +264,9 @@ const app = createApp({ onMounted(() => { // If a setting is set in storage already, follow that - if (sidebarShown.value === null) { - if (windowWidth.value > smallScreenSize) { - sidebarShown.value = true; + if (sidebarShown.value === null) { + if (windowWidth.value > smallScreenSize) { + sidebarShown.value = true; } } @@ -266,8 +278,8 @@ const app = createApp({ block: "center", inline: "center", }); - }); - + }); + const enableTocFilter = ref('none'); const searchQuery = ref(''); @@ -291,8 +303,8 @@ const app = createApp({ matches = matches || childMatches; } return matches; - } - + } + watch(searchQuery, (newQuery) => { if (!newQuery) { expandedTocs.clear(); @@ -310,51 +322,51 @@ const app = createApp({ } }); } - }); - + }); + function normalizeString(str) { return str.replace(/[^\w\s]|_/g, "").replace(/\s+/g, " ").toLowerCase(); - } - - return { - previousPageUrl, - nextPageUrl, - goToPrevious, - goToNext, - openSearch, - - snackbarMessage, - snackbarColor, - copyToClipboard, - - contentComingSoonList, - featuresComingSoonList, - completedFeaturesList, - - sidebarShown, - sidebarTab, - toggleSidebar, - - smallScreen, - - sectionTitle, - tocData, - expandedTocs, - currentPage, - percentComplete, - chapterParentPage, + } + + return { + previousPageUrl, + nextPageUrl, + goToPrevious, + goToNext, + openSearch, + + snackbarMessage, + snackbarColor, + copyToClipboard, + + contentComingSoonList, + featuresComingSoonList, + completedFeaturesList, + + sidebarShown, + sidebarTab, + toggleSidebar, + + smallScreen, + + sectionTitle, + tocData, + expandedTocs, + currentPage, + percentComplete, + chapterParentPage, searchQuery, - filteredTocData, - enableTocFilter, - isContentPage - }; - }, -}); - -app.component("toc-tree", { - props: ["item", "expandedTocs", "currentPage"], - template: "#toc-tree", -}); - -app.mount("#app"); + filteredTocData, + enableTocFilter, + isContentPage + }; + }, +}); + +app.component("toc-tree", { + props: ["item", "expandedTocs", "currentPage"], + template: "#toc-tree", +}); + +app.mount("#app"); From d9dc06ed26a707a17e1c8362ed957ab88b58e13c Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Mon, 14 Jul 2025 20:03:15 -0700 Subject: [PATCH 2/2] PR Feedback --- .../Views/Home/Guidelines.cshtml | 8 +- EssentialCSharp.Web/wwwroot/js/site.js | 344 +++++++++--------- 2 files changed, 176 insertions(+), 176 deletions(-) diff --git a/EssentialCSharp.Web/Views/Home/Guidelines.cshtml b/EssentialCSharp.Web/Views/Home/Guidelines.cshtml index a9de4307..cb2a4842 100644 --- a/EssentialCSharp.Web/Views/Home/Guidelines.cshtml +++ b/EssentialCSharp.Web/Views/Home/Guidelines.cshtml @@ -11,9 +11,9 @@

C# Naming Conventions - Quick Reference Table - +

@@ -46,9 +46,9 @@ {

@group.Key - +

foreach (var guideline in group) { diff --git a/EssentialCSharp.Web/wwwroot/js/site.js b/EssentialCSharp.Web/wwwroot/js/site.js index ee6313c4..ca70cbfb 100644 --- a/EssentialCSharp.Web/wwwroot/js/site.js +++ b/EssentialCSharp.Web/wwwroot/js/site.js @@ -1,118 +1,118 @@ -import { - createApp, - ref, - reactive, - onMounted, - markRaw, - watch, - computed, -} from "vue"; -import { useWindowSize } from "vue-window-size"; - -/** - * @typedef {Object} TocItem - * @prop {number} [level] - * @prop {string} [key] - * @prop {string} [href] - * @prop {string} [title] - * @prop {TocItem[]} [items] - */ -/** @type {TocItem} */ -const tocData = markRaw(TOC_DATA); - -//Add new content or features here: - -const featuresComingSoonList = [ - { - title: "Client-side Compiler", - text: "Write, compile, and run code snippets right from your browser. Enjoy hands-on experience with the code as you go through the site.", - }, - { - title: "Interactive Code Listings", - text: "Edit, compile, and run the code listings found throughout Essential C#.", - }, - { - title: "Hyperlinking", - text: "Easily navigate to interesting and relevant sites as well as related sections in Essential C#.", - }, - { - title: "Table of Contents Filtering", - text: "The Table of Contents filter will let you narrow down the list of topics to help you quickly and easily find your destination.", - }, -]; - -const contentComingSoonList = [ - { - title: "Experimental attribute", - text: "New feature from C# 12.0.", - }, - { - title: "Source Generators", - text: "A newer .NET feature.", - }, - { - title: "C# 13.0 Features", - text: "Various new features coming in .C# 13.0", - }, -]; - -const completedFeaturesList = [ - { - title: "Copying Header Hyperlinks", - text: "Easily copy a header URL to link to a book section.", - }, - { - title: "Home Page", - text: "Add a home page that features a short description of the book and a high level mindmap.", - }, - { - title: "Keyboard Shortcuts", - text: "Quickly navigate through the book via keyboard shortcuts (right/left arrows, 'n', 'p').", - }, -]; - -/** - * Find the path of TOC entries that lead to the current page. - * @param {TocItem[]} path - * @param {TocItem[]} items - * @returns {TocItem[] | undefined} path of items to the current page - * */ -function findCurrentPage(path, items) { - for (const item of items) { - const itemPath = [item, ...path]; - if ( - window.location.href.endsWith("/" + item.href) || - window.location.href.endsWith("/" + item.key) - ) { - return itemPath; - } - - const recursivePath = findCurrentPage(itemPath, item.items); - if (recursivePath) return recursivePath; - } -} - -function openSearch() { - const el = document - .getElementById("docsearch") - .querySelector(".DocSearch-Button"); - el.click(); -} - -const smallScreenSize = 768; - -const removeHashPath = (path) => { - if (!path) { - return null; - } - let index = path.indexOf("#"); - index = index > 0 ? index : path.length; - return path.substring(0, index); -}; -// v-bind dont like the # in the url -const nextPagePath = removeHashPath(NEXT_PAGE); -const previousPagePath = removeHashPath(PREVIOUS_PAGE); - +import { + createApp, + ref, + reactive, + onMounted, + markRaw, + watch, + computed, +} from "vue"; +import { useWindowSize } from "vue-window-size"; + +/** + * @typedef {Object} TocItem + * @prop {number} [level] + * @prop {string} [key] + * @prop {string} [href] + * @prop {string} [title] + * @prop {TocItem[]} [items] + */ +/** @type {TocItem} */ +const tocData = markRaw(TOC_DATA); + +//Add new content or features here: + +const featuresComingSoonList = [ + { + title: "Client-side Compiler", + text: "Write, compile, and run code snippets right from your browser. Enjoy hands-on experience with the code as you go through the site.", + }, + { + title: "Interactive Code Listings", + text: "Edit, compile, and run the code listings found throughout Essential C#.", + }, + { + title: "Hyperlinking", + text: "Easily navigate to interesting and relevant sites as well as related sections in Essential C#.", + }, + { + title: "Table of Contents Filtering", + text: "The Table of Contents filter will let you narrow down the list of topics to help you quickly and easily find your destination.", + }, +]; + +const contentComingSoonList = [ + { + title: "Experimental attribute", + text: "New feature from C# 12.0.", + }, + { + title: "Source Generators", + text: "A newer .NET feature.", + }, + { + title: "C# 13.0 Features", + text: "Various new features coming in .C# 13.0", + }, +]; + +const completedFeaturesList = [ + { + title: "Copying Header Hyperlinks", + text: "Easily copy a header URL to link to a book section.", + }, + { + title: "Home Page", + text: "Add a home page that features a short description of the book and a high level mindmap.", + }, + { + title: "Keyboard Shortcuts", + text: "Quickly navigate through the book via keyboard shortcuts (right/left arrows, 'n', 'p').", + }, +]; + +/** + * Find the path of TOC entries that lead to the current page. + * @param {TocItem[]} path + * @param {TocItem[]} items + * @returns {TocItem[] | undefined} path of items to the current page + * */ +function findCurrentPage(path, items) { + for (const item of items) { + const itemPath = [item, ...path]; + if ( + window.location.href.endsWith("/" + item.href) || + window.location.href.endsWith("/" + item.key) + ) { + return itemPath; + } + + const recursivePath = findCurrentPage(itemPath, item.items); + if (recursivePath) return recursivePath; + } +} + +function openSearch() { + const el = document + .getElementById("docsearch") + .querySelector(".DocSearch-Button"); + el.click(); +} + +const smallScreenSize = 768; + +const removeHashPath = (path) => { + if (!path) { + return null; + } + let index = path.indexOf("#"); + index = index > 0 ? index : path.length; + return path.substring(0, index); +}; +// v-bind dont like the # in the url +const nextPagePath = removeHashPath(NEXT_PAGE); +const previousPagePath = removeHashPath(PREVIOUS_PAGE); + const app = createApp({ setup() { const { width: windowWidth } = useWindowSize(); @@ -139,8 +139,8 @@ const app = createApp({ } let referralId = REFERRAL_ID; - if (referralId && referralId.trim()) { - url = addQueryParam(url, 'rid', referralId); + if (referralId && referralId.trim()) { + url = addQueryParam(url, 'rid', referralId); } navigator.clipboard .writeText(url) @@ -168,10 +168,10 @@ const app = createApp({ ); } - function addQueryParam(url, key, value) { - let urlObj = new URL(url, window.location.origin); - urlObj.searchParams.set(key, value); - return urlObj.toString(); + function addQueryParam(url, key, value) { + let urlObj = new URL(url, window.location.origin); + urlObj.searchParams.set(key, value); + return urlObj.toString(); } function goToPrevious() { @@ -264,9 +264,9 @@ const app = createApp({ onMounted(() => { // If a setting is set in storage already, follow that - if (sidebarShown.value === null) { - if (windowWidth.value > smallScreenSize) { - sidebarShown.value = true; + if (sidebarShown.value === null) { + if (windowWidth.value > smallScreenSize) { + sidebarShown.value = true; } } @@ -278,8 +278,8 @@ const app = createApp({ block: "center", inline: "center", }); - }); - + }); + const enableTocFilter = ref('none'); const searchQuery = ref(''); @@ -303,8 +303,8 @@ const app = createApp({ matches = matches || childMatches; } return matches; - } - + } + watch(searchQuery, (newQuery) => { if (!newQuery) { expandedTocs.clear(); @@ -322,51 +322,51 @@ const app = createApp({ } }); } - }); - + }); + function normalizeString(str) { return str.replace(/[^\w\s]|_/g, "").replace(/\s+/g, " ").toLowerCase(); - } - - return { - previousPageUrl, - nextPageUrl, - goToPrevious, - goToNext, - openSearch, - - snackbarMessage, - snackbarColor, - copyToClipboard, - - contentComingSoonList, - featuresComingSoonList, - completedFeaturesList, - - sidebarShown, - sidebarTab, - toggleSidebar, - - smallScreen, - - sectionTitle, - tocData, - expandedTocs, - currentPage, - percentComplete, - chapterParentPage, + } + + return { + previousPageUrl, + nextPageUrl, + goToPrevious, + goToNext, + openSearch, + + snackbarMessage, + snackbarColor, + copyToClipboard, + + contentComingSoonList, + featuresComingSoonList, + completedFeaturesList, + + sidebarShown, + sidebarTab, + toggleSidebar, + + smallScreen, + + sectionTitle, + tocData, + expandedTocs, + currentPage, + percentComplete, + chapterParentPage, searchQuery, - filteredTocData, - enableTocFilter, - isContentPage - }; - }, -}); - -app.component("toc-tree", { - props: ["item", "expandedTocs", "currentPage"], - template: "#toc-tree", -}); - -app.mount("#app"); + filteredTocData, + enableTocFilter, + isContentPage + }; + }, +}); + +app.component("toc-tree", { + props: ["item", "expandedTocs", "currentPage"], + template: "#toc-tree", +}); + +app.mount("#app");