@spfx-fast/sharepoint-ui 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/AGENTS.md +23 -3
  2. package/QUICKSTART.md +133 -0
  3. package/README.md +21 -878
  4. package/components.json +178 -33
  5. package/dist/appearance.js +4 -4
  6. package/dist/elements/alert.js +26 -2
  7. package/dist/elements/avatar.js +1 -1
  8. package/dist/elements/button.js +1 -1
  9. package/dist/elements/carousel.js +5 -2
  10. package/dist/elements/color-picker.js +1 -1
  11. package/dist/elements/combobox.js +5 -5
  12. package/dist/elements/data-grid-model.d.ts +3 -0
  13. package/dist/elements/data-grid.d.ts +2 -1
  14. package/dist/elements/data-grid.js +19 -10
  15. package/dist/elements/date-picker.js +1 -1
  16. package/dist/elements/dialog.js +1 -1
  17. package/dist/elements/file-upload.js +1 -1
  18. package/dist/elements/floating-position.d.ts +10 -0
  19. package/dist/elements/floating-position.js +53 -0
  20. package/dist/elements/info.js +1 -1
  21. package/dist/elements/input.js +2 -2
  22. package/dist/elements/link.js +1 -1
  23. package/dist/elements/message-bar.js +33 -5
  24. package/dist/elements/nav.js +4 -1
  25. package/dist/elements/overflow.d.ts +1 -0
  26. package/dist/elements/overflow.js +2 -2
  27. package/dist/elements/popover.js +1 -1
  28. package/dist/elements/rating.js +3 -3
  29. package/dist/elements/rich-text-editor.d.ts +15 -1
  30. package/dist/elements/rich-text-editor.js +938 -79
  31. package/dist/elements/search.js +3 -10
  32. package/dist/elements/select.js +2 -2
  33. package/dist/elements/shared.d.ts +4 -1
  34. package/dist/elements/shared.js +14 -1
  35. package/dist/elements/sharepoint-field-basic-controls.d.ts +1 -0
  36. package/dist/elements/sharepoint-field-basic-controls.js +14 -0
  37. package/dist/elements/sharepoint-fields.d.ts +29 -0
  38. package/dist/elements/sharepoint-fields.js +460 -0
  39. package/dist/elements/spin-button.js +2 -2
  40. package/dist/elements/tag-picker.js +29 -11
  41. package/dist/elements/textarea.js +2 -2
  42. package/dist/elements/toaster.js +2 -1
  43. package/dist/elements/tooltip.js +18 -1
  44. package/dist/elements/tree.js +1 -1
  45. package/dist/elements.d.ts +3 -2
  46. package/dist/elements.js +4 -1
  47. package/dist/index.d.ts +6 -4
  48. package/dist/index.js +4 -2
  49. package/dist/react.d.ts +188 -19
  50. package/dist/react.js +321 -61
  51. package/dist/sharepoint-fields.d.ts +135 -0
  52. package/dist/sharepoint-fields.js +402 -0
  53. package/dist/theme.d.ts +9 -1
  54. package/dist/theme.js +78 -1
  55. package/package.json +11 -2
  56. package/skills/sharepoint-ui/SKILL.md +1 -1
  57. package/skills/sharepoint-ui/references/sharepoint-patterns.md +1 -1
package/dist/theme.js CHANGED
@@ -1,4 +1,14 @@
1
1
  import { getSharePointUIStyleTokens } from "./appearance.js";
2
+ /** Build named swatches from the current SharePoint palette; custom tenant keys are preserved. */
3
+ export function getSharePointThemeColorPresets(palette, labelForKey) {
4
+ return Object.entries(palette ?? {}).flatMap(([name, color]) => {
5
+ if (typeof color !== "string" || !color.trim())
6
+ return [];
7
+ const readableName = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Za-z])(\d+)/g, "$1 $2").replace(/[_-]+/g, " ").replace(/^theme\s+/i, "").replace(/\s+/g, " ").trim();
8
+ const label = labelForKey?.(name) ?? readableName.replace(/\b[a-z]/g, character => character.toUpperCase());
9
+ return [{ name, value: name, color, label }];
10
+ });
11
+ }
2
12
  /** Compose reusable theme packs left-to-right; later packs override earlier values within each group. */
3
13
  export function composeSharePointUIDesignThemes(...themes) {
4
14
  return themes.reduce((merged, theme) => ({
@@ -78,12 +88,53 @@ export function getSharePointUIDesignTokens(theme) {
78
88
  "--spui-selection-background": `color-mix(in srgb, ${accent} 14%, var(--spui-ui-surface))`,
79
89
  "--spui-selection-border": accent,
80
90
  "--spui-link-color": `var(--spui-semantic-link, color-mix(in srgb, ${accent} 55%, var(--spui-ui-text) 45%))`,
81
- "--spui-focus-ring": "var(--spui-ui-border-focus)",
91
+ "--spui-ui-border-focus": accent,
92
+ "--spui-focus-ring": accent,
82
93
  "--spui-ui-info": accent,
83
94
  "--spui-ui-info-surface": `color-mix(in srgb, ${accent} 12%, var(--spui-ui-surface))`,
84
95
  "--spui-primary-background": accent,
85
96
  "--spui-primary-hover": `color-mix(in srgb, ${accent} 84%, black)`
86
97
  });
98
+ if (theme?.brandColor) {
99
+ const darker = `color-mix(in srgb, ${accent} 78%, black)`;
100
+ const brandLink = `light-dark(color-mix(in srgb, ${accent} 65%, black), color-mix(in srgb, ${accent} 76%, white))`;
101
+ const canvas = `light-dark(color-mix(in srgb, ${accent} 7%, white), color-mix(in srgb, ${accent} 8%, #10191a))`;
102
+ const surface = `light-dark(color-mix(in srgb, ${accent} 2%, white), color-mix(in srgb, ${accent} 12%, #182526))`;
103
+ const surfaceHover = `light-dark(color-mix(in srgb, ${accent} 12%, white), color-mix(in srgb, ${accent} 20%, #26383b))`;
104
+ const border = `light-dark(color-mix(in srgb, ${accent} 24%, #d1d1d1), color-mix(in srgb, ${accent} 32%, #6b7476))`;
105
+ Object.assign(result, {
106
+ "--spui-ui-canvas": canvas,
107
+ "--spui-ui-surface": surface,
108
+ "--spui-ui-surface-alt": canvas,
109
+ "--spui-ui-surface-hover": surfaceHover,
110
+ "--spui-ui-stroke": border,
111
+ "--spui-ui-border-subtle": `light-dark(color-mix(in srgb, ${accent} 14%, #e5e5e5), color-mix(in srgb, ${accent} 20%, #344245))`,
112
+ "--spui-selection-background": `light-dark(color-mix(in srgb, ${accent} 15%, white), color-mix(in srgb, ${accent} 24%, #202b2c))`,
113
+ "--spui-semantic-primary-button-background": accent,
114
+ "--spui-semantic-primary-button-background-hovered": `color-mix(in srgb, ${accent} 84%, black)`,
115
+ "--spui-semantic-input-focus-border-alt": accent,
116
+ "--spui-semantic-input-background-checked": accent,
117
+ "--spui-semantic-link": brandLink,
118
+ "--spui-semantic-link-hovered": darker,
119
+ "--spui-control-border": border,
120
+ "--spui-input-border": border,
121
+ "--spui-semantic-body-background": canvas,
122
+ "--spui-semantic-card-background": surface,
123
+ "--spui-semantic-body-divider": border,
124
+ "--spui-semantic-button-background-hovered": surfaceHover,
125
+ "--colorBrandBackground": accent,
126
+ "--colorBrandBackground2": `light-dark(color-mix(in srgb, ${accent} 12%, white), color-mix(in srgb, ${accent} 18%, #182526))`,
127
+ "--colorBrandBackground2Hover": `light-dark(color-mix(in srgb, ${accent} 20%, white), color-mix(in srgb, ${accent} 26%, #26383b))`,
128
+ "--colorBrandBackgroundPressed": `color-mix(in srgb, ${accent} 76%, black)`,
129
+ "--colorBrandBackgroundSelected": darker,
130
+ "--colorBrandForegroundLink": brandLink,
131
+ "--colorBrandStroke2Hover": `light-dark(color-mix(in srgb, ${accent} 62%, white), color-mix(in srgb, ${accent} 62%, #6b7476))`,
132
+ "--colorNeutralBackground1": surface,
133
+ "--colorNeutralBackground1Hover": surfaceHover,
134
+ "--colorNeutralBackground2": canvas,
135
+ "--colorNeutralStroke2": border,
136
+ });
137
+ }
87
138
  if (accent && !theme?.colors?.accentForeground) {
88
139
  const hex = /^#([\da-f]{3}|[\da-f]{6})$/i.exec(accent)?.[1];
89
140
  if (hex) {
@@ -104,6 +155,22 @@ export function getSharePointUIDesignTokens(theme) {
104
155
  }
105
156
  }
106
157
  }
158
+ // Explicit Fluent values are the host's final named-token layer, even when
159
+ // a brand seed also generates defaults for the same Fluent roles.
160
+ for (const [name, value] of Object.entries(theme?.fluentTokens ?? {})) {
161
+ if (value !== undefined && (typeof value === "string" || typeof value === "number")) {
162
+ result[name.startsWith("--") ? name : `--${name}`] = String(value);
163
+ }
164
+ }
165
+ if (accent) {
166
+ const foreground = theme?.colors?.accentForeground ?? result["--spui-ui-accent-text"];
167
+ if (theme?.brandColor && result["--spui-semantic-primary-button-text"] === undefined && foreground)
168
+ result["--spui-semantic-primary-button-text"] = foreground;
169
+ if (theme?.brandColor && result["--colorNeutralForegroundOnBrand"] === undefined && foreground)
170
+ result["--colorNeutralForegroundOnBrand"] = foreground;
171
+ if (theme?.colors?.link)
172
+ result["--spui-semantic-link"] = theme.colors.link;
173
+ }
107
174
  // App-specific properties are the final design-pack layer; direct provider tokens still win later.
108
175
  for (const [name, value] of Object.entries(theme?.customProperties ?? {})) {
109
176
  if (name.startsWith("--") && name.length > 2)
@@ -149,6 +216,16 @@ export function applySharePointTheme(target, theme, fluentTokens, options = {})
149
216
  style.setProperty("--spui-is-inverted", theme.isInverted ? "1" : "0");
150
217
  names.add("--spui-is-inverted");
151
218
  }
219
+ // Older SPFx themes expose their brand only in palette.themePrimary. The
220
+ // appearance presets consume the semantic role, so bridge that value when
221
+ // neither semantic colors nor Fluent v9 already provide a brand token.
222
+ const semanticAccent = theme?.semanticColors?.primaryButtonBackground;
223
+ const fluentAccent = fluentTokens?.colorBrandBackground;
224
+ const paletteAccent = theme?.palette?.themePrimary;
225
+ if (!semanticAccent && !fluentAccent && paletteAccent) {
226
+ style.setProperty("--spui-semantic-primary-button-background", paletteAccent);
227
+ names.add("--spui-semantic-primary-button-background");
228
+ }
152
229
  for (const [name, value] of Object.entries(fluentTokens ?? {})) {
153
230
  if (typeof value !== "string" && typeof value !== "number")
154
231
  continue;
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@spfx-fast/sharepoint-ui",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "license": "MIT",
5
5
  "description": "Host-aware, framework-independent UI elements for SharePoint Framework",
6
- "homepage": "https://vystartasv.github.io/sharepoint-ui-docs/",
6
+ "homepage": "https://vystartasv.github.io/sharepoint-ui/",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/vystartasv/sharepoint-ui.git"
@@ -47,12 +47,21 @@
47
47
  "types": "./dist/appearance.d.ts",
48
48
  "import": "./dist/appearance.js"
49
49
  },
50
+ "./theme": {
51
+ "types": "./dist/theme.d.ts",
52
+ "import": "./dist/theme.js"
53
+ },
54
+ "./sharepoint-fields": {
55
+ "types": "./dist/sharepoint-fields.d.ts",
56
+ "import": "./dist/sharepoint-fields.js"
57
+ },
50
58
  "./tokens": "./tokens.json"
51
59
  },
52
60
  "files": [
53
61
  "dist",
54
62
  "LICENSE",
55
63
  "README.md",
64
+ "QUICKSTART.md",
56
65
  "AGENTS.md",
57
66
  "components.json",
58
67
  "tokens.json",
@@ -7,6 +7,6 @@ description: Build or change accessible, theme-aware SharePoint Framework interf
7
7
 
8
8
  Use the package manifest at `../../components.json` to find exact React names, custom-element tags, and leaf entry points. The source repository checks it with `npm run catalog:check --workspace @spfx-fast/sharepoint-ui`. Use the package's TypeScript declarations as the final API authority. Do not infer that an unlisted Fluent v9 component exists; the library is still growing toward parity.
9
9
 
10
- For a fast composition guide, read [choosing components](references/choosing-components.md). For theme, locale, RTL, forms, and persistence rules, read [SharePoint patterns](references/sharepoint-patterns.md).
10
+ Start with [`QUICKSTART.md`](../../QUICKSTART.md) for the shared composition contract and copy-ready examples. Use [choosing components](references/choosing-components.md) to select controls and [SharePoint patterns](references/sharepoint-patterns.md) for locale, RTL, forms, and persistence.
11
11
 
12
12
  Prefer leaf imports. Keep SharePoint permissions, data fetching, and writes in an SPFx service. Controls emit state; they do not save records implicitly. Validate keyboard use, theme contrast on the target surface, and RTL when relevant.
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Theme and layout
4
4
 
5
- Wrap a React surface in `SharePointUIProvider` and pass the SharePoint `ThemeProvider` values through `applySharePointTheme()` at the web-part boundary. Set `lang` and `dir` on the provider. Use tokens and CSS parts to customize; avoid copying a component's internal markup. Glass derives its values from the selected SharePoint/base appearance.
5
+ Wrap a React surface in `SharePointUIProvider` and pass the SharePoint `ThemeProvider` values through `applySharePointTheme()` at the web-part boundary. Set the provider `lang`; it derives `dir` from the BCP 47 language tag. Set `dir` explicitly only to override that direction. Use tokens and CSS parts to customize; avoid copying a component's internal markup. Glass derives its values from the selected SharePoint/base appearance.
6
6
 
7
7
  ## Forms and events
8
8