Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/react-components/react-icons-compat/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# @fluentui/react-icons-compat

**React Icons components for [Fluent UI React](https://react.fluentui.dev/)**
**React Icons utility functions to help with migration from v8 to v9 [Fluent UI React](https://react.fluentui.dev/)**

These are not production-ready components and **should never be used in product**. This space is useful for testing new components whose APIs might change before final release.
It is recommended to use this package only if you still need to use functions such as `registerIcons` from v8 icons. Otherwise it is recommended that you use the v9 icons directly from `@fluentui/react-icons`.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,65 @@

```ts

// @public
export function getIcon(name?: string): IconRecord | undefined;

// @public (undocumented)
export interface IconOptions {
disableWarnings: boolean;
// @deprecated (undocumented)
warnOnMissingIcons?: boolean;
}

// @public (undocumented)
export interface IconRecord {
// (undocumented)
code: string | undefined;
// (undocumented)
subset: IconSubsetRecord;
}

// @public (undocumented)
export interface IconRecords {
// (undocumented)
[key: string]: IconRecord | {};
// (undocumented)
__options: IconOptions;
// (undocumented)
__remapped: {
[key: string]: string;
};
}

// @public (undocumented)
export interface IconSubset {
// (undocumented)
icons: {
[key: string]: string | JSX.Element;
};
mergeImageProps?: boolean;
}

// @public (undocumented)
export interface IconSubsetRecord extends IconSubset {
// (undocumented)
className?: string;
// (undocumented)
isRegistered?: boolean;
}

// @public
export function registerIconAlias(iconName: string, mappedToName: string): void;

// @public
export function registerIcons(iconSubset: IconSubset, options?: Partial<IconOptions>): void;

// @public
export function setIconOptions(options: Partial<IconOptions>): void;

// @public
export function unregisterIcons(iconNames: string[]): void;

// (No @packageDocumentation comment for this package)

```
119 changes: 119 additions & 0 deletions packages/react-components/react-icons-compat/src/GlobalSettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { getWindow } from './getWindow';

/**
* Storing global state in local module variables has issues when more than one copy
* of the module gets loaded on the page (due to a bundling error or simply by consuming
* a prebundled script.)
*
* This file contains helpers to deal with the getting and setting local state, and allows
* callers to get called back when it mutates.
*/

const GLOBAL_SETTINGS_PROP_NAME = '__globalSettings__';
const CALLBACK_STATE_PROP_NAME = '__callbacks__';

let _counter = 0;

/**
* Change description used for change callbacks in GlobalSettings.
*
* @public
*/
export interface ChangeDescription {
key: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
oldValue: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any;
}

/**
* Change event callback.
*
* @public
*/
export interface ChangeEventCallback {
__id__?: string;
(changeDescription?: ChangeDescription): void;
}

/**
* Global settings helper, which stores settings in the global (window) namespace.
* If window is not provided, it will store settings in module scope. Provides a
* way to observe changes as well when their values change.
*
* @public
*/
export class GlobalSettings {
public static getValue<T>(key: string, defaultValue?: T | (() => T)): T {
const globalSettings = _getGlobalSettings();

if (globalSettings[key] === undefined) {
globalSettings[key] = typeof defaultValue === 'function' ? (defaultValue as Function)() : defaultValue;
}

return globalSettings[key];
}

public static setValue<T>(key: string, value: T): T {
const globalSettings = _getGlobalSettings();
const callbacks = globalSettings[CALLBACK_STATE_PROP_NAME];
const oldValue = globalSettings[key];

if (value !== oldValue) {
globalSettings[key] = value;

const changeDescription = {
oldValue,
value,
key,
};

for (const id in callbacks) {
if (callbacks.hasOwnProperty(id)) {
callbacks[id](changeDescription);
}
}
}

return value;
}

public static addChangeListener(cb: ChangeEventCallback): void {
// Note: we use generated ids on the callbacks to create a map of the callbacks, which optimizes removal.
// (It's faster to delete a key than it is to look up the index of an object and splice an array.)
let id = cb.__id__;
const callbacks = _getCallbacks();

if (!id) {
id = cb.__id__ = String(_counter++);
}

callbacks[id] = cb;
}

public static removeChangeListener(cb: ChangeEventCallback): void {
const callbacks = _getCallbacks();
delete callbacks[cb.__id__ as string];
}
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function _getGlobalSettings(): { [key: string]: any } {
const win = getWindow();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const globalObj: { [key: string]: any } = win || {};

if (!globalObj[GLOBAL_SETTINGS_PROP_NAME]) {
globalObj[GLOBAL_SETTINGS_PROP_NAME] = {
[CALLBACK_STATE_PROP_NAME]: {},
};
}

return globalObj[GLOBAL_SETTINGS_PROP_NAME];
}

function _getCallbacks(): { [key: string]: () => void } {
const globalSettings = _getGlobalSettings();
return globalSettings[CALLBACK_STATE_PROP_NAME];
}
31 changes: 31 additions & 0 deletions packages/react-components/react-icons-compat/src/getWindow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { canUseDOM } from '@fluentui/react-utilities';

let _window: Window | undefined = undefined;

// Note: Accessing "window" in IE11 is somewhat expensive, and calling "typeof window"
// hits a memory leak, whereas aliasing it and calling "typeof _window" does not.
// Caching the window value at the file scope lets us minimize the impact.
try {
// eslint-disable-next-line no-restricted-globals
_window = window;
} catch (e) {
/* no-op */
}

/**
* Helper to get the window object. The helper will make sure to use a cached variable
* of "window", to avoid overhead and memory leaks in IE11. Note that in popup scenarios the
* window object won't match the "global" window object, and for these scenarios, you should
* pass in an element hosted within the popup.
*
* @public
*/
export function getWindow(rootElement?: Element | null): Window | undefined {
if (!canUseDOM() || typeof _window === 'undefined') {
return undefined;
} else {
const el = rootElement as Element;

return el && el.ownerDocument && el.ownerDocument.defaultView ? el.ownerDocument.defaultView : _window;
}
}
Loading